The project now has exactly one UI library. Every naive-ui component in
packages/client/src is replaced by its Arco equivalent across 24 views and
roughly 80 components, and naive-ui is dropped from package.json along with
its transitive vueuc / @css-render / vdirs / seemly dependencies.
The migration is rendering-layer only. No store logic, no API contract, and
no /chat-run payload was touched.
Three classes of leftover were invisible to `grep naive-ui` and are now
covered by tests/client/no-naive-ui.test.ts, which asserts all three:
- dead .n-* class names in style blocks
- dead --n-* custom properties
- naive-ui in package.json
Notable behavioural traps handled along the way, recorded in
docs/arco-migration-rules.md:
- a-input-password defaults to masked; passing default-visibility="false"
would have exposed API keys in clear text
- a-upload fires a real XHR unless auto-upload is false
- nested Trigger components fight over the first element child, so the
group-chat settings dropdown was flattened
- Arco renders tooltip default-slot text inline, which was silently
collapsing the chat and group-chat toolbars
74 lines
4.1 KiB
Markdown
74 lines
4.1 KiB
Markdown
---
|
|
date: 2026-10-18
|
|
pr: pending
|
|
feature: Phase 007 - full naive-ui to Arco Design Vue migration across all remaining views
|
|
impact: Rendering-layer only. Every naive-ui component in packages/client/src is replaced by its Arco Design Vue equivalent across 24 views and roughly 80 components, and the naive-ui dependency is removed from package.json and package-lock.json. No chat store logic, no /chat-run payload, and no message rendering path is touched. Three chat-chain files changed as a consequence of that rendering-only sweep, listed below with the specific reason each needed a change.
|
|
---
|
|
|
|
# Phase 007 — full Arco migration
|
|
|
|
Phase 006 moved the chat page. Phase 007 finished the job: `grep -rn "naive-ui\|vueuc\|@css-render" packages/client/src` is now empty, and the project has exactly one UI library.
|
|
|
|
Batches 7a-7g each shipped independently per `docs/phases/phase_007.md`. `docs/arco-migration-rules.md` holds the component mapping table the whole sweep followed.
|
|
|
|
## Why chat-chain files changed
|
|
|
|
Four files under the chat chain were caught by the sweep. None of the changes touch logic.
|
|
|
|
### `chat/ChatPanel.vue`
|
|
|
|
- Three leftover naive CSS custom properties were still referenced: `--n-text-color-3` (twice)
|
|
and `--n-color-hover` (once). These were dead after Phase 006 — the variables no longer
|
|
resolve, so the rules silently did nothing. Rewritten to the project's own tokens
|
|
(`--text-muted`, `--bg-card-hover`).
|
|
- `:native-scrollbar="false"` on the workspace dropdown had no Arco counterpart and was
|
|
dropped.
|
|
|
|
### `group-chat/GroupChatInput.vue`
|
|
|
|
- Eighteen dead naive rules under `.tool-trace-toggle`, `.input-settings-button` and
|
|
`.send-button` targeted `.n-button__content`, `.n-button__icon`, `.n-button__border`,
|
|
`.n-button__state-border`, `.n-button__ripple` and `.n-base-wave`. Arco's `a-button` has
|
|
no content wrapper and none of the ripple internals, so `gap` now lives on the button
|
|
itself and the ripple rules were deleted rather than renamed.
|
|
- The settings button was `NDropdown > NTooltip > NButton` — a three-deep Trigger nest.
|
|
Arco's Trigger components each claim the first element child via `mergeFirstChild`, so
|
|
nested popups fight over it and the menu never opens. Flattened to `a-dropdown > a-button`
|
|
with the description moved to a native `:title`, which is the same approach Phase 006
|
|
already took in `ChatInput.vue`.
|
|
- The attach tooltip carried its label as trailing default-slot text. Arco renders the
|
|
default slot inline, so that text became a zero-width flex item in the toolbar. Moved to
|
|
the `content` prop.
|
|
|
|
### `group-chat/CreateRoomForm.vue`
|
|
|
|
- Straight component swap. `InputLikeInstance = { focus: () => void }` was kept because
|
|
Arco's `a-input` still exposes `focus()` (`es/input/input.js:315`), so the existing
|
|
`nextTick` focus calls still work.
|
|
|
|
### `group-chat/GroupChatPanel.vue`
|
|
|
|
- The room context menu was `NDropdown` in `trigger="manual"` mode driven by `:x` / `:y` /
|
|
`:show` / `@clickoutside`. Arco has no manual trigger, so it became
|
|
`a-trigger trigger="contextMenu"` with `:popup-style` for coordinates, matching the
|
|
pattern already used by `FileContextMenu.vue` and `WorkflowView.vue`. Arco's
|
|
`clickOutsideToClose` defaults to true, which replaces the hand-written clickoutside
|
|
handler.
|
|
- `NPopover`'s `placement="bottom-end"` maps to Arco `position="br"` (bottom-right).
|
|
Arco's `a-popover` has no `width` prop and its `Trigger` sets `inheritAttrs: false`, so a
|
|
`:width` would be dropped silently; the width moved to `:content-style`.
|
|
|
|
## Verification
|
|
|
|
`packages/client/src` has zero naive references by three independent checks — `naive-ui`
|
|
imports, `.n-*` class names in style blocks, and `--n-*` custom properties. The last two
|
|
matter because `grep naive-ui` cannot see them; `tests/client/no-naive-ui.test.ts` now
|
|
guards all three so the patterns cannot come back.
|
|
|
|
## Behavior impact
|
|
|
|
Rendering-layer only. No chat store logic, no `/chat-run` payload, and no message rendering
|
|
path was touched. The group-chat message bubbles already used plain HTML and were left
|
|
untouched, so the "no Arco components inside bubbles" rule still holds — `a-tree`, `a-select`
|
|
and the other components appear only outside the bubble.
|