Files
hermes-ui/docs/chat-chain-changes/2026-10-17-phase006-chat-arco.md
T
toor effc29085c Phase 006: migrate the chat page from naive-ui to Arco Design Vue
Swaps the rendering layer only. Chat store logic, keyboard handling, and the
/chat-run Socket.IO contract are untouched, so a mixed UI cannot hide a chat
regression behind a library swap.

Arco's dropdowns take their items from a #content slot of a-doption children
rather than a droplist prop, and Arco Triggers cannot be nested, so the two
toolbar menus lost their a-tooltip in favour of a native title. Both rules are
recorded in docs/arco-mapping.md, and tests/e2e/chat-input-dropdown.spec.ts plus
tests/e2e/chat-rendering-smoke.spec.ts keep the menus, the streaming markdown
rendering, and the no-Arco-inside-the-bubble red line from regressing silently.
2026-10-17 22:17:32 +08:00

4.6 KiB

date, pr, feature, impact
date pr feature impact
2026-10-17 pending Phase 006 - chat page naive-ui to Arco Design Vue rendering migration No chat-chain behavior change; chat components only swap naive-ui components for Arco equivalents. Chat run flow, chat store logic, keyboard handling, and the /chat-run Socket.IO contract are untouched.

Phase 006 — Chat page Arco migration

Phase: docs/phases/phase_006.md

What Changed

Rendering-layer only, across packages/client/src/components/hermes/chat/:

  • ChatInput.vue — NPopselect reasoning-effort picker and NDropdown settings menu became a-dropdown whose #content slot declares a-doption items; NModal became a-modal (:footer="false" for informational dialogs, custom #footer for confirm dialogs); NInputNumber became a-input-number hide-button. The two menu buttons use a native :title instead of a-tooltip because Arco Triggers cannot be nested. useKeyboard wiring and the textarea height auto-resize logic were not touched.
  • ChatPanel.vue — session context menu moved from NDropdown trigger="manual" to a-dropdown trigger="contextMenu" align-point wrapping .session-items (the three-level export submenu became nested a-dsubmenu / a-doption); 4 dialogs, the new-chat drawer, 5 NSelect, 6 NTooltip, and the NPopconfirm confirmations became Arco equivalents.
  • MarkdownRenderer.vue — the text preview NDrawer/NDrawerContent pair became a single a-drawer, and it is now lazily mounted behind v-if="textPreviewVisible". The component's public interface is unchanged because it is reused by 8 call sites (including Phase 007's GroupMessageItem).
  • MessageItem.vue — the tool-change NDrawer/NDrawerContent became a single a-drawer. It uses :visible + @update:visible rather than v-model:visible so the existing "refuse to close with unsaved changes" guard still runs.
  • MessageList.vue — approval / clarification overlay buttons and inputs.
  • FolderPicker.vue — folder tree context menu moved to a-trigger trigger="contextMenu" align-point with an #content slot; NModal preset="dialog" became a-modal with a custom #footer.
  • SessionListItem.vue, SessionSearchModal.vue, TerminalPanel.vue, FilesPanel.vue — remaining buttons, checkboxes, tooltips, popconfirms, selects, and spinners.

VirtualMessageList.vue already had no NScrollbar and keeps vue-virtual-scroller as the sole scroll owner (recorded as an exception in docs/arco-mapping.md).

stores/hermes/chat.ts, stores/hermes/files.ts, and ChatView.vue needed no changes.

Contract Check

/chat-run event names, field names, field types, and emission timing are unchanged. No file under packages/server/, services/hermes/group-chat/, lib/context-compressor/, or services/hermes/context-engine/ was modified, so no bridge, compressor, or context-engine behavior changed.

The §1 red line (no Arco components inside the message bubble) is enforced by tests/client/message-item-arko.test.ts, which registers the real Arco plugin and asserts that no .arco-* element appears in the rendered bubble for assistant, user, and tool messages, including across streaming re-renders.

Verification

grep -cE "from ['\"]naive-ui['\"]" packages/client/src/components/hermes/chat/*.vue \
                             packages/client/src/views/hermes/ChatView.vue \
                             packages/client/src/stores/hermes/chat.ts
npm run harness:check
npm run test
npm run test:e2e
npm run build

Result: grep 0 across all chat files; 274 test files / 2026 tests passing; 35 e2e tests passing; harness check and build clean.

Follow-up Fix

Arco's a-dropdown has no droplist prop — its menu content must be declared in the #content slot with a-doption / a-dsubmenu children. The first migration pass used :droplist, which Arco silently ignored (unknown props fall through as plain attributes), so all three dropdowns opened an empty popup. Fixed by declaring the options in the #content slot, and locked down by tests/e2e/chat-input-dropdown.spec.ts, which asserts the reasoning effort menu (7 options, selection applied), the input settings menu (2 options, toggle applied), and the session context menu plus its export submenu.

The §9.2 manual checklist rows that need a live run are covered by tests/e2e/chat-rendering-smoke.spec.ts: streamed deltas rendering bold text, inline code, a highlighted code block, KaTeX math, and a Mermaid diagram; the Stop button appearing on run start and clearing on run.completed; the "no Arco inside the bubble" red line; and the session model dialog plus session search opening from the chat UI.