fischer-agentkit/docs/plans/2026-07-02-001-feat-private...

51 KiB
Raw Permalink Blame History

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

  1. 单会话多私董会无约束ChatInput.vue:75 的"私董会"按钮 @click 直接 showBoardModal = true,对当前会话是否已存在私董会无任何判断。连续两次 SendMessage("@board:...") 会创建第二个私董会,叠加在第一个未结束的私董会之上,boardState.experts 被覆盖、轮次错乱、StickyModeHeader 头像数与实际不符。
  2. BoardBannerCard 样式过重BoardBannerCard.vue 使用了 card+border+shadow+4px 紫条+进度条+专家 chip pill 等装饰,与方案 B 整体"克制、不重样式"取向冲突。方案 B 截图中的"开始"区域是单行文本,不带装饰。
  3. 方案 B 气泡未落地:方案 B 截图中专家发言区域浅灰圆角矩形气泡包裹内容,与 ChatGPT / Notion AI 风格一致。当前 MessageShell.vue:178-184.message-shell__content 没有背景/边框/圆角,气泡效果完全缺失。

Requirements

完整继承 origin 文档的 13 条 R-IDsR1-R13并新增 R14-R19F4-A / D4-方案1 / U4 决策固化),共 19 条,分组如下:

单会话私董会限制R1-R4

  • R1ChatInput.vue "私董会"按钮 @click 在打开 BoardMeetingModal 前检查 chatStore.boardStatestatus === 'discussing' | 'concluding' 时弹 a-modal打开 modal
  • R2a-modal 标题"当前会话已存在私董会",副文"请新建会话来创建新的私董会",按钮"我知道了"+ "新建会话"(主操作);"新建会话"流程:关 modal → chatStore.createConversation()chatStore.selectConversation(newId, true) → 不自动打开 modal
  • R3boardState === nullstatus === 'completed' | 'dissolved' 时保持当前行为
  • R4以前端 chatStore.boardState 为权威源,不依赖后端 / is_board 标记

BoardBannerCard 简化R5-R8

  • R5重构为单行标题+副标题,保留 topic / maxRounds / currentRound props 向后兼容,删除 experts prop
  • R6模板输出两行私董会 — {topic} + 轮次:第 {currentRound} / {maxRounds} 轮
  • R7删除 .board-banner-card 的重样式background/border/border-radius/box-shadow__bar / __chip 等重样式类,保留 .board-banner-card 容器(仅 margin/padding+ __title / __meta 最小样式
  • R8useMessageRenderer.tsboard_started 渲染路径不变

AssistantText 浅灰气泡R9-R15

  • R9MessageShell.vue.message-shell__contentbackground: 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-bubblelight #ffffff / dark #1f1f1f),与 --bg-secondaryinline code/table 背景)解耦,避免气泡背景与代码/表格背景视觉冲突
  • R10不使用 !important,不覆盖代码块/表格/路由 tag 样式
  • R11不影响 BoardRoundCardAssistantText 渲染
  • R12私董会专家发言、普通 chat 通过 MessageShell + AssistantText 统一获得气泡;@team 阶段使用 TeamPlanCard、Debate 使用 DebateArgumentCard/DebateSummaryCard/DebateBannerCard 等自带 card chrome 的组件(通过 MessageShell 渲染但不走 AssistantText),由 R14 排除气泡
  • R13气泡宽度继承现有 width: 100%; max-width: 100%,不强制固定最大宽度
  • R14F4-A 决策Round 4 扩展):气泡选择器排除所有自带 card chrome 的 assistant 消息类型(共 9 种):board_conclusionBoardConclusionCard.board-conclusion-cardfull chromebg + border + radius + shadowteam_planTeamPlanCard.team-plan-cardfull chromedebate_bannerDebateBannerCard.debate-bannerpartial chromebg + left-border + radiusdebate_argumentDebateArgumentCard.debate-argumentpartialdebate_summaryDebateSummaryCard.debate-summarypartialdebate_resolvedDebateConclusionCard.debate-conclusionpartial + 4 变体 bgcollaboration_graphCollaborationGraphCard.collab-graphpartialreview_resultReviewResultCard.review-cardpartial + 3 变体 bgrisk_flaggedRiskFlagCard.risk-cardpartialchrome 区分Round 4 修正):仅 BoardConclusionCard + TeamPlanCard 有完整 chromebg + 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 通过 messageType prop 或 :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
  • R15D4-方案1 决策:空 slot 内容pre-stream thinking / tool-call-only时气泡不渲染背景——通过 :empty 选择器隐藏 background / border / padding,仅显示 thinking dots。有内容流入后自动恢复气泡样式

