51 KiB
| date | type | title | origin | status |
|---|---|---|---|---|
| 2026-07-02 | feat | 私董会单会话限制 + 方案B 气泡 + 简化开始卡片 | docs/brainstorms/2026-07-02-private-board-restrictions-and-scheme-b-bubbles-requirements.md | ready |
Summary
收尾私董会(@board)模块的 UI 细节与单会话状态约束,四处协同改动:(1) 在 ChatInput.vue 的"私董会"按钮 click 处拦截"当前会话已存在进行中的私董会"场景,弹 a-modal 提示并提供快捷新建会话按钮;(2) 将 BoardBannerCard.vue 从带边框/紫条/进度条/专家 chip 的重样式简化为单行标题+副标题;(3) 给 MessageShell.vue 中所有 role === 'assistant' 的消息内容添加方案 B 风格的浅灰圆角矩形气泡(F1-A 独立 token / F4-A 排除所有 card-bearing 类型 / D4-方案1 :empty 隐藏);(4) 将 UserBubble.vue 普通文本消息改为 demo 中的深色右对齐气泡(--color-primary + --text-inverse),@board/@team 命令卡片保持现有浅色背景。改动范围仅限前端 Vue 组件与少量 store/type/token 文件,不动后端、不动方案 B 调色板、不动 StickyModeHeader 顶部条。
Problem Frame
私董会功能上线后存在三处 UI 粗糙点(详见 origin: docs/brainstorms/2026-07-02-private-board-restrictions-and-scheme-b-bubbles-requirements.md):
- 单会话多私董会无约束:
ChatInput.vue:75的"私董会"按钮@click直接showBoardModal = true,对当前会话是否已存在私董会无任何判断。连续两次SendMessage("@board:...")会创建第二个私董会,叠加在第一个未结束的私董会之上,boardState.experts被覆盖、轮次错乱、StickyModeHeader头像数与实际不符。 - BoardBannerCard 样式过重:
BoardBannerCard.vue使用了 card+border+shadow+4px 紫条+进度条+专家 chip pill 等装饰,与方案 B 整体"克制、不重样式"取向冲突。方案 B 截图中的"开始"区域是单行文本,不带装饰。 - 方案 B 气泡未落地:方案 B 截图中专家发言区域有浅灰圆角矩形气泡包裹内容,与 ChatGPT / Notion AI 风格一致。当前
MessageShell.vue:178-184的.message-shell__content没有背景/边框/圆角,气泡效果完全缺失。
Requirements
完整继承 origin 文档的 13 条 R-IDs(R1-R13)并新增 R14-R19(F4-A / D4-方案1 / U4 决策固化),共 19 条,分组如下:
单会话私董会限制(R1-R4):
- R1:
ChatInput.vue"私董会"按钮@click在打开BoardMeetingModal前检查chatStore.boardState,status === 'discussing' | 'concluding'时弹 a-modal,不打开 modal - R2:a-modal 标题"当前会话已存在私董会",副文"请新建会话来创建新的私董会",按钮"我知道了"+ "新建会话"(主操作);"新建会话"流程:关 modal →
chatStore.createConversation()→chatStore.selectConversation(newId, true)→ 不自动打开 modal - R3:
boardState === null或status === 'completed' | 'dissolved'时保持当前行为 - R4:以前端
chatStore.boardState为权威源,不依赖后端 /is_board标记
BoardBannerCard 简化(R5-R8):
- R5:重构为单行标题+副标题,保留
topic / maxRounds / currentRoundprops 向后兼容,删除expertsprop - R6:模板输出两行:
私董会 — {topic}+轮次:第 {currentRound} / {maxRounds} 轮 - R7:删除
.board-banner-card的重样式(background/border/border-radius/box-shadow)及__bar / __chip等重样式类,保留.board-banner-card容器(仅 margin/padding)+__title / __meta最小样式 - R8:
useMessageRenderer.ts中board_started渲染路径不变
AssistantText 浅灰气泡(R9-R15):
- R9:
MessageShell.vue的.message-shell__content加background: var(--bg-message-bubble)+border-radius: var(--radius-md)+padding: var(--space-3) var(--space-4)+border: 1px solid var(--border-color)+color: var(--text-primary),仅role === 'assistant'时生效。F1-A 决策:引入独立 token--bg-message-bubble(light#ffffff/ dark#1f1f1f),与--bg-secondary(inline code/table 背景)解耦,避免气泡背景与代码/表格背景视觉冲突 - R10:不使用
!important,不覆盖代码块/表格/路由 tag 样式 - R11:不影响
BoardRoundCard内AssistantText渲染 - R12:私董会专家发言、普通 chat 通过
MessageShell + AssistantText统一获得气泡;@team 阶段使用TeamPlanCard、Debate 使用DebateArgumentCard/DebateSummaryCard/DebateBannerCard等自带 card chrome 的组件(通过MessageShell渲染但不走AssistantText),由 R14 排除气泡 - R13:气泡宽度继承现有
width: 100%; max-width: 100%,不强制固定最大宽度 - R14:F4-A 决策(Round 4 扩展):气泡选择器排除所有自带 card chrome 的 assistant 消息类型(共 9 种):
board_conclusion(BoardConclusionCard→.board-conclusion-card,full chrome:bg + border + radius + shadow)、team_plan(TeamPlanCard→.team-plan-card,full chrome)、debate_banner(DebateBannerCard→.debate-banner,partial chrome:bg + left-border + radius)、debate_argument(DebateArgumentCard→.debate-argument,partial)、debate_summary(DebateSummaryCard→.debate-summary,partial)、debate_resolved(DebateConclusionCard→.debate-conclusion,partial + 4 变体 bg)、collaboration_graph(CollaborationGraphCard→.collab-graph,partial)、review_result(ReviewResultCard→.review-card,partial + 3 变体 bg)、risk_flagged(RiskFlagCard→.risk-card,partial)。chrome 区分(Round 4 修正):仅BoardConclusionCard+TeamPlanCard有完整 chrome(bg + border + radius + shadow),其余 7 种只有 bg + left-border + radius(无 shadow、无 full border)。已验证根 class 名(Round 4 R4-F1):注意 7 种 partial chrome 卡片 root class 名与组件名不匹配,不带-card后缀(.debate-banner/.debate-argument/.debate-summary/.debate-conclusion/.collab-graph/.review-card/.risk-card)。实现方式:在MessageShell.vue通过messageTypeprop 或:has()选择器排除(具体机制依 G3 决策)。第三种架构替代方案(Round 4 R4-A3):在MessageRenderSpec接口(useMessageRenderer.ts:44-48)新增bubble: boolean字段,在 renderer 层集中决策是否包裹气泡,避免 messageType prop 污染或:has()选择器脆弱性——见 Open Questions Round 4。未来防护(Round 4 R4-A4):新增 card-bearing 类型时需手动加入 F4-A 排除列表,或采用上述bubble字段方案由 renderer 集中管理——见 Open Questions Round 4 - R15:D4-方案1 决策:空 slot 内容(pre-stream thinking / tool-call-only)时气泡不渲染背景——通过
:empty选择器隐藏background / border / padding,仅显示 thinking dots。有内容流入后自动恢复气泡样式
UserBubble 普通文本深色气泡(R16-R19):
- R16:
UserBubble.vue的普通文本消息(<span class="user-bubble__text">)样式改为 demo 中的深色右对齐气泡:background: var(--color-primary)+color: var(--text-inverse)+padding: var(--space-3) var(--space-4)+max-width: 70% - R17:@board/@team 命令卡片(
.user-bubble__command)和文件附件保持现有--bg-tertiary浅色背景,不应用深色气泡样式 - R18:dark mode 自动反转——
--color-primary在 dark mode 下为#fbfbfa(浅),--text-inverse为#1a1a1a(深),user 气泡自动变为浅色背景 + 深色文字 - R19:通过
isPlainTextcomputed(!fileAttachment && !commandBubble)+.user-bubble--textmodifier class 区分普通文本与命令卡片,不修改.user-bubble默认样式
Key Technical Decisions
- 拦截点选择 ChatInput 按钮 click,不放在 BoardMeetingModal 内部 — 最自然的 UX 拦截点,避免用户填表后才发现不能发起。判断依据
chatStore.boardState实时状态,不依赖后端 /is_board标记,避免 reload 后误判(见 origin R4)。 - 私董会状态判定取
discussing | concluding—BoardState.status类型为"discussing" | "concluding" | "completed" | "dissolved"(chatStream.ts:65 确认)。completed | dissolved的旧私董会不阻塞新私董会发起,符合 origin R3 要求。 - "新建会话"按钮流程:
createConversation()→selectConversation(newId, true)—createConversation()是同步函数(chatStore.ts:333 确认),自动设置currentConversationId但不加载服务端会话;后续selectConversation(newId, true)强制 reload 确保状态干净。不自动打开BoardMeetingModal,由用户在新会话中再次点击"私董会"按钮继续(见 origin R2)。 - BoardBannerCard 简化为单行标题+副标题,不删除组件本身 — 保留
BoardBannerCard.vue文件,仅重构 template + style。useMessageRenderer.ts的board_started渲染路径不变(origin R8),保持 streaming 期间与 reload 后视觉一致。 - 气泡样式挂在
MessageShell.vue的.message-shell__content上,不挂在AssistantText.vue—MessageShell是所有 assistant 消息的统一外壳,挂在这里可一次性覆盖 board_speech / board_summary / 普通聊天 / @team / Debate 等场景(origin R12),且不影响UserBubble.vue的 user 消息样式。card-bearing 类型例外(F4-A Round 4 扩展,共 9 种):board_conclusion/team_plan/debate_banner/debate_argument/debate_summary/debate_resolved/collaboration_graph/review_result/risk_flagged渲染的 card 组件自带 card chrome(详见 R14),不被气泡包裹。 - 气泡使用 CSS 变量,禁止硬编码 — 新增
--bg-message-bubble: #ffffff(light) /#1f1f1f(dark) 到tokens.css,与--bg-secondary(inline code/table 背景#fbfbfa)解耦(F1-A 决策)。dark mode 自动切换。禁止硬编码#f3f4f6/#fbfbfa/#ffffff等值(project rules 硬约束)。 role === 'assistant'时启用气泡,role === 'user'不启用 — 通过.message-shell--assistant .message-shell__content选择器限定,避免污染 user 消息的UserBubble样式。- F4-A 排除所有 card-bearing 类型(Round 4 扩展,共 9 种) — 排除 9 种 card-bearing assistant 消息类型:
board_conclusion/team_plan/debate_banner/debate_argument/debate_summary/debate_resolved/collaboration_graph/review_result/risk_flagged。chrome 区分(Round 4 修正):仅BoardConclusionCard(.board-conclusion-card)+TeamPlanCard(.team-plan-card)有完整 chrome(bg + border + radius + shadow);其余 7 种(DebateBannerCard→.debate-banner、DebateArgumentCard→.debate-argument、DebateSummaryCard→.debate-summary、DebateConclusionCard→.debate-conclusion、CollaborationGraphCard→.collab-graph、ReviewResultCard→.review-card、RiskFlagCard→.risk-card)只有 bg + left-border + radius(无 shadow、无 full border)。气泡选择器需排除这些类型,避免"card 嵌套在气泡里"的视觉冲突。实现方式:在MessageShell.vue增加messageTypeprop(或:has()选择器),card-bearing 类型不加气泡样式。具体机制依 G3 决策。第三种替代方案:在MessageRenderSpec新增bubble: boolean字段由 renderer 集中决策(见 Open Questions Round 4 R4-A3)。未来防护:新增 card 类型需手动加入排除列表或采用bubble字段方案(见 Open Questions Round 4 R4-A4)。 - D4-方案1
:empty隐藏空气泡 — 空 slot 内容(pre-stream thinking / tool-call-only)时通过:empty选择器隐藏background / border / padding,仅显示 thinking dots 动画。有内容流入后自动恢复气泡样式,无需 JS 判断。 - U4 仅普通文本消息深色气泡,命令卡片保持浅色 —
UserBubble.vue同时渲染三种内容(普通文本 / @board|@team 命令卡片 / 文件附件),命令卡片含结构化信息(header + experts list),深色背景会降低可读性。通过isPlainTextcomputed +.user-bubble--textmodifier 精确限定深色样式仅作用于普通文本,命令卡片和文件附件继承.user-bubble默认--bg-tertiary浅色背景。 - U4 用
--color-primary+--text-inverse自动适配 dark mode —--color-primary在 light mode 为#1a1a1a(深),dark mode 为#fbfbfa(浅);--text-inverse反之。user 气泡在 dark mode 下自动反转为浅色背景 + 深色文字,无需额外 dark mode 覆盖。
Implementation Units
U1. ChatInput 拦截 + a-modal 弹窗 + 快捷新建会话
Goal: 在 ChatInput.vue 的"私董会"按钮 click 处增加拦截逻辑:当前会话已存在进行中的私董会时弹 a-modal 提示,提供"我知道了"和"新建会话"两个按钮;"新建会话"按钮调用 chatStore.createConversation() + selectConversation(newId, true) 跳转到新空会话。
Requirements: R1, R2, R3, R4
Dependencies: 无
Files:
- src/agentkit/server/frontend/src/components/chat/ChatInput.vue(修改)
- src/agentkit/server/frontend/src/components/chat/__tests__/ChatInput.test.ts(新建)
Approach:
- 在
<template>中新增一个<a-modal v-model:open="showBoardBlockModal">,标题"当前会话已存在私董会",内容"请新建会话来创建新的私董会",footer 两个按钮:<a-button @click="showBoardBlockModal = false">我知道了</a-button>+<a-button type="primary" @click="handleCreateNewConversationForBoard">新建会话</a-button> - 新增
showBoardBlockModal = ref(false)state - 新增
handleBoardClick()函数替代 inline@click="showBoardModal = true":- 检查
chatStore.boardState?.status:若为'discussing' | 'concluding'→showBoardBlockModal.value = true,return - 否则
showBoardModal.value = true
- 检查
- 新增
handleCreateNewConversationForBoard()函数:showBoardBlockModal.value = falsechatStore.createConversation()await chatStore.selectConversation(chatStore.currentConversationId!, true)- 不设置
showBoardModal.value = true(让用户在新会话中再次点击)
- "私董会"按钮
@click改为handleBoardClick - 焦点恢复(Round 4 R4-D3):a-modal 关闭时(无论"我知道了"还是"新建会话")需将焦点返回到触发按钮("私董会"按钮),符合 WAI-ARIA modal 对话模式。Ant Design Vue 的
<a-modal>通过@cancel/@ok事件触发关闭,关闭后焦点默认落在 modal 本身;需在handleCreateNewConversationForBoard和"我知道了"按钮 click 后显式nextTick(() => boardButtonRef.value?.focus())(或使用 autofocus 属性)。新建会话跳转后焦点自然落于新会话输入框,无需额外处理
Patterns to follow:
ChatInput.vue:63-71的showTeamModal模式(v-model:open + ref + handleXxxSubmit)chatStore.ts:333-345的createConversation同步签名chatStore.ts:219的selectConversation(id, force = false)异步签名
Test scenarios:
- Happy path:
chatStore.boardState === null→ 点击"私董会"按钮 →showBoardModal === true,showBoardBlockModal === false - Happy path:
chatStore.boardState?.status === 'completed'→ 点击"私董会"按钮 →showBoardModal === true(已完成私董会不阻塞) - Happy path:
chatStore.boardState?.status === 'dissolved'→ 同上 - 拦截 path:
chatStore.boardState?.status === 'discussing'→ 点击"私董会"按钮 →showBoardModal === false,showBoardBlockModal === true - 拦截 path:
chatStore.boardState?.status === 'concluding'→ 同上 - "新建会话"按钮:点击后
showBoardBlockModal === false,chatStore.currentConversationId已变化,chatStore.boardState === null(新会话清空 boardState) - "我知道了"按钮:点击后
showBoardBlockModal === false,chatStore.currentConversationId不变,showBoardModal === false
注:Pinia setup store 的 ref 自动解包,
chatStore.boardState已是BoardState | null,不能写chatStore.boardState.value。如需 ref 访问用storeToRefs(chatStore).boardState.value。
Verification:
- 启动 Tauri 客户端,在已有私董会的会话中点击"私董会"按钮,应弹出提示而非 modal
- 点击"新建会话"后自动跳转到新会话,boardState 清空
- 在新会话中点击"私董会"按钮可正常打开 BoardMeetingModal
U2. BoardBannerCard 简化为单行标题+副标题
Goal: 将 BoardBannerCard.vue 从带边框/紫条/进度条/专家 chip 的重样式简化为纯文本两行:私董会 — {topic} + 轮次:第 N/M 轮,删除 BankOutlined 图标、4px 紫条、专家 chip、卡片背景/边框/圆角/阴影。
Requirements: R5, R6, R7, R8
Dependencies: 无(与 U1/U3 互不依赖,可并行)
Files:
- src/agentkit/server/frontend/src/components/chat/messages/BoardBannerCard.vue(修改)
- src/agentkit/server/frontend/src/components/chat/helpers/useMessageRenderer.ts(修改,F3-A 决策:删除 board_banner case 中 experts prop 传参,避免 Vue 3 attribute fallthrough 把
[object Object]渲染到根 div) - src/agentkit/server/frontend/src/components/preview/scenes/Scene4BoardDiscussion.vue(修改,适配新 props)
- src/agentkit/server/frontend/src/components/chat/messages/__tests__/BoardBannerCard.test.ts(新建)
Approach:
<template>简化为:<div class="board-banner-card"> <div class="board-banner-card__title">私董会 — {{ topic }}</div> <div class="board-banner-card__meta">轮次:第 {{ currentRound }} / {{ maxRounds }} 轮</div> </div>- 删除
<script setup>中的BankOutlinedimport 和progressPercentcomputed interface Props删除experts字段,保留topic / maxRounds / currentRound<style scoped>删除.board-banner-card__bar / __body / __icon / __experts / __chip / __chip--moderator / __chip-avatar / __progress / __progress-fill共 9 个类,仅保留:.board-banner-card容器(margin-bottom: var(--space-3)+padding: var(--space-2) 0,无 background/border/shadow).board-banner-card__title(font-size: var(--font-md); font-weight: var(--font-weight-semibold); color: var(--text-primary); margin-bottom: var(--space-1)).board-banner-card__meta(font-size: var(--font-xs); color: var(--text-tertiary))
Scene4BoardDiscussion.vue中调用<BoardBannerCard>的地方删除:expertsprop 传参useMessageRenderer.ts中board_bannercase 删除expertsprop 传参(F3-A 决策)
Patterns to follow:
MessageShell.vue:150-163的.message-shell__name / __meta简洁文本样式
Test scenarios:
- Happy path:传入
topic="测试主题" maxRounds=5 currentRound=2→ 渲染两行文本,第一行"私董会 — 测试主题",第二行"轮次:第 2 / 5 轮" - 边界:
currentRound=0→ 第二行"轮次:第 0 / 5 轮"(不报错) - 边界:
maxRounds=0→ 第二行"轮次:第 1 / 0 轮"(不报错,不做除零校验) - 渲染契约:组件根 div 不含
background / border / border-radius / box-shadow任一 CSS 属性 - 渲染契约:组件不渲染
<BankOutlined />/.board-banner-card__bar/.board-banner-card__chip任一元素 - Props 契约:传入
experts数组时组件不报错(向后兼容,prop 已删除会被 Vue 忽略为 fallthrough attr,但需确认不会作为 attribute 渲染到根 div) - F3-A fallthrough 契约:
useMessageRenderer.ts的board_bannercase 不再传expertsprop(已删除),根 div 不含experts="[object Object]"属性
Verification:
- 启动 Tauri 客户端,发起私董会,第一条消息应显示为纯文本两行,无卡片背景/图标/进度条
- 预览场景
Scene4BoardDiscussion.vue仍然能正常渲染
U3. MessageShell 浅灰气泡(方案B)
Goal: 在 MessageShell.vue 的 .message-shell__content 上增加方案 B 风格的浅灰圆角矩形气泡样式,仅 role === 'assistant' 时生效,使用 CSS 变量绑定,dark mode 自动切换。F1-A 决策:气泡用独立 token --bg-message-bubble(与 inline code/table 的 --bg-secondary 解耦);F4-A 决策(Round 4 扩展,共 9 种):排除所有 card-bearing assistant 类型(board_conclusion / team_plan / debate_banner / debate_argument / debate_summary / debate_resolved / collaboration_graph / review_result / risk_flagged),这些类型由各自 card 组件自带 card chrome(2 种 full chrome + 7 种 partial chrome,详见 R14);D4-方案1 决策:空内容通过 :empty 隐藏气泡。
Requirements: R9, R10, R11, R12, R13, R14, R15
Dependencies: 无(与 U1/U2 互不依赖,可并行)
Files:
- src/agentkit/server/frontend/src/styles/tokens.css(修改,F1-A 决策:新增
--bg-message-bubbletoken,light#ffffff/ dark#1f1f1f) - src/agentkit/server/frontend/src/components/chat/messages/MessageShell.vue(修改)
- src/agentkit/server/frontend/src/components/chat/messages/__tests__/MessageShell.test.ts(新建或扩展)
Approach:
- 第一步(F1-A):在
tokens.css的:root(light)和[data-theme="dark"](或等效 dark 选择器)下新增--bg-message-bubble: #ffffff(light)/#1f1f1f(dark)。位置紧邻--bg-secondary定义,注释说明"消息气泡背景,与 inline code/table 背景解耦" - 第二步:在
MessageShell.vue的<style scoped>中新增规则:.message-shell--assistant .message-shell__content { background: var(--bg-message-bubble); border: 1px solid var(--border-color); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); color: var(--text-primary); } /* F4-A: 所有 card-bearing assistant 类型不加气泡(共 9 种,Round 4 扩展)。 已验证根 class 名:`.board-conclusion-card` / `.team-plan-card`(full chrome,含 shadow) / `.debate-banner` / `.debate-argument` / `.debate-summary` / `.debate-conclusion` / `.collab-graph` / `.review-card` / `.risk-card` (7 种 partial chrome,仅 bg + left-border + radius,无 shadow 无 full border;注意 root class 名与组件名不匹配,不带 -card 后缀)。 实现期依 G3 决策扩展选择器或 messageType prop 排除列表 */ .message-shell--assistant.message-shell--conclusion .message-shell__content, .message-shell--assistant:has(.board-conclusion-card) .message-shell__content { background: transparent; border: none; border-radius: 0; padding: 0; } /* D4-方案1: 空 slot 内容隐藏气泡背景,仅显示 thinking dots */ .message-shell--assistant .message-shell__content:empty { background: transparent; border: none; padding: 0; } - 第三步(F4-A 配套):
MessageShell.vue的interface Props新增messageType?: stringprop(可选),template 根 div 根据messageType === 'board_conclusion'添加.message-shell--conclusionclass。或使用:has(.board-conclusion-card)选择器(更简洁但需确认浏览器支持——现代浏览器已支持:has(),Tauri WebView 基于 Chromium 111+ 支持) - 选择器限定
.message-shell--assistant前缀,确保role === 'user'的UserBubble不受影响 - 不使用
!important,让AssistantText.vue:257-401内部的pre / hljs / code / table样式自然继承(这些样式已用--code-bg等 token,浅灰气泡背景下自动适配) - 不修改
.message-shell__content的display: flex / flex-direction: column / gap: var(--space-2) / width: 100% / max-width: 100%(R13 要求) - 不修改
.message-shell--user .message-shell__content的align-items: flex-end(user 消息保持右对齐,不加气泡)
Patterns to follow:
tokens.css:65,223的--bg-secondary定义(light#fbfbfa,dark#1f1f1f)——--bg-message-bubble紧邻此定义tokens.css:79,237的--border-color定义tokens.css:97的--radius-md: 6pxLoginView.vue:184-196的"显式绑定 token,禁硬编码"模式
Test scenarios:
- Happy path(assistant):渲染
<MessageShell role="assistant">+ 默认 slot 内容 →.message-shell__content计算样式含background-color: rgb(255, 255, 255)(light mode 下--bg-message-bubble: #ffffff) - Happy path(user):渲染
<MessageShell role="user">+ 默认 slot 内容 →.message-shell__content计算样式background-color为空或rgba(0,0,0,0)(不加气泡) - F1-A 区分性:slot 内含
<code>async</code>inline code → inline code 背景为--bg-secondary(#fbfbfa),气泡背景为--bg-message-bubble(#ffffff),二者视觉可区分 - 气泡内代码块:slot 内含
<pre><code>→ 代码块背景--code-bg不被气泡背景覆盖(视觉可区分) - 气泡宽度:
.message-shell__content计算样式width: 100%,max-width: 100%(跟随消息列宽) - Dark mode:切换到 dark mode →
.message-shell__content计算样式background-color: rgb(31, 31, 31)(--bg-message-bubble: #1f1f1f) - F4-A 排除 card-bearing 类型(Round 4 扩展,共 9 种):分别渲染
<MessageShell role="assistant" messageType="board_conclusion|team_plan|debate_banner|debate_argument|debate_summary|debate_resolved|collaboration_graph|review_result|risk_flagged">+ 对应 card slot(BoardConclusionCard/TeamPlanCard/DebateBannerCard/DebateArgumentCard/DebateSummaryCard/DebateConclusionCard/CollaborationGraphCard/ReviewResultCard/RiskFlagCard)→ 每种类型.message-shell__content计算样式background-color: transparent,border: none(不加气泡,card 自身 chrome 保留;2 种 full chrome + 7 种 partial chrome 详见 R14) - D4-方案1 空内容隐藏:渲染
<MessageShell role="assistant">+ 空 slot →.message-shell__content计算样式background-color: transparent,border: none,padding: 0(:empty选择器生效) - D4-方案1 有内容恢复:渲染
<MessageShell role="assistant">+ 非空 slot →:empty不匹配,气泡样式正常应用 - 渲染契约:样式不使用
!important - 渲染契约:样式不硬编码
#f3f4f6 / #fbfbfa / #ededec / #ffffff等值(必须用 CSS 变量)
Verification:
- 启动 Tauri 客户端,发起普通 chat,assistant 消息应有浅灰(实际为
#ffffff纯白,与#fbfbfa的 inline code 区分)圆角矩形气泡 - 发起私董会,专家发言(board_speech)应有同款气泡
- 发起私董会,结论消息(board_conclusion)应保留
BoardConclusionCard自身 card 样式,不被气泡包裹 - 同理验证
team_plan/debate_banner/debate_argument/debate_summary/debate_resolved/collaboration_graph/review_result/risk_flagged消息保留各自 card chrome(TeamPlanCard/DebateBannerCard/DebateArgumentCard/DebateSummaryCard/DebateConclusionCard/CollaborationGraphCard/ReviewResultCard/RiskFlagCard),不被气泡包裹(F4-A Round 4 扩展,共 9 种) - 切换 dark mode,气泡背景应自动变为深色(
#1f1f1f) - user 消息(右侧)应显示为 demo 中的深色气泡(
--color-primary背景 +--text-inverse白字),@board/@team 命令卡片保持现有浅色背景(见 U4) - assistant 消息在 thinking 阶段(空内容)不应显示空气泡矩形,仅显示三点动画
U4. UserBubble 普通文本深色气泡(参考 demo)
Goal: 将 UserBubble.vue 的普通文本消息(<span class="user-bubble__text">)样式改为 demo 中的深色右对齐气泡:--color-primary 背景 + --text-inverse 白字 + padding: var(--space-3) var(--space-4) + max-width: 70%。@board/@team 命令卡片和文件附件保持现有 --bg-tertiary 浅色背景,不变。
Requirements: R16, R17, R18, R19
Dependencies: 无(与 U1/U2/U3 互不依赖,可并行)
Files:
- src/agentkit/server/frontend/src/components/chat/messages/UserBubble.vue(修改)
- src/agentkit/server/frontend/src/components/chat/messages/__tests__/UserBubble.test.ts(新建或扩展)
Approach:
<script setup>新增isPlainTextcomputed:!fileAttachment.value && !commandBubble.value<template>根 div 的:class新增'user-bubble--text': isPlainTextmodifier:<div class="user-bubble" :class="{ 'user-bubble--focusable': msgId, 'user-bubble--text': isPlainText, }" ... ><style scoped>新增.user-bubble--text规则(仅普通文本消息时覆盖.user-bubble默认背景):/* U4: 普通文本消息参考 demo 深色气泡,command card / file attachment 保持浅色 */ .user-bubble--text { background: var(--color-primary); color: var(--text-inverse); padding: var(--space-3) var(--space-4); max-width: 70%; }- 不修改
.user-bubble默认样式(--bg-tertiary背景 +--text-primary文字),让 command card 和 file attachment 继承现有浅色背景 - 不修改
.user-bubble__command/.user-bubble__command-*内部样式(保持现有结构化卡片可读性) - 不修改
.user-bubble__actions操作按钮工具栏样式(当前--bg-secondary背景 +--border-color边框,在深色气泡旁可见性需验证) --color-primary和--text-inverse在 light/dark mode 下自动反转(tokens.css:18,176 + 75,233 确认),dark mode 下 user 气泡自动变为浅色背景 + 深色文字
Patterns to follow:
- demo
tmp_scheme_b_options_demo.html:230-240的.user-msg__bubble样式(--color-primary+--text-inverse+--space-3 var(--space-4)+--radius-lg+max-width: 70%) tokens.css:18,176的--color-primary定义(light#1a1a1a/ dark#fbfbfa)tokens.css:75,233的--text-inverse定义(light#ffffff/ dark#1a1a1a)
Test scenarios:
- Happy path(普通文本):渲染
<UserBubble content="Hello">→ 根 div 含.user-bubble--textclass,计算样式background-color: rgb(26, 26, 26)(light mode 下--color-primary: #1a1a1a),color: rgb(255, 255, 255)(--text-inverse: #ffffff) - Happy path(@board 命令):渲染
<UserBubble content="@board 测试主题">→ 根 div 不含.user-bubble--textclass,背景保持--bg-tertiary(浅色),command card 内部样式不变 - Happy path(@team 命令):渲染
<UserBubble content="@team 测试">→ 同上,保持浅色背景 - Happy path(文件附件):渲染
<UserBubble content="[文件][x.pdf](url)">→ 根 div 不含.user-bubble--textclass,背景保持--bg-tertiary - Dark mode:切换到 dark mode → 普通文本消息
.user-bubble--text计算样式background-color: rgb(251, 251, 250)(--color-primary: #fbfbfa),color: rgb(26, 26, 26)(--text-inverse: #1a1a1a),自动反转 - 操作按钮工具栏:普通文本消息 hover →
.user-bubble__actions可见,在深色气泡左侧(right: calc(100% + var(--space-2)))显示,--bg-secondary背景与深色气泡有视觉区分 - max-width 契约:普通文本消息
max-width: 70%,command card 保持max-width: 100%(.user-bubble__command自身设置) - 渲染契约:样式不使用
!important - 渲染契约:样式不硬编码
#1a1a1a / #ffffff等值(必须用 CSS 变量)
Verification:
- 启动 Tauri 客户端,发送普通文本消息 → 右侧显示深色圆角气泡,白字
- 发送 @board 命令 → 右侧显示浅色结构化命令卡片(保持现有样式)
- 发送 @team 命令 → 同上,保持浅色
- 切换 dark mode → 普通文本消息自动反转为浅色气泡 + 深色文字,command card 不变
- 普通文本消息 hover → 操作按钮工具栏在气泡左侧可见
Scope Boundaries
Deferred to Follow-Up Work
- 后端按会话去重
board_started:本次只做前端拦截,不改后端。如果未来需要在多端/多浏览器同步场景下做强一致,再考虑后端校验(origin 文档已声明 deferred)。 - StickyModeHeader 顶部条的视觉细化(徽章大小、专家头像间距、紫色边框粗细)不在本次范围。
- BoardConclusionCard / DebateBannerCard / TeamPlanCard 等其他 board 模式组件的样式统一:本次只改
BoardBannerCard,其他卡片样式留待后续迭代。
Outside this product's identity
- 不调整方案 B 调色板(
expertIdentity.ts的 12 色 PALETTE)和专家头像首字符规则。 - 不调整私董会后端流程(
BoardOrchestrator/chatStream事件顺序)。 - 不调整 AssistantText 的 markdown 渲染逻辑、代码高亮、表格样式。
Risks & Dependencies
- 风险 1:U3 气泡样式可能影响
AssistantText内部代码块/表格的视觉对比度 — 缓解:测试场景明确要求代码块背景--code-bg不被覆盖;AssistantText.vue:257-401的pre/hljs/code/table样式已用独立 token,理论上不受影响。如出现对比度问题,可在AssistantText.vue内微调--code-bg但不回滚 U3 的气泡。 - 风险 2:U1 的
selectConversation(newId, true)强制 reload 可能导致用户感知"卡顿" — 缓解:force=true仅在用户主动点击"新建会话"时触发,是用户预期行为;如确实卡顿,可考虑后续优化(不在本次范围)。 - 依赖 1:
chatStore.createConversation()是同步函数(无返回值),但内部已自动设置currentConversationId— 实现时需注意:先createConversation()再读chatStore.currentConversationId拿到新 id,不能直接const newId = await chatStore.createConversation()。 - 依赖 2:
BoardState.status当前联合类型为 4 个值,未来若新增forming / executing / synthesizing等中间状态需扩展"已存在"判断 — 在handleBoardClick中用if (status === 'discussing' || status === 'concluding')显式判断,未来扩展时只需补||。
Open Questions
以下问题在 ce-doc-review 中由 reviewer 提出。Round 1(2026-07-02 demo 确认) 已决策 F1/F3/F4/C2/D4,Round 2(2026-07-02 复审) 新增 7 个 findings(G1-G5 + D2-R2/D3-R2/D4-R2)全部 append 到本 section 留待实现期处理。其中 G1 挑战 Round 1 D4-方案1 决策(P0 load-bearing,3 reviewer 一致 confidence 100 认为不可实现),G5 挑战 Round 1 F1-A 决策(#ffffff 与 #fbfbfa 视觉不可区分)—— 实现期需优先处理这两个挑战。
已决策(Round 1 — 2026-07-02 demo 确认)
- F1(feasibility,P1)— 已决策 F1-A:U3 气泡背景与 inline code/table 背景冲突。决策:引入独立 token
--bg-message-bubble(light#ffffff/ dark#1f1f1f)区分气泡背景与 inline code/table 背景(保持--bg-secondary: #fbfbfa)。已固化到 R9、U3 Approach、Key Technical Decisions。⚠️ Round 2 G5 挑战:#ffffff与#fbfbfa色差 <1% 低于 JND,且#ffffff与--bg-primary相同导致气泡仅靠边框可见——见下方 G5。 - F3(feasibility,P2)— 已决策 F3-A:U2 删除
BoardBannerCard的expertsprop 后useMessageRenderer.ts:145的 fallthrough 风险。决策:在 U2 Files 列表中加useMessageRenderer.ts,删除board_bannercase 中expertsprop 传参。已固化到 U2 Files、U2 Approach、U2 Test scenarios。 - F4(feasibility,P2)— 已决策 F4-A:U3 气泡包裹
BoardConclusionCard的嵌套冲突。决策:气泡选择器排除board_conclusion类型,BoardConclusionCard保留自身 card chrome 不被气泡包裹。已固化到 R14、U3 Approach、U3 Test scenarios、Key Technical Decisions。⚠️ Round 2 G2/G3 挑战::has()事实错误 +messageTypeprop 选项需 ChatMessage.vue(不在 Files)——见下方 G2/G3。⚠️ Round 3 扩展:F4-A 排除列表从 1 种扩展到 5 种(新增team_plan/debate_banner/debate_argument/debate_summary)。⚠️ Round 4 扩展(R4-A1):进一步扩展到 9 种(新增debate_resolved/collaboration_graph/review_result/risk_flagged),并修正 chrome 描述(2 种 full chrome + 7 种 partial chrome)。详见 R14 与 Open Questions Round 4。 - C2(coherence,P2)— 已应用 safe_auto:R7 表述已修订为"删除
.board-banner-card的重样式(background/border/border-radius/box-shadow)及__bar / __chip等重样式类,保留.board-banner-card容器(仅 margin/padding)+__title / __meta最小样式"。 - D4(design-lens,P2)— 已决策 D4-方案1:U3 空内容气泡表现。决策:通过
:empty选择器隐藏background / border / padding,仅显示 thinking dots。已固化到 R15、U3 Approach、U3 Test scenarios、Key Technical Decisions。⚠️ Round 2 G1 推翻::empty永不匹配(AssistantText 总渲染根 div)——见下方 G1,实现期必须替换方案。
仍待实现期决定
Round 1 遗留
- D1(design-lens,P1):U1
selectConversation(newId, true)失败时无错误处理——modal 已关闭、currentConversationId 已变更、用户无反馈。待选方案:try/catch + a-message error toast + 回滚 currentConversationId。 - D2(design-lens,P2):U1 modal 关闭与 selectConversation resolve 之间的过渡期无 loading 反馈。待选方案:modal 内"新建会话"按钮 loading spinner / 全局 loading overlay。
- D3(design-lens,P2):U2 长 topic 文本无 overflow/wrap/truncation 策略。待选方案:
text-overflow: ellipsis+white-space: nowrap/word-break: break-word/ 多行 line-clamp。
Round 2 复审新增(2026-07-02)
P0/P1 load-bearing(挑战 Round 1 决策,实现期优先处理):
-
G1(feasibility + design-lens + adversarial 三方共识,P0,conf 100×3)— 推翻 Round 1 D4-方案1:
:empty选择器永不匹配。AssistantText.vue:1-6总是渲染根<div class="assistant-text">(含 loading spinner / tool cards / routing div),useMessageRenderer.ts:361-362总是将 AssistantText 作为 slot 子组件挂载到.message-shell__content,所以.message-shell__content在生产环境永远非空。D4-方案1 的:emptyCSS 规则不会生效,thinking 阶段会显示空气泡矩形(正是 D4-方案1 要防止的 UX 缺陷)。plan 的测试场景"空 slot →:empty生效"用真空 slot 不反映生产。待选方案:- (a) MessageShell 加
isEmptycomputed(!message.content && !message.thinking && toolCalls.length === 0)+.message-shell--emptyclass 覆盖气泡样式 —— 推荐,最简单 - (b) 用
:has(.assistant-text__loading:only-child)检测纯 loading 状态 —— 脆弱,不推荐 - (c) AssistantText 真空时不渲染根 div —— 高风险,可能破坏 ThinkingBlock / ToolCallCard
- 实现期需选择 (a)/(b)/(c) 并更新 R15、U3 Approach 第二步 CSS、U3 Test scenarios、Key Technical Decisions。
- (a) MessageShell 加
-
G5(adversarial + feasibility residual,P2,conf 75)— 挑战 Round 1 F1-A:
#ffffff(纯白)vs#fbfbfa(off-white)色差 <1% 低于人眼 JND(~2.3),且#ffffff与--bg-primary: #ffffff(页面背景)相同导致气泡仅靠 1px--border-color: #ededec边框可见。AE6"视觉可区分"断言可能不成立。origin 文档描述为"浅灰圆角矩形气泡",但#ffffff实际是白色。待选方案:- (a)
--bg-message-bubblelight 改为#f7f7f5(实际浅灰,与--bg-tertiary一致)—— 视觉符合 origin 描述 - (b) 保持
#ffffff但修订 AE6 + U3 Test scenarios "视觉可区分" 为"token 级解耦(未来兼容),视觉差异 sub-JND" - (c) 改用
#f3f4f6等更明显灰值 - 实现期需选择 (a)/(b)/(c) 并更新 R9、U3 Approach、AE6、tokens.css 值。
- (a)
P2 gated_auto(事实修正 + 决策落地):
-
G2(adversarial + feasibility residual,P2,conf 75)— Tauri
:has()事实错误:plan U3 Approach 第 202 行称"Tauri WebView 基于 Chromium 111+ 支持",实际 Tauri 2.x macOS 用 WKWebView(Safari 引擎),Linux 用 WebKitGTK,仅 Windows 用 WebView2(Chromium)。:has()在 Safari 15.4+(2022-03)/ WebKitGTK 2.35+ / Chromium 105+ 支持,实际可用但 plan 推理错误。Fix:修正 U3 Approach 第 202 行 + Key Technical Decisions F4-A 段为"Tauri 2.x macOS 用 WKWebView(Safari 15.4+ 支持:has());Linux WebKitGTK 2.35+;Windows WebView2 Chromium 105+。若需支持更老 macOS,优先用messageTypeprop 方案"。 -
G3(coherence + adversarial,P2,conf 75)— F4-A
:has()vsmessageTypeprop 决策悬而未决 + ChatMessage.vue 缺失:plan 对 F4-A 结论排除提供两个选项(:has(.board-conclusion-card)选择器 ORmessageTypeprop +.message-shell--conclusionclass)但不决策;且三处描述不一致(Key Technical Decisions 用:not(:has()),U3 Approach 用"或",U3 CSS 用两者 comma-joined)。若选messageTypeprop,需修改 ChatMessage.vue 传递:message-type="spec.type",但 ChatMessage.vue 不在 U3 Files 列表。Fix:实现期决策用messageTypeprop(更稳健,无 CSS 兼容风险),添加src/agentkit/server/frontend/src/components/chat/ChatMessage.vue到 U3 Files,更新 Key Technical Decisions + U3 Approach + U3 CSS 三处统一为messageTypeprop 方案。
P2 manual(设计决策):
-
D2-R2(design-lens,P2,conf 75)— U4 focus ring 在深色气泡上不可见:
UserBubble.vue:304用var(--accent-primary, #1a1a1a),但--accent-primary在 tokens.css 未定义,fallback#1a1a1a与--color-primary相同。U4 深色气泡(--color-primary: #1a1a1a背景)上 focus ring 不可见,WCAG 1.4.11 违规。待选方案:(a) 定义--accent-primarytoken(如#3b82f6light /#60a5fadark);(b) 改用--color-info/--accent-team;(c) 不在本次处理(pre-existing bug,U4 加剧但不引入)。 -
D3-R2(design-lens,P2,conf 75)— U3 dark mode 代码块对比度不足:dark mode 下
--code-bg: #11111b(tokens.css:255)vs--bg-message-bubble: #1f1f1f仅差 ~14 亮度级,代码块可能融入气泡。light mode 对比度足够(#1e1e2eon#ffffff)。待选方案:(a) 加 dark mode 专属测试场景验证对比度;(b) 调整--bg-message-bubbledark 值为#252525(接近--bg-elevated);(c) 给pre加border: 1px solid var(--border-color)。 -
D4-R2(design-lens,P2,conf 75)— U2 简化 banner 缺乏视觉分隔:删除所有 chrome 后,两行纯文本 banner(font-md semibold + font-xs tertiary)可能融入消息流,失去"section divider"作用。BankOutlined 图标删除后无视觉锚点。待选方案:(a) 保留 2px 左边框
--accent-board(匹配私董会身份,克制);(b) 加小色点前缀;(c) 纯排版足够("私董会 —" 文本前缀即锚点)。
Round 3 复审新增(2026-07-03)
P1 manual(origin scope 边界):
- R3-A1(scope-guardian,P1,conf 100)— U4 UserBubble 深色气泡超出 origin 范围:origin
docs/brainstorms/2026-07-02-private-board-restrictions-and-scheme-b-bubbles-requirements.md的 Key Decisions 明确声明"role === 'user' 的用户消息气泡保留现有 UserBubble.vue 的右对齐独立样式,不加 AssistantText 风格的浅灰块"。但本 plan U4 改动UserBubble.vue,将普通文本消息改为--color-primary深色背景 +--text-inverse白字的深色气泡(R16-R19),属于 origin 之外的 scope creep。待选方案:(a) Drop U4 及 R16-R19,保持 origin 不变(U3 仍完成 assistant 气泡统一);(b) 保留 U4 但正式 amend origin requirements doc,补充"用户普通文本消息改为深色右对齐气泡"决策。建议 (b),因为 U4 视觉冲击显著、demo 已确认方向,但需走 origin amend 流程避免 plan/origin 漂移。
P2 gated_auto(事实修正):
- R3-F2(feasibility,P2,conf 50)— selectConversation
force=true对 local 会话不生效:plan U1 Approach 中关于"切换会话时通过selectConversation(force=true)强制重新加载消息"的描述有事实偏差。chatStore.ts:233的守卫为!conv?.is_local && (force || ...),对is_local=true的会话(含新建会话)会跳过 reload 调用——状态干净实际由createConversation保证(新建即清空 boardState/selectedExpert 等)。建议 fix:在 U1 Approach / Key Technical Decisions 条目中明确归因——"切换会话时 boardState 清空由createConversation在新建会话时执行;对非 local 会话调用selectConversation时force=true仍生效"。无需修改代码,仅修正 plan 文档归因。
Round 4 复审新增(2026-07-03)
已应用(auto-resolve 决策汇总):
- R4-C1(coherence,P1,conf 100,safe_auto applied) — Lines 58 & 389 "board_conclusion 例外" 已更新为 "card-bearing 类型例外(F4-A Round 4 扩展)"
- R4-F1(feasibility,P2,conf 75,gated_auto applied) — 已验证 9 种 card root class 名并添加到 R14、Key Technical Decisions F4-A、U3 CSS 注释(注意 7 种 partial chrome 卡片 root class 名不带
-card后缀) - R4-A1(adversarial,P1,conf 100,auto-resolve applied) — F4-A 排除列表从 5 种扩展到 9 种(新增
debate_resolved/collaboration_graph/review_result/risk_flagged)。已更新 R14、Key Technical Decisions F4-A、U3 Goal、U3 CSS 注释、U3 Test scenarios、U3 Verification - R4-D1(design-lens,P2,conf 100,auto-resolve applied) — F4-A card chrome claim 已修正:仅
BoardConclusionCard+TeamPlanCard有完整 chrome(bg + border + radius + shadow),其余 7 种只有 partial chrome(bg + left-border + radius,无 shadow 无 full border)。已更新 R14、Key Technical Decisions F4-A、U3 CSS 注释 - R4-D3(design-lens,P2,conf 75,auto-resolve applied) — U1 Approach 新增焦点恢复说明:a-modal 关闭后焦点返回"私董会"按钮(WAI-ARIA modal 对话模式)
P2 manual(待后续处理):
- R4-DA1(design-lens + adversarial cross-persona promote,P2,conf 100)— 7 种 partial chrome 卡片硬编码浅色,dark mode 下与 F4-A 排除气泡后视觉冲突加剧:
DebateBannerCard(#f9f0ff)、DebateArgumentCard(#fafafa)、DebateSummaryCard(#fff7e6)、DebateConclusionCard(4 变体#fff7e6/#f6ffed/#fffbe6/#f5f5f5/#fff2f0)、CollaborationGraphCard(#f0f8ff)、ReviewResultCard(3 变体#fafafa/#f6ffed/#fffbe6/#fff2f0)、RiskFlagCard(#fff7e6)均使用硬编码浅色背景。U3 气泡用--bg-message-bubbletoken 自动适配 dark mode,但这些 card 被 F4-A 排除后,dark mode 下仍显示硬编码浅色背景,对比度刺眼。这是 pre-existing 问题,超出 U3 scope(U3 仅负责气泡,不负责修复 card 颜色)。建议:新建独立 unit(如 U5: Card Dark Mode Adaptation)将这 7 种 card 的硬编码颜色迁移到 CSS 变量,与 U3 并行但不阻塞。或暂记为 follow-up tech debt。 - R4-A3(adversarial,P2,conf 75)— F4-A 遗漏第三种架构替代方案:当前 G3 决策在
messageTypeprop 与:has()选择器间二选一。存在第三种方案:在MessageRenderSpec接口(useMessageRenderer.ts:44-48)新增bubble: boolean字段,在 renderer 层(ChatMessage.vue)集中决策是否包裹气泡,card-bearing 类型在 spec 定义时设bubble: false。优势:新增 card 类型时无需改MessageShell.vue排除列表,避免 R4-A4 的未来防护问题;spec 集中管理,单一数据源。劣势:需修改MessageRenderSpec接口和所有 spec 定义点(12+ 类型),改动面比 prop/selector 方案大。建议:实现期若 G3 二选一难以决断,可考虑此方案作为 tie-breaker;否则记为 future refactor 候选。 - R4-A4(adversarial,P2,conf 75)— F4-A 排除列表对未来新增 card 类型无防护:当前 F4-A 排除列表硬编码 9 种类型,新增 card-bearing 类型时需手动加入
MessageShell.vue排除列表(prop 或 selector 方案),否则新 card 会被气泡错误包裹。与 R4-A3 关联:采用MessageRenderSpec.bubble字段方案可从根本上解决(renderer 集中决策),但需更大重构。建议:若采用 G3 prop/selector 方案,需在useMessageRenderer.ts新增 card 类型时强制走 code review checklist 验证 F4-A 排除列表是否同步更新;或采用 R4-A3 的bubble字段方案一劳永逸。
System-Wide Impact
- 前端用户:所有 Tauri 客户端和 Web GUI 用户将看到 (a) 私董会按钮在已有私董会的会话中弹提示而非 modal;(b) 私董会开始消息显示为简洁两行文本;(c) 所有 assistant 消息有浅灰圆角气泡(
--bg-message-bubble: #ffffff),card-bearing 类型例外(保留自身 card);(d) thinking 阶段不显示空气泡。视觉变化明显但符合方案 B 整体取向。 - 后端/运维:无影响(不动后端、不动部署、不动配置)。
- 其他团队:无影响(仅前端 Vue 组件)。
Acceptance Examples
- AE1:用户在已存在
status='discussing'私董会的会话中点击"私董会"按钮 → 弹出 a-modal 提示"当前会话已存在私董会",点击"新建会话"后自动跳转到新空会话,boardState 清空。 - AE2:用户在新会话中发起私董会 → 第一条消息显示为两行纯文本"私董会 — {topic}" + "轮次:第 1/5 轮",无卡片背景/图标/进度条。
- AE3:用户发起私董会,专家依次发言 → 每条专家发言(board_speech)显示为:彩色圆形头像 + 粗体名字 + "第 N 轮 · 专家" + 浅灰圆角矩形气泡包裹内容。
- AE4:用户发起普通 chat(不涉及私董会)→ assistant 消息有浅灰圆角气泡,user 普通文本消息有深色右对齐气泡(U4)。
- AE5:用户切换到 dark mode → 浅灰气泡自动变为深色(
--bg-message-bubble: #1f1f1f),代码块/表格对比度保持可读。 - AE6(F1-A):assistant 消息气泡内含 inline code → inline code 背景
--bg-secondary: #fbfbfa与气泡背景--bg-message-bubble: #ffffff视觉可区分。 - AE7(F4-A):私董会结论消息(board_conclusion)→
BoardConclusionCard保留自身 card chrome(background/border/shadow),不被气泡包裹,无嵌套冲突。 - AE8(D4-方案1):assistant 消息在 thinking 阶段(空内容)→ 不显示空气泡矩形,仅显示三点动画;内容流入后自动出现气泡。
- AE9(U4):用户发送普通文本消息 → 右侧显示深色圆角气泡(
--color-primary背景 + 白字),max-width 70% - AE10(U4):用户发送 @board 命令 → 右侧显示浅色结构化命令卡片(保持现有
--bg-tertiary背景),不受深色气泡影响 - AE11(U4):切换 dark mode → 普通文本消息自动反转为浅色气泡 + 深色文字,@board 命令卡片样式不变