@@ -6,6 +6,19 @@ import type { StoredMessage, ToolCall, ChatLabels, DisplayContent } from '../typ
66function isArchived(m : StoredMessage ): boolean {
77 return m .kind === MessageKind .Archive
88}
9+ function isCommand(m : StoredMessage ): boolean {
10+ return m .kind === MessageKind .Command
11+ }
12+ function isException(m : StoredMessage ): boolean {
13+ return m .kind === MessageKind .Exception
14+ }
15+ function rowKindClass(m : StoredMessage ) {
16+ return {
17+ archived: isArchived (m ),
18+ command: isCommand (m ),
19+ exception: isException (m ),
20+ }
21+ }
922import { fmtTs , fmtDateSep , toggleToolCall } from ' ../messageRender'
1023import { inlineArgs , resultPreview } from ' ../toolCallFormat'
1124import { resolveLabels , tpl } from ' ../labels'
@@ -76,11 +89,13 @@ function isEmbeddedTool(msg: StoredMessage): boolean {
7689
7790 <template v-if =" ! isEmbeddedTool (msg )" >
7891 <!-- Human message -->
79- <div v-if =" msg.message.role === MessageRole.Human" class =" msg-row human" :class =" { archived: isArchived (msg) } " >
92+ <div v-if =" msg.message.role === MessageRole.Human" class =" msg-row human" :class =" rowKindClass (msg)" >
8093 <div class =" msg-bubble human" >
8194 <div class =" msg-role-bar" >
8295 <span class =" msg-role" >{{ L.roleUser }}</span >
8396 <span v-if =" isArchived(msg)" class =" msg-archived-tag" >{{ L.archivedTag }}</span >
97+ <span v-if =" isCommand(msg)" class =" msg-kind-tag msg-kind-command" >{{ L.commandTag }}</span >
98+ <span v-if =" isException(msg)" class =" msg-kind-tag msg-kind-exception" >{{ L.exceptionTag }}</span >
8499 <span v-if =" msg.createdAt" class =" msg-time" >{{ fmtTs(msg.createdAt) }}</span >
85100 <div v-if =" msg.thinkId && thinksUrlPrefix" class =" think-toggle think-toggle-human" @click =" openThink(msg.thinkId!)" >
86101 <span >▸</span ><span >{{ L.think }}</span >
@@ -91,11 +106,13 @@ function isEmbeddedTool(msg: StoredMessage): boolean {
91106 </div >
92107
93108 <!-- AI message -->
94- <div v-else-if =" msg.message.role === MessageRole.AI" class =" msg-row ai" :class =" { archived: isArchived (msg) } " >
109+ <div v-else-if =" msg.message.role === MessageRole.AI" class =" msg-row ai" :class =" rowKindClass (msg)" >
95110 <div v-if =" msg.message.content" class =" msg-bubble ai" >
96111 <div class =" msg-role-bar" >
97112 <span class =" msg-role" >{{ L.roleAi }}</span >
98113 <span v-if =" isArchived(msg)" class =" msg-archived-tag" >{{ L.archivedTag }}</span >
114+ <span v-if =" isCommand(msg)" class =" msg-kind-tag msg-kind-command" >{{ L.commandTag }}</span >
115+ <span v-if =" isException(msg)" class =" msg-kind-tag msg-kind-exception" >{{ L.exceptionTag }}</span >
99116 <span v-if =" msg.createdAt" class =" msg-time" >{{ fmtTs(msg.createdAt) }}</span >
100117 <div v-if =" msg.thinkId && thinksUrlPrefix" class =" think-toggle" @click =" openThink(msg.thinkId!)" >
101118 <span >▸</span ><span >{{ L.think }}</span >
@@ -109,6 +126,8 @@ function isEmbeddedTool(msg: StoredMessage): boolean {
109126 <div class =" msg-role has-think" >
110127 {{ tpl(L.toolCalls, { count: msg.message.tool_calls.length }) }}
111128 <span v-if =" isArchived(msg) && !msg.message.content" class =" msg-archived-tag" >{{ L.archivedTag }}</span >
129+ <span v-if =" isCommand(msg) && !msg.message.content" class =" msg-kind-tag msg-kind-command" >{{ L.commandTag }}</span >
130+ <span v-if =" isException(msg) && !msg.message.content" class =" msg-kind-tag msg-kind-exception" >{{ L.exceptionTag }}</span >
112131 <div v-if =" msg.thinkId && thinksUrlPrefix && !msg.message.content" class =" think-toggle" @click =" openThink(msg.thinkId!)" >
113132 <span >▸</span ><span >{{ L.think }}</span >
114133 </div >
@@ -141,22 +160,26 @@ function isEmbeddedTool(msg: StoredMessage): boolean {
141160 </div >
142161
143162 <!-- Tool message (standalone, no tool_call_id) -->
144- <div v-else-if =" msg.message.role === MessageRole.Tool" class =" msg-row ai" :class =" { archived: isArchived (msg) } " >
163+ <div v-else-if =" msg.message.role === MessageRole.Tool" class =" msg-row ai" :class =" rowKindClass (msg)" >
145164 <div class =" msg-bubble tool" >
146165 <div class =" msg-role-bar" >
147166 <span class =" msg-role" >Tool{{ msg.message.name ? ` · ${msg.message.name}` : '' }}</span >
148167 <span v-if =" isArchived(msg)" class =" msg-archived-tag" >{{ L.archivedTag }}</span >
168+ <span v-if =" isCommand(msg)" class =" msg-kind-tag msg-kind-command" >{{ L.commandTag }}</span >
169+ <span v-if =" isException(msg)" class =" msg-kind-tag msg-kind-exception" >{{ L.exceptionTag }}</span >
149170 </div >
150171 <ContentParts :content =" msg .message .content " @open-image =" openLightbox " />
151172 </div >
152173 </div >
153174
154175 <!-- System / other -->
155- <div v-else class =" msg-row ai" :class =" { archived: isArchived (msg) } " >
176+ <div v-else class =" msg-row ai" :class =" rowKindClass (msg)" >
156177 <div class =" msg-bubble ai" >
157178 <div class =" msg-role-bar" >
158179 <span class =" msg-role" >{{ msg.message.role }}</span >
159180 <span v-if =" isArchived(msg)" class =" msg-archived-tag" >{{ L.archivedTag }}</span >
181+ <span v-if =" isCommand(msg)" class =" msg-kind-tag msg-kind-command" >{{ L.commandTag }}</span >
182+ <span v-if =" isException(msg)" class =" msg-kind-tag msg-kind-exception" >{{ L.exceptionTag }}</span >
160183 <span v-if =" msg.createdAt" class =" msg-time" >{{ fmtTs(msg.createdAt) }}</span >
161184 </div >
162185 <ContentParts :content =" msg .message .content " @open-image =" openLightbox " />
@@ -469,6 +492,47 @@ function isEmbeddedTool(msg: StoredMessage): boolean {
469492 white-space : nowrap ;
470493}
471494
495+ /* Command (指令型回调) — 不进入 LLM 上下文,仅作为旁路记录展示 */
496+ .msg-row.command .msg-bubble ,
497+ .msg-row.command .msg-tool-calls {
498+ border-left : 3px solid #818cf8 ;
499+ }
500+ .msg-row.command .msg-bubble.ai ,
501+ .msg-row.command .msg-bubble.tool ,
502+ .msg-row.command .msg-tool-calls {
503+ background : rgba (99 , 102 , 241 , 0.06 );
504+ }
505+ .msg-kind-tag {
506+ font-size : 10px ;
507+ font-weight : 600 ;
508+ padding : 1px 6px ;
509+ border-radius : 3px ;
510+ text-transform : uppercase ;
511+ letter-spacing : 0.04em ;
512+ white-space : nowrap ;
513+ }
514+ .msg-kind-command {
515+ color : #4338ca ;
516+ background : #e0e7ff ;
517+ border : 1px solid #c7d2fe ;
518+ }
519+
520+ /* Exception (异常记录) — 标红警示 */
521+ .msg-row.exception .msg-bubble ,
522+ .msg-row.exception .msg-tool-calls {
523+ border-left : 3px solid #f87171 ;
524+ }
525+ .msg-row.exception .msg-bubble.ai ,
526+ .msg-row.exception .msg-bubble.tool ,
527+ .msg-row.exception .msg-tool-calls {
528+ background : rgba (239 , 68 , 68 , 0.06 );
529+ }
530+ .msg-kind-exception {
531+ color : #b91c1c ;
532+ background : #fee2e2 ;
533+ border : 1px solid #fca5a5 ;
534+ }
535+
472536/* Thinking indicator */
473537.chatui-thinking {
474538 color : var (--chatui-fg-secondary );
0 commit comments