Files
hermes-ui/docs/phases/phase_006.md
T
toor 1e9ea89d86
Build / build (push) Canceled after 0s
NPM Lockfile Check / npm ci --ignore-scripts (push) Canceled after 0s
Playwright / e2e (push) Canceled after 0s
chore: bootstrap hermes-ui skeleton from 0.6.29 snapshot
Import the client, server, skills, tests, and build scripts as the starting
point for the rebuild, then trim the surfaces this project does not ship:
Electron desktop packaging, the marketing website, and the ESP32 firmware
tree. CI keeps the web build, e2e, lockfile, release, and Docker workflows.

Rename the package and CLI entry points to hermes-ui, drop the prepare hook so
npm ci no longer triggers a build, and drop the removed tsconfig and nodemon
references. Rewrite AGENTS.md, ARCHITECTURE.md, DEVELOPMENT.md, and the READMEs
so the documented package map and hard rules match this repository.

reference_code/ is ignored: it is a read-only snapshot, not an input to the build.
2026-10-16 10:34:35 +08:00

8.1 KiB
Raw Blame History

Phase 006 — Arco 迁移:聊天页

项 值
目标 聊天页全量换 Arco,功能与旧版完全对等
前置 Phase 005 通过(docs/ui-baseline.md 已定稿)
产出 聊天全流程手工验收清单全绿
参考 docs/03-施工蓝图.md §8.1

0. 为什么聊天页优先

它是全站唯一的 Socket.IO 流式实时页,含审批弹窗、工具轨迹、思考块、Markdown 渲染、虚拟滚动、附件上传。最难的坑在这里踩完,后面 20 个 CRUD 页就是纯体力活。


1. 硬性能规则:消息气泡内禁用 Arco 组件

流式增量渲染时,MessageItem.vue 每次 token 到达都 re-render。Arco 组件被包进消息气泡会引入不必要的 diff 开销。

✅ 允许:a-button(输入区工具条、审批按钮)
✅ 允许:a-modal(审批弹窗,在气泡外层)
❌ 禁止:a-tag / a-avatar / a-progress 等包在气泡内的装饰性组件
❌ 禁止:气泡内任何有内部状态的组件

气泡内用原生 HTML + 项目自己的 SCSS。这条已写进 docs/ui-baseline.md。


2. 改动落点清单

packages/client/src/views/hermes/ChatView.vue
packages/client/src/components/hermes/chat/ChatPanel.vue
packages/client/src/components/hermes/chat/ChatInput.vue
packages/client/src/components/hermes/chat/MessageList.vue
packages/client/src/components/hermes/chat/MessageItem.vue
packages/client/src/components/hermes/chat/VirtualMessageList.vue
packages/client/src/components/hermes/chat/SessionSearchModal.vue
packages/client/src/components/hermes/chat/HistoryMessageList.vue
packages/client/src/components/hermes/chat/FilesPanel.vue
packages/client/src/components/hermes/chat/DrawerPanel.vue
packages/client/src/components/hermes/chat/MarkdownRenderer.vue
packages/client/src/components/hermes/chat/OutlinePanel.vue
packages/client/src/components/hermes/chat/TerminalPanel.vue
packages/client/src/components/hermes/chat/ConversationMonitorPane.vue
packages/client/src/stores/hermes/chat.ts
packages/client/src/stores/hermes/files.ts
packages/client/src/api/hermes/chat.ts

3. 功能任务 1 — 布局与容器

原组件 新组件 备注
NLayout / NLayoutSider a-layout / a-layout-sider 三栏:会话列表 / 聊天区 / 侧面板
NCard a-card 会话项卡片
NScrollbar a-scrollbar 注意:虚拟滚动与 a-scrollbar 冲突,见 §5
NEmpty a-empty 空态
NPopover a-popover 会话项操作菜单
NTooltip a-tooltip
NDrawer a-drawer 侧面板
NCollapse a-collapse 工具轨迹折叠

4. 功能任务 2 — 输入区

原组件 新组件
NInput a-textarea
NButton a-button
NSelect(模型) a-select
NDropdown a-dropdown
NPopconfirm a-popconfirm
NProgress(录音) a-progress
NIcon Arco Icon

ChatInput.vue 里有 useKeyboard / 高度自适应(chat-input-height.test.ts 覆盖),不要重构这块逻辑,只换渲染层。


5. 功能任务 3 — 消息列表(最高风险区)

5.1 虚拟滚动

VirtualMessageList.vue 用 vue-virtual-scroller。

a-scrollbar 与 vue-virtual-scroller 不能同时管滚动容器。二选一:

  • 保留 vue-virtual-scroller 的滚动容器,去掉 a-scrollbar 包裹
  • 或放弃虚拟滚动改 a-table-virtual(不推荐,会破坏消息气泡的自适应高度)

结论:保留 vue-virtual-scroller,去掉 a-scrollbar。在 docs/arco-mapping.md 记下这条例外。

