Phase 007: migrate every remaining view from naive-ui to Arco Design Vue

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
This commit is contained in:
2026-10-18 22:57:25 +08:00
parent c3eeea4187
commit 836d0228d2
115 changed files with 4067 additions and 3891 deletions
+4 -4
View File
@@ -259,14 +259,14 @@ a {
}
// Prevent iOS Safari auto-zoom on input focus by ensuring font-size is at least 16px
// Arco 的 a-input 渲染原生 <input>,已被上面的元素选择器覆盖;
// 需要额外兜住的是 a-textarea(渲染 textarea,字体继承自容器)与 Arco 包装层。
@media (max-width: 768px) {
input,
textarea,
select,
.n-input,
.n-input__input-element,
.n-input__textarea-el,
.n-input__textarea-element {
.arco-input,
.arco-textarea {
font-size: 16px !important;
}
}
-176
View File
@@ -1,176 +0,0 @@
import type { GlobalThemeOverrides } from 'naive-ui'
export const lightThemeOverrides: GlobalThemeOverrides = {
common: {
primaryColor: '#333333',
primaryColorHover: '#1a1a1a',
primaryColorPressed: '#000000',
primaryColorSuppl: '#333333',
bodyColor: '#fafafa',
cardColor: '#ffffff',
modalColor: '#ffffff',
popoverColor: '#ffffff',
tableColor: '#ffffff',
inputColor: '#ffffff',
actionColor: '#f0f0f0',
textColorBase: '#1a1a1a',
textColor1: '#1a1a1a',
textColor2: '#666666',
textColor3: '#999999',
dividerColor: '#e0e0e0',
borderColor: '#e0e0e0',
hoverColor: 'rgba(0, 0, 0, 0.04)',
borderRadius: '8px',
borderRadiusSmall: '6px',
fontSize: '14px',
fontSizeMedium: '14px',
heightMedium: '36px',
fontFamily: 'Inter, system-ui, -apple-system, sans-serif',
fontFamilyMono: 'JetBrains Mono, Fira Code, Consolas, monospace',
},
Layout: {
color: '#fafafa',
siderColor: '#f5f5f5',
headerColor: '#fafafa',
},
Menu: {
itemTextColorActive: '#1a1a1a',
itemTextColorActiveHover: '#1a1a1a',
itemTextColorChildActive: '#1a1a1a',
itemIconColorActive: '#1a1a1a',
itemIconColorActiveHover: '#000000',
itemColorActive: 'rgba(0, 0, 0, 0.06)',
itemColorActiveHover: 'rgba(0, 0, 0, 0.1)',
arrowColorActive: '#1a1a1a',
},
Button: {
textColorPrimary: '#ffffff',
colorPrimary: '#333333',
colorHoverPrimary: '#1a1a1a',
colorPressedPrimary: '#000000',
},
Input: {
color: '#ffffff',
colorFocus: '#ffffff',
border: '1px solid #e0e0e0',
borderHover: '1px solid #999999',
borderFocus: '1px solid #333333',
borderDisabled: '1px solid #ebebeb',
groupLabelBorder: '1px solid #e0e0e0',
placeholderColor: '#999999',
caretColor: '#1a1a1a',
},
InternalSelection: {
border: '1px solid #e0e0e0',
borderHover: '1px solid #999999',
borderActive: '1px solid #333333',
borderFocus: '1px solid #333333',
},
Card: {
color: '#ffffff',
borderColor: '#e0e0e0',
},
Modal: {
color: '#ffffff',
},
Tag: {
borderRadius: '6px',
},
}
export const darkThemeOverrides: GlobalThemeOverrides = {
common: {
primaryColor: '#e0e0e0',
primaryColorHover: '#f5f5f5',
primaryColorPressed: '#ffffff',
primaryColorSuppl: '#e0e0e0',
bodyColor: '#1a1a1a',
cardColor: '#2a2a2a',
modalColor: '#2a2a2a',
popoverColor: '#2a2a2a',
tableColor: '#2a2a2a',
inputColor: '#2a2a2a',
actionColor: '#252525',
textColorBase: '#e0e0e0',
textColor1: '#e0e0e0',
textColor2: '#a0a0a0',
textColor3: '#666666',
dividerColor: '#3a3a3a',
borderColor: '#3a3a3a',
hoverColor: 'rgba(255, 255, 255, 0.06)',
borderRadius: '8px',
borderRadiusSmall: '6px',
fontSize: '14px',
fontSizeMedium: '14px',
heightMedium: '36px',
fontFamily: 'Inter, system-ui, -apple-system, sans-serif',
fontFamilyMono: 'JetBrains Mono, Fira Code, Consolas, monospace',
},
Layout: {
color: '#1a1a1a',
siderColor: '#202020',
headerColor: '#1a1a1a',
},
Menu: {
itemTextColorActive: '#e0e0e0',
itemTextColorActiveHover: '#e0e0e0',
itemTextColorChildActive: '#e0e0e0',
itemIconColorActive: '#e0e0e0',
itemIconColorActiveHover: '#ffffff',
itemColorActive: 'rgba(255, 255, 255, 0.08)',
itemColorActiveHover: 'rgba(255, 255, 255, 0.12)',
arrowColorActive: '#e0e0e0',
},
Button: {
textColorPrimary: '#1a1a1a',
colorPrimary: '#e0e0e0',
colorHoverPrimary: '#f5f5f5',
colorPressedPrimary: '#ffffff',
},
Input: {
color: '#2a2a2a',
colorFocus: '#2a2a2a',
border: '1px solid #555555',
borderHover: '1px solid #777777',
borderFocus: '1px solid #e0e0e0',
borderDisabled: '1px solid #3a3a3a',
groupLabelBorder: '1px solid #555555',
placeholderColor: '#666666',
caretColor: '#e0e0e0',
},
InternalSelection: {
border: '1px solid #555555',
borderHover: '1px solid #777777',
borderActive: '1px solid #e0e0e0',
borderFocus: '1px solid #e0e0e0',
},
Card: {
color: '#2a2a2a',
borderColor: '#3a3a3a',
},
Modal: {
color: '#2a2a2a',
},
Tag: {
borderRadius: '6px',
},
Switch: {
railColor: '#3a3a3a',
railColorActive: '#66bb6a',
loadingColor: '#e0e0e0',
opacityDisabled: 0.4,
},
}
export function getThemeOverrides(isDark: boolean, isComic?: boolean): GlobalThemeOverrides {
const base = isDark ? darkThemeOverrides : lightThemeOverrides
if (!isComic) return base
const comicFont = "'Comic Neue', 'ZCOOL KuaiLe', 'Zen Maru Gothic', 'Gaegu', cursive, sans-serif"
return {
...base,
common: {
...base.common!,
fontFamily: comicFont,
},
}
}