2025-05-15 22:34:32 +08:00
< template >
2025-11-12 14:04:34 +08:00
< div v-if ="message.message_type === 'multimodal_image' && message.image_content" class="message-image" >
< img :src ="`data:image/jpeg;base64,${message.image_content}`" alt = "用户上传的图片" / >
< / div >
2025-05-15 22:34:32 +08:00
< div class = "message-box" : class = "[message.type, customClasses]" >
<!-- 用户消息 -- >
< p v-if ="message.type === 'human'" class="message-text" > {{ message.content }} < / p >
2025-11-01 21:34:16 +08:00
< p v -else -if = " message.type = = = ' system ' " class = "message-text-system" > { { message . content } } < / p >
2025-05-15 22:34:32 +08:00
<!-- 助手消息 -- >
< div v -else -if = " message.type = = = ' ai ' " class = "assistant-message" >
2025-08-25 13:57:47 +08:00
< div v-if ="parsedData.reasoning_content" class="reasoning-box" >
2025-05-15 22:34:32 +08:00
< a -collapse v -model :activeKey ="reasoningActiveKey" :bordered ="false" >
< template # expandIcon = "{ isActive }" >
< caret -right -outlined : rotate = "isActive ? 90 : 0" / >
< / template >
< a -collapse -panel key = "show" : header = "message.status=='reasoning' ? '正在思考...' : '推理过程'" class = "reasoning-header" >
2025-08-25 13:57:47 +08:00
< p class = "reasoning-content" > { { parsedData . reasoning _content } } < / p >
2025-05-15 22:34:32 +08:00
< / a - c o l l a p s e - p a n e l >
< / a - c o l l a p s e >
< / div >
<!-- 消息内容 -- >
2025-08-25 13:57:47 +08:00
< MdPreview v -if = " parsedData.content " ref = "editorRef"
2025-05-15 22:34:32 +08:00
editorId = "preview-only"
2025-11-23 01:39:44 +08:00
: theme = "theme"
2025-05-15 22:34:32 +08:00
previewTheme = "github"
: showCodeRowNumber = "false"
2025-08-25 13:57:47 +08:00
: modelValue = "parsedData.content"
2025-05-15 22:34:32 +08:00
: key = "message.id"
class = "message-md" / >
2025-08-25 13:57:47 +08:00
< div v -else -if = " parsedData.reasoning_content " class = "empty-block" > < / div >
2025-05-15 22:34:32 +08:00
2025-10-12 23:45:17 +08:00
<!-- 错误提示块 -- >
2025-11-25 22:34:44 +08:00
< div v-if ="displayError" class="error-hint" >
< span v-if ="getErrorMessage" > {{ getErrorMessage }} < / span >
< span v -else -if = " message.error_type = = = ' interrupted ' " > 回答生成已中断 < / span >
2025-10-12 23:45:17 +08:00
< span v -else -if = " message.error_type = = = ' unexpect ' " > 生成过程中出现异常 < / span >
2025-11-08 14:20:19 +08:00
< span v -else -if = " message.error_type = = = ' content_guard_blocked ' " > 检测到敏感内容 , 已中断输出 < / span >
2025-11-25 22:34:44 +08:00
< span v-else > {{ message.error_type | | ' 未知错误 ' }} < / span >
2025-10-12 23:45:17 +08:00
< / div >
2025-10-27 18:41:35 +08:00
< div v-if ="validToolCalls && validToolCalls.length > 0" class="tool-calls-container" >
< div v-for ="(toolCall, index) in validToolCalls" :key="toolCall.id || index" class="tool-call-container" >
2025-05-15 22:34:32 +08:00
< div v-if ="toolCall" class="tool-call-display" :class="{ 'is-collapsed': !expandedToolCalls.has(toolCall.id) }" >
< div class = "tool-header" @click ="toggleToolCall(toolCall.id)" >
2025-11-15 12:52:21 +08:00
< span v-if ="toolCall.status === 'success' || toolCall.tool_call_result" >
< span > < CircleCheckBig size = "16" class = "tool-loader tool-success" / > < / span > & nbsp ; 工具 < span class = "tool-name" > { { getToolNameByToolCall ( toolCall ) } } < / span > 执行完成
< / span >
< span v -else -if = " toolCall.status = = = ' error ' " >
< span > < CircleCheckBig size = "16" class = "tool-loader tool-error" / > < / span > & nbsp ; 工具 < span class = "tool-name" > { { getToolNameByToolCall ( toolCall ) } } < / span > 执行失败
< span v-if ="toolCall.error_message" > ( {{ toolCall.error_message }} ) < / span >
< / span >
< span v-else >
2025-08-26 10:54:28 +08:00
< span > < Loader size = "16" class = "tool-loader rotate tool-loading" / > < / span > & nbsp ;
2025-05-15 22:34:32 +08:00
< span > 正在调用工具 : < / span >
2025-08-25 01:46:31 +08:00
< span class = "tool-name" > { { getToolNameByToolCall ( toolCall ) } } < / span >
2025-05-15 22:34:32 +08:00
< / span >
< / div >
< div class = "tool-content" v-show ="expandedToolCalls.has(toolCall.id)" >
2025-12-17 23:39:53 +08:00
< div class = "tool-params" v-if ="String(toolCall.args).length > 2 || String(toolCall.function?.arguments).length > 2" >
2025-05-15 22:34:32 +08:00
< div class = "tool-params-content" >
2025-12-17 23:39:53 +08:00
< strong > 参数 : < / strong >
< span > { { getFormattedToolArgs ( toolCall ) } } < / span >
2025-05-15 22:34:32 +08:00
< / div >
< / div >
2025-06-22 23:08:40 +08:00
< div class = "tool-result" v-if ="toolCall.tool_call_result && toolCall.tool_call_result.content" >
2025-07-30 11:05:18 +08:00
< div class = "tool-result-content" :data-tool-call-id ="toolCall.id" >
2025-06-22 23:08:40 +08:00
< ToolResultRenderer
2025-10-27 18:41:35 +08:00
: tool - name = "toolCall.name || toolCall.function?.name"
2025-06-22 23:08:40 +08:00
: result - content = "toolCall.tool_call_result.content"
/ >
2025-05-15 22:34:32 +08:00
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
< div v-if ="message.isStoppedByUser" class="retry-hint" >
你停止生成了本次回答
< span class = "retry-link" @ click = "emit('retryStoppedMessage', message.id)" > 重新编辑问题 < / span >
< / div >
< div v-if ="(message.role=='received' || message.role=='assistant') && message.status=='finished' && showRefs" >
< RefsComponent :message ="message" :show-refs ="showRefs" :is-latest-message ="isLatestMessage" @retry ="emit('retry')" @ openRefs = "emit('openRefs', $event)" / >
< / div >
<!-- 错误消息 -- >
< / div >
2025-09-11 03:09:17 +08:00
< div v-if ="infoStore.debugMode" class="status-info" > {{ message }} < / div >
2025-05-15 22:34:32 +08:00
<!-- 自定义内容 -- >
< slot > < / slot >
< / div >
< / template >
< script setup >
import { computed , ref } from 'vue' ;
import { CaretRightOutlined , ThunderboltOutlined , LoadingOutlined } from '@ant-design/icons-vue' ;
import RefsComponent from '@/components/RefsComponent.vue'
2025-06-27 01:53:45 +08:00
import { Loader , CircleCheckBig } from 'lucide-vue-next' ;
2025-06-22 23:08:40 +08:00
import { ToolResultRenderer } from '@/components/ToolCallingResult'
2025-08-25 01:46:31 +08:00
import { useAgentStore } from '@/stores/agent'
2025-09-11 03:09:17 +08:00
import { useInfoStore } from '@/stores/info'
2025-11-23 01:39:44 +08:00
import { useThemeStore } from '@/stores/theme'
2025-08-25 01:46:31 +08:00
import { storeToRefs } from 'pinia'
2025-05-15 22:34:32 +08:00
import { MdPreview } from 'md-editor-v3'
import 'md-editor-v3/lib/preview.css' ;
const props = defineProps ( {
// 消息角色:'user'|'assistant'|'sent'|'received'
message : {
type : Object ,
required : true
} ,
// 是否正在处理中
isProcessing : {
type : Boolean ,
default : false
} ,
// 自定义类
customClasses : {
type : Object ,
default : ( ) => ( { } )
} ,
// 是否显示推理过程
showRefs : {
type : [ Array , Boolean ] ,
default : ( ) => false
} ,
2025-09-11 03:09:17 +08:00
// 是否为最新消息
isLatestMessage : {
2025-05-15 22:34:32 +08:00
type : Boolean ,
default : false
} ,
2025-09-11 03:09:17 +08:00
// 是否显示调试信息 (已废弃,使用 infoStore.debugMode)
debugMode : {
2025-05-15 22:34:32 +08:00
type : Boolean ,
default : false
}
} ) ;
const editorRef = ref ( )
const emit = defineEmits ( [ 'retry' , 'retryStoppedMessage' , 'openRefs' ] ) ;
// 推理面板展开状态
2025-08-27 00:13:38 +08:00
const reasoningActiveKey = ref ( [ 'hide' ] ) ;
2025-05-15 22:34:32 +08:00
const expandedToolCalls = ref ( new Set ( ) ) ; // 展开的工具调用集合
2025-11-25 22:34:44 +08:00
// 错误消息处理
const displayError = computed ( ( ) => {
// 简化错误判断:只检查明确的错误类型标识
return ! ! ( props . message . error _type || props . message . extra _metadata ? . error _type ) ;
} ) ;
const getErrorMessage = computed ( ( ) => {
// 优先使用直接的 error_message 字段
if ( props . message . error _message ) {
return props . message . error _message ;
}
// 其次从 extra_metadata 中获取具体的错误信息
if ( props . message . extra _metadata ? . error _message ) {
return props . message . extra _metadata . error _message ;
}
// 对于已知的错误类型,返回默认提示
switch ( props . message . error _type ) {
case 'interrupted' :
return '回答生成已中断' ;
case 'content_guard_blocked' :
return '检测到敏感内容,已中断输出' ;
case 'unexpect' :
return '生成过程中出现异常' ;
case 'agent_error' :
return '智能体获取失败' ;
default :
return null ;
}
} ) ;
2025-08-25 01:46:31 +08:00
// 引入智能体 store
const agentStore = useAgentStore ( ) ;
2025-09-11 03:09:17 +08:00
const infoStore = useInfoStore ( ) ;
2025-11-23 01:39:44 +08:00
const themeStore = useThemeStore ( ) ;
2025-08-25 01:46:31 +08:00
const { availableTools } = storeToRefs ( agentStore ) ;
2025-11-23 01:39:44 +08:00
// 主题设置 - 根据系统主题动态切换
const theme = computed ( ( ) => themeStore . isDark ? 'dark' : 'light' ) ;
2025-08-25 01:46:31 +08:00
// 工具相关方法
const getToolNameByToolCall = ( toolCall ) => {
2025-10-27 18:41:35 +08:00
const toolId = toolCall . name || toolCall . function ? . name ;
2025-08-25 01:46:31 +08:00
const toolsList = availableTools . value ? Object . values ( availableTools . value ) : [ ] ;
const tool = toolsList . find ( t => t . id === toolId ) ;
return tool ? tool . name : toolId ;
} ;
2025-10-27 18:41:35 +08:00
const getFormattedToolArgs = ( toolCall ) => {
2025-12-17 23:39:53 +08:00
const args = toolCall . args ? toolCall . args : toolCall . function ? . arguments ;
2025-10-27 18:41:35 +08:00
if ( ! args ) return '' ;
try {
// 尝试解析JSON格式的参数
if ( typeof args === 'string' && args . trim ( ) . startsWith ( '{' ) ) {
const parsed = JSON . parse ( args ) ;
return JSON . stringify ( parsed , null , 2 ) ;
2025-12-17 23:39:53 +08:00
} else if ( typeof args === 'object' && args !== null ) {
// 如果是对象类型,直接转换为字符串
console . log ( 'Object args:' , args ) ;
return JSON . stringify ( args , null , 2 ) ;
2025-10-27 18:41:35 +08:00
}
} catch ( e ) {
// 如果解析失败,直接返回原始字符串
2025-12-17 23:39:53 +08:00
console . log ( 'Failed to parse tool arguments as JSON:' , args ) ;
2025-10-27 18:41:35 +08:00
}
return args ;
} ;
// 过滤有效的工具调用
const validToolCalls = computed ( ( ) => {
if ( ! props . message . tool _calls || ! Array . isArray ( props . message . tool _calls ) ) {
return [ ] ;
}
return props . message . tool _calls . filter ( toolCall => {
// 过滤掉无效的工具调用
return toolCall &&
( toolCall . id || toolCall . name ) &&
( toolCall . args !== undefined ||
toolCall . function ? . arguments !== undefined ||
toolCall . tool _call _result !== undefined ) ;
} ) ;
} ) ;
2025-08-25 13:57:47 +08:00
const parsedData = computed ( ( ) => {
// Start with default values from the prop to avoid mutation.
2025-08-25 23:21:49 +08:00
let content = props . message . content . trim ( ) || '' ;
2025-08-25 13:57:47 +08:00
let reasoning _content = props . message . additional _kwargs ? . reasoning _content || '' ;
2025-09-11 02:11:06 +08:00
if ( reasoning _content ) {
return {
content ,
reasoning _content ,
}
}
2025-08-25 13:57:47 +08:00
// Regex to find <think>...</think> or an unclosed <think>... at the end of the string.
const thinkRegex = / < t h i n k > ( . * ? ) < \ / t h i n k > | < t h i n k > ( . * ? ) $ / s ;
const thinkMatch = content . match ( thinkRegex ) ;
if ( thinkMatch ) {
// The captured reasoning is in either group 1 (closed tag) or 2 (unclosed tag).
reasoning _content = ( thinkMatch [ 1 ] || thinkMatch [ 2 ] || '' ) . trim ( ) ;
// Remove the entire matched <think> block from the original content.
content = content . replace ( thinkMatch [ 0 ] , '' ) . trim ( ) ;
2025-06-22 23:56:12 +08:00
}
2025-08-25 13:57:47 +08:00
return {
content ,
reasoning _content ,
} ;
2025-06-22 23:56:12 +08:00
} ) ;
2025-05-15 22:34:32 +08:00
const toggleToolCall = ( toolCallId ) => {
if ( expandedToolCalls . value . has ( toolCallId ) ) {
expandedToolCalls . value . delete ( toolCallId ) ;
} else {
expandedToolCalls . value . add ( toolCallId ) ;
}
} ;
< / script >
< style lang = "less" scoped >
. message - box {
display : inline - block ;
border - radius : 1.5 rem ;
margin : 0.8 rem 0 ;
padding : 0.625 rem 1.25 rem ;
user - select : text ;
word - break : break - word ;
word - wrap : break - word ;
font - size : 15 px ;
line - height : 24 px ;
box - sizing : border - box ;
2025-11-23 01:39:44 +08:00
color : var ( -- gray - 10000 ) ;
2025-05-15 22:34:32 +08:00
max - width : 100 % ;
position : relative ;
letter - spacing : .25 px ;
& . human , & . sent {
max - width : 95 % ;
2025-11-12 14:04:34 +08:00
color : var ( -- gray - 1000 ) ;
background - color : var ( -- main - 50 ) ;
2025-05-15 22:34:32 +08:00
align - self : flex - end ;
border - radius : .5 rem ;
padding : 0.5 rem 1 rem ;
}
& . assistant , & . received , & . ai {
color : initial ;
width : 100 % ;
text - align : left ;
margin : 0 ;
padding : 0 px ;
background - color : transparent ;
border - radius : 0 ;
}
. message - text {
max - width : 100 % ;
margin - bottom : 0 ;
white - space : pre - line ;
}
2025-11-01 21:34:16 +08:00
. message - text - system {
max - width : 100 % ;
margin - bottom : 0 ;
white - space : pre - line ;
color : var ( -- gray - 600 ) ;
font - style : italic ;
font - size : 14 px ;
padding : 8 px 12 px ;
background - color : var ( -- gray - 50 ) ;
border - left : 3 px solid var ( -- gray - 300 ) ;
border - radius : 4 px ;
}
2025-05-15 22:34:32 +08:00
. err - msg {
2025-11-25 22:34:44 +08:00
color : var ( -- color - error - 500 ) ;
2025-11-23 01:39:44 +08:00
border : 1 px solid currentColor ;
2025-05-15 22:34:32 +08:00
padding : 0.5 rem 1 rem ;
border - radius : 8 px ;
text - align : left ;
2025-11-25 22:34:44 +08:00
background : var ( -- color - error - 50 ) ;
2025-05-15 22:34:32 +08:00
margin - bottom : 10 px ;
cursor : pointer ;
}
. searching - msg {
color : var ( -- gray - 700 ) ;
animation : colorPulse 1 s infinite ease - in - out ;
}
. reasoning - box {
margin - top : 10 px ;
margin - bottom : 15 px ;
2025-08-27 00:13:38 +08:00
border - radius : 8 px ;
2025-11-23 01:39:44 +08:00
border : 1 px solid var ( -- gray - 150 ) ;
2025-06-24 15:44:05 +08:00
background - color : var ( -- gray - 25 ) ;
2025-06-22 23:56:12 +08:00
overflow : hidden ;
transition : all 0.2 s ease ;
: deep ( . ant - collapse ) {
background - color : transparent ;
border : none ;
. ant - collapse - item {
border : none ;
. ant - collapse - header {
2025-06-24 15:44:05 +08:00
padding : 8 px 12 px ;
// background-color: var(--gray-100);
2025-06-22 23:56:12 +08:00
font - size : 14 px ;
font - weight : 500 ;
2025-08-27 00:13:38 +08:00
color : var ( -- gray - 700 ) ;
2025-06-22 23:56:12 +08:00
transition : all 0.2 s ease ;
. ant - collapse - expand - icon {
2025-08-27 00:13:38 +08:00
color : var ( -- gray - 400 ) ;
2025-06-22 23:56:12 +08:00
}
}
. ant - collapse - content {
border : none ;
background - color : transparent ;
. ant - collapse - content - box {
padding : 16 px ;
2025-08-27 09:57:29 +08:00
background - color : var ( -- gray - 25 ) ;
2025-06-22 23:56:12 +08:00
}
}
}
}
2025-05-15 22:34:32 +08:00
. reasoning - content {
font - size : 13 px ;
color : var ( -- gray - 800 ) ;
white - space : pre - wrap ;
margin : 0 ;
2025-06-22 23:56:12 +08:00
line - height : 1.6 ;
2025-05-15 22:34:32 +08:00
}
}
. assistant - message {
width : 100 % ;
}
2025-10-12 23:45:17 +08:00
. error - hint {
margin : 10 px 0 ;
padding : 8 px 16 px ;
border - radius : 8 px ;
font - size : 14 px ;
display : flex ;
align - items : center ;
gap : 8 px ;
2025-11-25 22:34:44 +08:00
background - color : var ( -- color - error - 50 ) ;
2025-11-08 14:20:19 +08:00
// border: 1px solid #f87171;
2025-11-25 22:34:44 +08:00
color : var ( -- color - error - 500 ) ;
2025-10-12 23:45:17 +08:00
span {
line - height : 1.5 ;
}
}
2025-05-15 22:34:32 +08:00
. status - info {
display : block ;
background - color : var ( -- gray - 50 ) ;
color : var ( -- gray - 700 ) ;
padding : 10 px ;
border - radius : 8 px ;
margin - bottom : 10 px ;
font - size : 12 px ;
font - family : monospace ;
max - height : 200 px ;
overflow - y : auto ;
}
: deep ( . tool - calls - container ) {
width : 100 % ;
margin - top : 10 px ;
. tool - call - container {
margin - bottom : 10 px ;
& : last - child {
margin - bottom : 0 ;
}
}
}
: deep ( . tool - call - display ) {
2025-06-24 15:44:05 +08:00
background - color : var ( -- gray - 25 ) ;
2025-11-23 01:39:44 +08:00
outline : 1 px solid var ( -- gray - 150 ) ;
2025-07-21 18:18:47 +08:00
border - radius : 8 px ;
2025-05-15 22:34:32 +08:00
overflow : hidden ;
2025-06-22 23:56:12 +08:00
transition : all 0.2 s ease ;
2025-05-15 22:34:32 +08:00
. tool - header {
2025-06-24 15:44:05 +08:00
padding : 8 px 12 px ;
// background-color: var(--gray-100);
2025-05-15 22:34:32 +08:00
font - size : 14 px ;
font - weight : 500 ;
color : var ( -- gray - 800 ) ;
2025-08-27 09:57:29 +08:00
border - bottom : 1 px solid var ( -- gray - 100 ) ;
2025-05-15 22:34:32 +08:00
display : flex ;
align - items : center ;
gap : 8 px ;
cursor : pointer ;
user - select : none ;
position : relative ;
2025-07-21 18:18:47 +08:00
transition : color 0.2 s ease ;
2025-06-27 01:53:45 +08:00
align - items : center ;
2025-06-22 23:56:12 +08:00
2025-05-15 22:34:32 +08:00
. anticon {
2025-07-26 03:36:54 +08:00
color : var ( -- main - color ) ;
2025-06-22 23:56:12 +08:00
font - size : 16 px ;
}
. tool - name {
font - weight : 600 ;
color : var ( -- main - 700 ) ;
2025-05-15 22:34:32 +08:00
}
2025-06-27 01:53:45 +08:00
span {
display : flex ;
align - items : center ;
gap : 4 px ;
}
. tool - loader {
margin - top : 2 px ;
color : var ( -- main - 700 ) ;
}
. tool - loader . rotate {
animation : rotate 2 s linear infinite ;
2025-05-15 22:34:32 +08:00
}
2025-08-26 10:54:28 +08:00
. tool - loader . tool - success {
2025-11-25 22:34:44 +08:00
color : var ( -- color - success - 500 ) ;
2025-08-26 10:54:28 +08:00
}
. tool - loader . tool - error {
2025-11-25 22:34:44 +08:00
color : var ( -- color - error - 500 ) ;
2025-08-26 10:54:28 +08:00
}
. tool - loader . tool - loading {
2025-11-25 22:34:44 +08:00
color : var ( -- color - info - 500 ) ;
2025-08-26 10:54:28 +08:00
}
2025-05-15 22:34:32 +08:00
}
. tool - content {
transition : all 0.3 s ease ;
2025-06-22 23:56:12 +08:00
2025-05-15 22:34:32 +08:00
. tool - params {
2025-09-01 03:38:46 +08:00
padding : 8 px 12 px ;
2025-08-27 09:57:29 +08:00
background - color : var ( -- gray - 25 ) ;
border - bottom : 1 px solid var ( -- gray - 150 ) ;
2025-05-15 22:34:32 +08:00
. tool - params - content {
margin : 0 ;
2025-12-17 23:39:53 +08:00
font - size : 12 px ;
2025-05-15 22:34:32 +08:00
overflow - x : auto ;
2025-08-27 09:57:29 +08:00
color : var ( -- gray - 700 ) ;
2025-06-22 23:56:12 +08:00
line - height : 1.5 ;
2025-05-20 20:49:50 +08:00
pre {
margin : 0 ;
2025-06-22 23:56:12 +08:00
font - family : 'Monaco' , 'Menlo' , 'Ubuntu Mono' , monospace ;
2025-05-20 20:49:50 +08:00
}
2025-05-15 22:34:32 +08:00
}
}
2025-06-22 23:08:40 +08:00
. tool - result {
padding : 0 ;
background - color : transparent ;
. tool - result - header {
2025-06-22 23:56:12 +08:00
padding : 12 px 16 px ;
2025-06-22 23:08:40 +08:00
background - color : var ( -- gray - 100 ) ;
font - size : 12 px ;
color : var ( -- gray - 700 ) ;
font - weight : 500 ;
border - bottom : 1 px solid var ( -- gray - 200 ) ;
}
. tool - result - content {
padding : 0 ;
background - color : transparent ;
}
}
2025-05-15 22:34:32 +08:00
}
& . is - collapsed {
. tool - header {
border - bottom : none ;
}
}
}
}
. retry - hint {
margin - top : 8 px ;
padding : 8 px 16 px ;
2025-11-23 01:39:44 +08:00
color : var ( -- gray - 600 ) ;
2025-05-15 22:34:32 +08:00
font - size : 14 px ;
text - align : left ;
}
. retry - link {
2025-11-25 22:34:44 +08:00
color : var ( -- color - info - 500 ) ;
2025-05-15 22:34:32 +08:00
cursor : pointer ;
margin - left : 4 px ;
& : hover {
text - decoration : underline ;
}
}
. ant - btn - icon - only {
& : has ( . anticon - stop ) {
2025-11-25 22:34:44 +08:00
background - color : var ( -- color - error - 500 ) ! important ;
2025-05-15 22:34:32 +08:00
& : hover {
2025-11-25 22:34:44 +08:00
background - color : var ( -- color - error - 100 ) ! important ;
2025-05-15 22:34:32 +08:00
}
}
}
@ keyframes colorPulse {
0 % { color : var ( -- gray - 700 ) ; }
50 % { color : var ( -- gray - 300 ) ; }
100 % { color : var ( -- gray - 700 ) ; }
}
@ keyframes fadeInUp {
from {
opacity : 0 ;
transform : translateY ( 10 px ) ;
}
to {
opacity : 1 ;
transform : translateY ( 0 ) ;
}
}
2025-06-27 01:53:45 +08:00
@ keyframes rotate {
from {
transform : rotate ( 0 deg ) ;
}
to {
transform : rotate ( 360 deg ) ;
}
}
2025-11-12 14:04:34 +08:00
// 多模态消息样式
. message - image {
border - radius : 12 px ;
overflow : hidden ;
margin - left : auto ;
// max-height: 200px;
border : 1 px solid rgba ( 255 , 255 , 255 , 0.2 ) ;
img {
max - width : 100 % ;
max - height : 200 px ;
object - fit : contain ;
}
}
2025-05-15 22:34:32 +08:00
< / style >
2025-08-25 13:57:47 +08:00
< style lang = "less" scoped >
: deep ( . message - md ) {
2025-05-15 22:34:32 +08:00
margin : 8 px 0 ;
}
2025-08-25 13:57:47 +08:00
: deep ( . message - md . md - editor - preview - wrapper ) {
2025-05-15 22:34:32 +08:00
max - width : 100 % ;
padding : 0 ;
font - family : - apple - system , BlinkMacSystemFont , 'Noto Sans SC' , 'PingFang SC' , 'Noto Sans SC' , 'Microsoft YaHei' , 'Hiragino Sans GB' , 'Source Han Sans CN' , 'Courier New' , monospace ;
# preview - only - preview {
2025-07-23 12:54:36 +08:00
font - size : 1 rem ;
line - height : 1.75 ;
2025-08-25 13:57:47 +08:00
color : var ( -- gray - 1000 ) ;
2025-05-15 22:34:32 +08:00
}
2025-07-23 12:54:36 +08:00
2025-05-15 22:34:32 +08:00
h1 , h2 {
2025-07-23 12:54:36 +08:00
font - size : 1.2 rem ;
2025-05-15 22:34:32 +08:00
}
h3 , h4 {
2025-07-23 12:54:36 +08:00
font - size : 1.1 rem ;
2025-05-15 22:34:32 +08:00
}
h5 , h6 {
2025-07-23 12:54:36 +08:00
font - size : 1 rem ;
2025-05-15 22:34:32 +08:00
}
2025-06-25 02:12:29 +08:00
strong {
2025-08-25 13:57:47 +08:00
font - weight : 500 ;
2025-06-25 02:12:29 +08:00
}
li > p , ol > p , ul > p {
margin : 0.25 rem 0 ;
}
2025-05-15 22:34:32 +08:00
2025-08-26 10:54:28 +08:00
ul li : : marker ,
2025-08-25 13:57:47 +08:00
ol li : : marker {
2025-08-26 10:54:28 +08:00
color : var ( -- main - bright ) ;
2025-08-25 13:57:47 +08:00
}
2025-08-26 10:54:28 +08:00
ul , ol {
padding - left : 1.625 rem ;
2025-06-25 02:12:29 +08:00
}
2025-05-15 22:34:32 +08:00
2025-06-24 15:44:05 +08:00
cite {
font - size : 12 px ;
color : var ( -- gray - 700 ) ;
font - style : normal ;
background - color : var ( -- gray - 200 ) ;
border - radius : 4 px ;
outline : 2 px solid var ( -- gray - 200 ) ;
}
2025-05-15 22:34:32 +08:00
a {
color : var ( -- main - 700 ) ;
}
2025-08-27 11:52:49 +08:00
. md - editor - code {
border : var ( -- gray - 50 ) ;
border - radius : 8 px ;
. md - editor - code - head {
background - color : var ( -- gray - 50 ) ;
2025-10-12 23:45:17 +08:00
z - index : 1 ;
2025-08-27 11:52:49 +08:00
. md - editor - collapse - tips {
color : var ( -- gray - 400 ) ;
}
}
}
2025-05-15 22:34:32 +08:00
code {
font - size : 13 px ;
font - family : 'Menlo' , 'Monaco' , 'Consolas' , 'PingFang SC' , 'Noto Sans SC' , 'Microsoft YaHei' , 'Hiragino Sans GB' , 'Source Han Sans CN' , 'Courier New' , monospace ;
line - height : 1.5 ;
letter - spacing : 0.025 em ;
tab - size : 4 ;
- moz - tab - size : 4 ;
2025-08-27 11:52:49 +08:00
background - color : var ( -- gray - 25 ) ;
2025-05-15 22:34:32 +08:00
}
2025-06-29 15:43:40 +08:00
p : last - child {
margin - bottom : 0 ;
}
2025-08-26 10:54:28 +08:00
table {
width : 100 % ;
border - collapse : collapse ;
2025-09-19 00:58:35 +08:00
margin : 2 em 0 ;
font - size : 15 px ;
2025-08-26 10:54:28 +08:00
display : table ;
2025-09-19 00:58:35 +08:00
outline : 1 px solid var ( -- gray - 100 ) ;
outline - offset : 14 px ;
border - radius : 12 px ;
2025-08-26 10:54:28 +08:00
2025-09-19 00:58:35 +08:00
thead tr th {
padding - top : 0 ;
}
2025-08-26 10:54:28 +08:00
thead th ,
tbody th {
border : none ;
2025-09-19 00:58:35 +08:00
border - bottom : 1 px solid var ( -- gray - 200 ) ;
2025-08-26 10:54:28 +08:00
}
tbody tr : last - child td {
2025-09-19 00:58:35 +08:00
border - bottom : 1 px solid var ( -- gray - 200 ) ;
2025-08-26 10:54:28 +08:00
border : none ;
2025-09-19 00:58:35 +08:00
padding - bottom : 0 ;
2025-08-26 10:54:28 +08:00
}
}
th ,
td {
padding : 0.5 rem 0 rem ;
text - align : left ;
border : none ;
}
td {
border - bottom : 1 px solid var ( -- gray - 100 ) ;
color : var ( -- gray - 800 ) ;
}
th {
font - weight : 600 ;
color : var ( -- gray - 800 ) ;
}
tr {
background - color : var ( -- gray - 0 ) ;
}
// tbody tr:last-child td {
// border-bottom: none;
// }
2025-05-15 22:34:32 +08:00
}
2025-08-25 13:57:47 +08:00
: deep ( . chat - box . font - smaller # preview - only - preview ) {
2025-05-15 22:34:32 +08:00
font - size : 14 px ;
h1 , h2 {
font - size : 1.1 rem ;
}
h3 , h4 {
font - size : 1 rem ;
}
}
2025-08-25 13:57:47 +08:00
: deep ( . chat - box . font - larger # preview - only - preview ) {
2025-05-15 22:34:32 +08:00
font - size : 16 px ;
h1 , h2 {
font - size : 1.3 rem ;
}
h3 , h4 {
font - size : 1.2 rem ;
}
h5 , h6 {
font - size : 1.1 rem ;
}
code {
font - size : 14 px ;
}
}
2025-11-25 22:34:44 +08:00
< / style >