UserBubble 普通文本深色气泡R16-R19

  • R16UserBubble.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 浅色背景,不应用深色气泡样式
  • R18dark mode 自动反转——--color-primary 在 dark mode 下为 #fbfbfa(浅),--text-inverse#1a1a1auser 气泡自动变为浅色背景 + 深色文字
  • R19通过 isPlainText computed!fileAttachment && !commandBubble+ .user-bubble--text modifier class 区分普通文本与命令卡片,不修改 .user-bubble 默认样式

Key Technical Decisions

  • 拦截点选择 ChatInput 按钮 click不放在 BoardMeetingModal 内部 — 最自然的 UX 拦截点,避免用户填表后才发现不能发起。判断依据 chatStore.boardState 实时状态,不依赖后端 / is_board 标记,避免 reload 后误判(见 origin R4
  • 私董会状态判定取 discussing | concludingBoardState.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.tsboard_started 渲染路径不变origin R8保持 streaming 期间与 reload 后视觉一致。
  • 气泡样式挂在 MessageShell.vue.message-shell__content 上,不挂在 AssistantText.vueMessageShell 是所有 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-secondaryinline 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_flaggedchrome 区分Round 4 修正):仅 BoardConclusionCard.board-conclusion-card+ TeamPlanCard.team-plan-card)有完整 chromebg + border + radius + shadow其余 7 种(DebateBannerCard.debate-bannerDebateArgumentCard.debate-argumentDebateSummaryCard.debate-summaryDebateConclusionCard.debate-conclusionCollaborationGraphCard.collab-graphReviewResultCard.review-cardRiskFlagCard.risk-card)只有 bg + left-border + radius无 shadow、无 full border。气泡选择器需排除这些类型避免"card 嵌套在气泡里"的视觉冲突。实现方式:在 MessageShell.vue 增加 messageType prop: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深色背景会降低可读性。通过 isPlainText computed + .user-bubble--text modifier 精确限定深色样式仅作用于普通文本,命令卡片和文件附件继承 .user-bubble 默认 --bg-tertiary 浅色背景。
  • U4 用 --color-primary + --text-inverse 自动适配 dark mode--color-primary 在 light mode 为 #1a1a1adark 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 = truereturn
    • 否则 showBoardModal.value = true
  • 新增 handleCreateNewConversationForBoard() 函数:
    • showBoardBlockModal.value = false
    • chatStore.createConversation()
    • await chatStore.selectConversation(chatStore.currentConversationId!, true)
    • 不设置 showBoardModal.value = true(让用户在新会话中再次点击)
  • "私董会"按钮 @click 改为 handleBoardClick
  • 焦点恢复Round 4 R4-D3a-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-71showTeamModal 模式v-model:open + ref + handleXxxSubmit
  • chatStore.ts:333-345createConversation 同步签名
  • chatStore.ts:219selectConversation(id, force = false) 异步签名

