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.
97 lines
4.6 KiB
Markdown
97 lines
4.6 KiB
Markdown
---
|
|
date: 2026-10-17
|
|
pr: pending
|
|
feature: Phase 006 - chat page naive-ui to Arco Design Vue rendering migration
|
|
impact: 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
|
|
|
|
```bash
|
|
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.
|