5.2 流式渲染

MessageItem.vue 的 re-render 频率高。确保:

  • 气泡内无 Arco 组件(§1)
  • token 拼接走局部 ref,不触发整列表 diff
  • Markdown 渲染有防抖(保留旧版逻辑)

5.3 审批与澄清弹窗

时序敏感:必须打断流式。这是 a-modal 与流式状态的竞争点。

  • 审批请求到达 → 流式暂停 → 弹窗
  • 用户选择 → 恢复流式
  • 断线重连时若有未处理的审批,要能恢复

MessageItem.vue 里的审批 UI 改成 a-modal 或内联 a-card + a-button(内联更好:不打断视觉连续性)。内联是首选。

5.4 工具轨迹

a-collapse + a-timeline。折叠状态要记住(重渲染不丢)。

5.5 Markdown / 代码 / 图表

MarkdownRenderer.vue 保持不变(不涉及 naive)。相关测试:

markdown-rendering.test.ts
markdown-rendering-mermaid-import-timeout.test.ts
markdown-special-mentions.test.ts
highlight-safety.test.ts
highlight-helper.test.ts

这些不改,跑绿即可。


6. 功能任务 4 — 会话列表

SessionListItem.vue / SessionSearchModal.vue:

原组件 新组件
NCard a-card
NInput(搜索) a-input
NEmpty a-empty
NTag(标签) a-tag
NDropdown a-dropdown
NBadge a-badge
NPopconfirm(删除确认) a-popconfirm

7. 功能任务 5 — 全局 hook 替换

全量 grep 替换,只用 Phase 005 §5 建的适配层:

cd packages/client/src
grep -rln "useMessage\|useDialog\|useNotification" components/hermes/chat/ stores/hermes/chat.ts

改成:

// 旧
const message = useMessage()          // naive hook

// 新
import { useMessage } from '@/composables/useMessage'
const message = useMessage()          // 我们的 composable,签名一致

签名一致,所以正文代码几乎不用改,只改 import 来源。这是 Phase 005 §5 的设计目的。


8. 功能任务 6 — 补测试

新建 tests/client/message-item-arko.test.ts:

  • 消息气泡内不含 Arco 组件标签(防止 §1 规则被破坏)
  • 审批弹窗状态机正确
  • 流式追加消息后 DOM 结构不抖动

9. 验收(手工逐条走,不许跳)

9.1 命令

cd /Users/jack/source/MyProject/js/hermes-ui

grep -c "from 'naive-ui'" packages/client/src/components/hermes/chat/*.vue \
                         packages/client/src/views/hermes/ChatView.vue \
                         packages/client/src/stores/hermes/chat.ts
# 期望:全部 0

npm run build
npm run test
npm run test:e2e

9.2 手工清单

流式与渲染

  • 发消息 → 流式逐字输出 → 完成态
  • 工具调用轨迹展开 / 收起
  • 思考块(thinking)正常显示 / 隐藏
  • 长消息滚动流畅(虚拟滚动没坏)
  • 滚动到底部自动跟随

交互

  • 审批请求弹出 → 选择 → 流式继续
  • 澄清请求 → 回答 → 继续
  • 中断(stop)按钮生效
  • 重新生成 / 分支切换
  • 会话切换时状态正确重置

内容

  • Markdown 标题 / 列表 / 表格
  • 代码块高亮 + 复制按钮
  • KaTeX 公式
  • Mermaid 图(注意导入超时)
  • @提及高亮

多标签与断线

  • 两个标签页开同一会话(chat-session-multitab.spec.ts)
  • 断网 → 重连 → 流式恢复(chat-run-reconnect.test.ts)
  • 移动端窄屏布局(chat-message-mobile-layout.test.ts)

其他

  • 附件上传 + 引用
  • 文件面板(FilesPanel)正常
  • 大纲面板(OutlinePanel)正常
  • 终端面板(TerminalPanel)正常
  • 会话搜索正常
  • 明暗双主题各过一遍

10. 风险

# 风险 缓解
P6-1 a-scrollbar 与虚拟滚动打架 → 白屏或列表不动 §5.1 已定方案,去掉 a-scrollbar
P6-2 流式性能退化(气泡内 Arco 组件) §1 硬规则 + message-item-arko.test.ts 卡住
P6-3 审批弹窗与流式状态竞争 → 卡死 §5.3 优先内联方案,减少 modal 竞争
P6-4 useMessage 等 hook 改 import 时漏改某个文件 §7 的 grep 逐个确认,编译会兜底
P6-5 顺手重构了 chat store 逻辑 硬规则:只换渲染层,不动逻辑。逻辑改动属 Phase 007+
P6-6 测试大量 fail 干扰判断 先确认 npm run test 的 fail 是否本来就存在(Phase 004 基线),只处理新增

11. 完成标志

进入 Phase 007(其余 20 视图批量迁移)。