Test scenarios:

  • Happy pathchatStore.boardState === null → 点击"私董会"按钮 → showBoardModal === trueshowBoardBlockModal === false
  • Happy pathchatStore.boardState?.status === 'completed' → 点击"私董会"按钮 → showBoardModal === true(已完成私董会不阻塞)
  • Happy pathchatStore.boardState?.status === 'dissolved' → 同上
  • 拦截 pathchatStore.boardState?.status === 'discussing' → 点击"私董会"按钮 → showBoardModal === falseshowBoardBlockModal === true
  • 拦截 pathchatStore.boardState?.status === 'concluding' → 同上
  • "新建会话"按钮:点击后 showBoardBlockModal === falsechatStore.currentConversationId 已变化,chatStore.boardState === null(新会话清空 boardState
  • "我知道了"按钮:点击后 showBoardBlockModal === falsechatStore.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> 中的 BankOutlined import 和 progressPercent computed
  • 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__titlefont-size: var(--font-md); font-weight: var(--font-weight-semibold); color: var(--text-primary); margin-bottom: var(--space-1)
    • .board-banner-card__metafont-size: var(--font-xs); color: var(--text-tertiary)
  • Scene4BoardDiscussion.vue 中调用 <BoardBannerCard> 的地方删除 :experts prop 传参
  • useMessageRenderer.tsboard_banner case 删除 experts prop 传参(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.tsboard_banner case 不再传 experts prop已删除根 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 chrome2 种 full chrome + 7 种 partial chrome详见 R14D4-方案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-bubble tokenlight #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:rootlight[data-theme="dark"](或等效 dark 选择器)下新增 --bg-message-bubble: #fffffflight/ #1f1f1fdark。位置紧邻 --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.vueinterface Props 新增 messageType?: string prop可选template 根 div 根据 messageType === 'board_conclusion' 添加 .message-shell--conclusion class。或使用 :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__contentdisplay: flex / flex-direction: column / gap: var(--space-2) / width: 100% / max-width: 100%R13 要求)
  • 不修改 .message-shell--user .message-shell__contentalign-items: flex-enduser 消息保持右对齐,不加气泡)

Patterns to follow:

  • tokens.css:65,223--bg-secondary 定义light #fbfbfadark #1f1f1f)—— --bg-message-bubble 紧邻此定义
  • tokens.css:79,237--border-color 定义
  • tokens.css:97--radius-md: 6px
  • LoginView.vue:184-196 的"显式绑定 token禁硬编码"模式

Test scenarios:

  • Happy pathassistant渲染 <MessageShell role="assistant"> + 默认 slot 内容 → .message-shell__content 计算样式含 background-color: rgb(255, 255, 255)light mode 下 --bg-message-bubble: #ffffff
  • Happy pathuser渲染 <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 slotBoardConclusionCard / TeamPlanCard / DebateBannerCard / DebateArgumentCard / DebateSummaryCard / DebateConclusionCard / CollaborationGraphCard / ReviewResultCard / RiskFlagCard)→ 每种类型 .message-shell__content 计算样式 background-color: transparentborder: none不加气泡card 自身 chrome 保留2 种 full chrome + 7 种 partial chrome 详见 R14
  • D4-方案1 空内容隐藏:渲染 <MessageShell role="assistant"> + 空 slot → .message-shell__content 计算样式 background-color: transparentborder: nonepadding: 0:empty 选择器生效)
  • D4-方案1 有内容恢复:渲染 <MessageShell role="assistant"> + 非空 slot → :empty 不匹配,气泡样式正常应用
  • 渲染契约:样式使用 !important
  • 渲染契约:样式硬编码 #f3f4f6 / #fbfbfa / #ededec / #ffffff 等值(必须用 CSS 变量)

Verification:

  • 启动 Tauri 客户端,发起普通 chatassistant 消息应有浅灰(实际为 #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 chromeTeamPlanCard / 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> 新增 isPlainText computed!fileAttachment.value && !commandBubble.value
  • <template> 根 div 的 :class 新增 'user-bubble--text': isPlainText modifier
    <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--text class计算样式 background-color: rgb(26, 26, 26)light mode 下 --color-primary: #1a1a1acolor: rgb(255, 255, 255)--text-inverse: #ffffff
  • Happy path@board 命令):渲染 <UserBubble content="@board 测试主题"> → 根 div .user-bubble--text class背景保持 --bg-tertiary浅色command card 内部样式不变
  • Happy path@team 命令):渲染 <UserBubble content="@team 测试"> → 同上,保持浅色背景
  • Happy path文件附件渲染 <UserBubble content="[文件][x.pdf](url)"> → 根 div .user-bubble--text class背景保持 --bg-tertiary
  • Dark mode切换到 dark mode → 普通文本消息 .user-bubble--text 计算样式 background-color: rgb(251, 251, 250)--color-primary: #fbfbfacolor: 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

  • 风险 1U3 气泡样式可能影响 AssistantText 内部代码块/表格的视觉对比度 — 缓解:测试场景明确要求代码块背景 --code-bg 不被覆盖;AssistantText.vue:257-401pre/hljs/code/table 样式已用独立 token理论上不受影响。如出现对比度问题可在 AssistantText.vue 内微调 --code-bg回滚 U3 的气泡。
  • 风险 2U1 的 selectConversation(newId, true) 强制 reload 可能导致用户感知"卡顿" — 缓解:force=true 仅在用户主动点击"新建会话"时触发,是用户预期行为;如确实卡顿,可考虑后续优化(不在本次范围)。
  • 依赖 1chatStore.createConversation() 是同步函数(无返回值),但内部已自动设置 currentConversationId — 实现时需注意:先 createConversation() 再读 chatStore.currentConversationId 拿到新 id不能直接 const newId = await chatStore.createConversation()
  • 依赖 2BoardState.status 当前联合类型为 4 个值,未来若新增 forming / executing / synthesizing 等中间状态需扩展"已存在"判断 — 在 handleBoardClick 中用 if (status === 'discussing' || status === 'concluding') 显式判断,未来扩展时只需补 ||

Open Questions

以下问题在 ce-doc-review 中由 reviewer 提出。Round 12026-07-02 demo 确认) 已决策 F1/F3/F4/C2/D4Round 22026-07-02 复审) 新增 7 个 findingsG1-G5 + D2-R2/D3-R2/D4-R2全部 append 到本 section 留待实现期处理。其中 G1 挑战 Round 1 D4-方案1 决策P0 load-bearing3 reviewer 一致 confidence 100 认为不可实现),G5 挑战 Round 1 F1-A 决策#ffffff#fbfbfa 视觉不可区分)—— 实现期需优先处理这两个挑战。

已决策Round 1 — 2026-07-02 demo 确认)

  • F1feasibilityP1— 已决策 F1-AU3 气泡背景与 inline code/table 背景冲突。决策:引入独立 token --bg-message-bubblelight #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。
  • F3feasibilityP2— 已决策 F3-AU2 删除 BoardBannerCardexperts prop 后 useMessageRenderer.ts:145 的 fallthrough 风险。决策:在 U2 Files 列表中加 useMessageRenderer.ts,删除 board_banner case 中 experts prop 传参。已固化到 U2 Files、U2 Approach、U2 Test scenarios。
  • F4feasibilityP2— 已决策 F4-AU3 气泡包裹 BoardConclusionCard 的嵌套冲突。决策:气泡选择器排除 board_conclusion 类型,BoardConclusionCard 保留自身 card chrome 不被气泡包裹。已固化到 R14、U3 Approach、U3 Test scenarios、Key Technical Decisions。⚠️ Round 2 G2/G3 挑战:has() 事实错误 + messageType prop 选项需 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。
  • C2coherenceP2— 已应用 safe_autoR7 表述已修订为"删除 .board-banner-card 的重样式background/border/border-radius/box-shadow__bar / __chip 等重样式类,保留 .board-banner-card 容器(仅 margin/padding+ __title / __meta 最小样式"。
  • D4design-lensP2— 已决策 D4-方案1U3 空内容气泡表现。决策:通过 :empty 选择器隐藏 background / border / padding,仅显示 thinking dots。已固化到 R15、U3 Approach、U3 Test scenarios、Key Technical Decisions。⚠️ Round 2 G1 推翻:empty 永不匹配AssistantText 总渲染根 div——见下方 G1实现期必须替换方案。

仍待实现期决定

Round 1 遗留

  • D1design-lensP1U1 selectConversation(newId, true) 失败时无错误处理——modal 已关闭、currentConversationId 已变更、用户无反馈。待选方案try/catch + a-message error toast + 回滚 currentConversationId。
  • D2design-lensP2U1 modal 关闭与 selectConversation resolve 之间的过渡期无 loading 反馈。待选方案modal 内"新建会话"按钮 loading spinner / 全局 loading overlay。
  • D3design-lensP2U2 长 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 决策,实现期优先处理):

  • G1feasibility + design-lens + adversarial 三方共识P0conf 100×3— 推翻 Round 1 D4-方案1:empty 选择器永不匹配。AssistantText.vue:1-6 总是渲染根 <div class="assistant-text">(含 loading spinner / tool cards / routing divuseMessageRenderer.ts:361-362 总是将 AssistantText 作为 slot 子组件挂载到 .message-shell__content,所以 .message-shell__content 在生产环境永远非空。D4-方案1 的 :empty CSS 规则不会生效thinking 阶段会显示空气泡矩形(正是 D4-方案1 要防止的 UX 缺陷。plan 的测试场景"空 slot → :empty 生效"用真空 slot 不反映生产。待选方案

    • (a) MessageShell 加 isEmpty computed!message.content && !message.thinking && toolCalls.length === 0+ .message-shell--empty class 覆盖气泡样式 —— 推荐,最简单
    • (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。
  • G5adversarial + feasibility residualP2conf 75— 挑战 Round 1 F1-A#ffffff纯白vs #fbfbfaoff-white色差 <1% 低于人眼 JND~2.3),且 #ffffff--bg-primary: #ffffff(页面背景)相同导致气泡仅靠 1px --border-color: #ededec 边框可见。AE6"视觉可区分"断言可能不成立。origin 文档描述为"浅灰圆角矩形气泡",但 #ffffff 实际是白色。待选方案

    • (a) --bg-message-bubble light 改为 #f7f7f5(实际浅灰,与 --bg-tertiary 一致)—— 视觉符合 origin 描述
    • (b) 保持 #ffffff 但修订 AE6 + U3 Test scenarios "视觉可区分" 为"token 级解耦(未来兼容),视觉差异 sub-JND"
    • (c) 改用 #f3f4f6 等更明显灰值
    • 实现期需选择 (a)/(b)/(c) 并更新 R9、U3 Approach、AE6、tokens.css 值。

P2 gated_auto事实修正 + 决策落地):

  • G2adversarial + feasibility residualP2conf 75— Tauri :has() 事实错误plan U3 Approach 第 202 行称"Tauri WebView 基于 Chromium 111+ 支持",实际 Tauri 2.x macOS 用 WKWebViewSafari 引擎Linux 用 WebKitGTK仅 Windows 用 WebView2Chromium: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 用 WKWebViewSafari 15.4+ 支持 :has()Linux WebKitGTK 2.35+Windows WebView2 Chromium 105+。若需支持更老 macOS优先用 messageType prop 方案"。

  • G3coherence + adversarialP2conf 75— F4-A :has() vs messageType prop 决策悬而未决 + ChatMessage.vue 缺失plan 对 F4-A 结论排除提供两个选项(:has(.board-conclusion-card) 选择器 OR messageType prop + .message-shell--conclusion class但不决策且三处描述不一致Key Technical Decisions 用 :not(:has())U3 Approach 用"或"U3 CSS 用两者 comma-joined。若选 messageType prop需修改 ChatMessage.vue 传递 :message-type="spec.type",但 ChatMessage.vue 不在 U3 Files 列表。Fix:实现期决策用 messageType prop更稳健无 CSS 兼容风险),添加 src/agentkit/server/frontend/src/components/chat/ChatMessage.vue 到 U3 Files更新 Key Technical Decisions + U3 Approach + U3 CSS 三处统一为 messageType prop 方案。

P2 manual设计决策

  • D2-R2design-lensP2conf 75— U4 focus ring 在深色气泡上不可见UserBubble.vue:304var(--accent-primary, #1a1a1a),但 --accent-primary 在 tokens.css 未定义fallback #1a1a1a--color-primary 相同。U4 深色气泡(--color-primary: #1a1a1a 背景)上 focus ring 不可见WCAG 1.4.11 违规。待选方案(a) 定义 --accent-primary token#3b82f6 light / #60a5fa dark(b) 改用 --color-info / --accent-team(c) 不在本次处理pre-existing bugU4 加剧但不引入)。

  • D3-R2design-lensP2conf 75— U3 dark mode 代码块对比度不足dark mode 下 --code-bg: #11111btokens.css:255vs --bg-message-bubble: #1f1f1f 仅差 ~14 亮度级代码块可能融入气泡。light mode 对比度足够(#1e1e2e on #ffffff)。待选方案(a) 加 dark mode 专属测试场景验证对比度;(b) 调整 --bg-message-bubble dark 值为 #252525(接近 --bg-elevated(c) 给 preborder: 1px solid var(--border-color)

  • D4-R2design-lensP2conf 75— U2 简化 banner 缺乏视觉分隔:删除所有 chrome 后,两行纯文本 bannerfont-md semibold + font-xs tertiary可能融入消息流失去"section divider"作用。BankOutlined 图标删除后无视觉锚点。待选方案(a) 保留 2px 左边框 --accent-board(匹配私董会身份,克制);(b) 加小色点前缀;(c) 纯排版足够("私董会 —" 文本前缀即锚点)。

Round 3 复审新增2026-07-03

P1 manualorigin scope 边界):

  • R3-A1scope-guardianP1conf 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-F2feasibilityP2conf 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 会话调用 selectConversationforce=true 仍生效"。无需修改代码,仅修正 plan 文档归因。

Round 4 复审新增2026-07-03

已应用auto-resolve 决策汇总):

  • R4-C1coherenceP1conf 100safe_auto applied — Lines 58 & 389 "board_conclusion 例外" 已更新为 "card-bearing 类型例外F4-A Round 4 扩展)"
  • R4-F1feasibilityP2conf 75gated_auto applied — 已验证 9 种 card root class 名并添加到 R14、Key Technical Decisions F4-A、U3 CSS 注释(注意 7 种 partial chrome 卡片 root class 名不带 -card 后缀)
  • R4-A1adversarialP1conf 100auto-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-D1design-lensP2conf 100auto-resolve applied — F4-A card chrome claim 已修正:仅 BoardConclusionCard + TeamPlanCard 有完整 chromebg + border + radius + shadow其余 7 种只有 partial chromebg + left-border + radius无 shadow 无 full border。已更新 R14、Key Technical Decisions F4-A、U3 CSS 注释
  • R4-D3design-lensP2conf 75auto-resolve applied — U1 Approach 新增焦点恢复说明a-modal 关闭后焦点返回"私董会"按钮WAI-ARIA modal 对话模式)

P2 manual待后续处理

  • R4-DA1design-lens + adversarial cross-persona promoteP2conf 100— 7 种 partial chrome 卡片硬编码浅色dark mode 下与 F4-A 排除气泡后视觉冲突加剧DebateBannerCard#f9f0ff)、DebateArgumentCard#fafafa)、DebateSummaryCard#fff7e6)、DebateConclusionCard4 变体 #fff7e6/#f6ffed/#fffbe6/#f5f5f5/#fff2f0)、CollaborationGraphCard#f0f8ff)、ReviewResultCard3 变体 #fafafa/#f6ffed/#fffbe6/#fff2f0)、RiskFlagCard#fff7e6均使用硬编码浅色背景。U3 气泡用 --bg-message-bubble token 自动适配 dark mode但这些 card 被 F4-A 排除后dark mode 下仍显示硬编码浅色背景,对比度刺眼。这是 pre-existing 问题,超出 U3 scopeU3 仅负责气泡,不负责修复 card 颜色)。建议:新建独立 unit如 U5: Card Dark Mode Adaptation将这 7 种 card 的硬编码颜色迁移到 CSS 变量,与 U3 并行但不阻塞。或暂记为 follow-up tech debt。
  • R4-A3adversarialP2conf 75— F4-A 遗漏第三种架构替代方案:当前 G3 决策在 messageType prop 与 :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-A4adversarialP2conf 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: #ffffffcard-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),代码块/表格对比度保持可读。
  • AE6F1-Aassistant 消息气泡内含 inline code → inline code 背景 --bg-secondary: #fbfbfa 与气泡背景 --bg-message-bubble: #ffffff 视觉可区分。
  • AE7F4-A私董会结论消息board_conclusionBoardConclusionCard 保留自身 card chromebackground/border/shadow被气泡包裹,无嵌套冲突。
  • AE8D4-方案1assistant 消息在 thinking 阶段(空内容)→ 不显示空气泡矩形,仅显示三点动画;内容流入后自动出现气泡。
  • AE9U4用户发送普通文本消息 → 右侧显示深色圆角气泡(--color-primary 背景 + 白字max-width 70%
  • AE10U4用户发送 @board 命令 → 右侧显示浅色结构化命令卡片(保持现有 --bg-tertiary 背景),不受深色气泡影响
  • AE11U4切换 dark mode → 普通文本消息自动反转为浅色气泡 + 深色文字,@board 命令卡片样式不变