Files
7418andClaude Opus 4.6 5d7850a180 refactor: complete UI governance — lint rules, icon/color unification, component splits
Design pattern & component consistency governance across 120 files:

ESLint governance (eslint.config.mjs):
- Enforce ui/ components over native HTML controls (warn)
- Ban Lucide imports (error), require Phosphor via ui/icon.tsx (warn)
- Component file size limit 500 lines (warn, skip blanks+comments)
- Patterns layer isolation (error: no hooks/lib imports)
- Exclude 资料/ from lint scope

Icon unification (ui/icon.tsx):
- Unified barrel export with 86+ Phosphor icons
- Decoupled types/index.ts from icon library (generic IconComponent type)
- Separated command data (lib/constants/commands.ts) from icons (command-icons.ts)

Color token system (globals.css):
- Added semantic status tokens: success/warning/error/info with muted/foreground/border variants
- Migrated 39 files from raw green/red/orange/amber/blue to semantic tokens
- grep-based lint:colors script covers green/emerald/red/orange/yellow/amber/blue

Component splits (all under 500 lines):
- MessageInput 1659→444: MessageInputParts, SlashCommandPopover, 7 hooks, 2 libs
- ChatView ~850→419: useStreamSubscription, useAssistantTrigger
- ProviderManager 1081→406: provider-presets, PresetConnectDialog, useProviderModels
- AppShell 527→470: useUpdateChecker
- AssistantWorkspaceSection 908→499: WorkspaceTabPanels/ConfirmDialogs/StatusCards
- ChatListPanel 677→484: SessionListItem, ProjectGroupHeader, chat-list-utils

Other:
- Migrated 80 native HTML controls to ui/ components across 38 files
- Fixed 22 hook dependency / set-state-in-effect issues
- Cleaned 7 dead code warnings
- Rebuilt better-sqlite3 for ARM architecture
- Added handover doc: docs/handover/ui-governance.md

Lint status: 0 errors 0 warnings on touched files; typecheck pass; 374/374 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 01:02:28 +08:00

6.5 KiB
Raw Permalink Blame History

设计模式与组件一致性治理

完成时间:2026-03-12 | 涉及 92 个文件 | 净减 ~2,900 行

治理目标

在保留现有视觉语言的前提下,建立统一的组件分层规则、消灭跨层重复模式、拆分超大组件、通过 ESLint 静态约束防止退化。

分层架构

src/components/ui/          → 纯 primitives(Button, Input, Select, Dialog…)
src/components/patterns/    → 复用展示模式(SettingsCard, FieldRow, StatusBanner…),零副作用
src/components/{feature}/   → 组合 hooks + patterns + 业务状态
src/app/                    → 页面装配
src/hooks/                  → 数据获取、状态管理
src/lib/constants/          → 纯数据常量,不引用 UI 层

已完成的治理项

1. ESLint 治理规则 (eslint.config.mjs)

规则 级别 作用域 说明
禁止原生 HTML 控件 warn 业务组件 + app 层 <button/input/select/textarea> → 使用 ui/ 组件
禁止 Lucide 导入 error src/** 统一使用 Phosphor,通过 ui/icon.tsx 入口
禁止直接引用 Phosphor warn 业务组件 + hooks 应从 @/components/ui/icon 导入
组件文件行数上限 500 warn src/components/**(排除 ui/、ai-elements/) skipBlankLines + skipComments
Patterns 层隔离 error src/components/patterns/** 禁止导入 @/hooks/* 和 @/lib/*(允许 @/lib/utils)
外部资料目录排除 — 资料/** 不参与主仓 lint

2. 图标统一 (src/components/ui/icon.tsx)

  • 所有业务组件通过 @/components/ui/icon 统一入口导入 Phosphor 图标
  • 目前导出 86+ 个图标 + ICON_SIZE 常量 + Icon/IconProps 类型
  • types/index.ts 中的 IconComponent 类型与 Phosphor 解耦,使用泛型 SVG 签名
  • 图标与命令数据分离:lib/constants/commands.ts(纯数据)+ lib/constants/command-icons.ts(图标映射),在 hooks/useSlashCommands.ts 中合并

3. 颜色 token 治理 (globals.css + lint:colors)

新增语义 token(light + dark):

--status-success-{muted,foreground,border}   (原 green-*)
--status-warning-{muted,foreground,border}   (原 orange/yellow/amber-*)
--status-error-{muted,foreground,border}     (原 red-*)
--status-info-{muted,foreground,border}      (原 blue-*)
  • Tailwind v4 通过 @theme inline 中的 --color-status-* 变量注册
  • npm run lint:colors 检查 green/emerald/red/orange/yellow/amber/blue-{400-700} 用法
  • 例外行标注 // lint-allow-raw-color(如 diff 语法高亮、终端提示符)

4. Patterns 层 (src/components/patterns/)

组件 用途
SettingsCard 统一的设置卡片容器(title + description + children)
FieldRow 设置项行布局(label + description + 右侧控件)
StatusBanner success/warning/error/info 状态横幅
EmptyState 空状态占位(icon + title + description + action)
CommandList 斜杠命令列表弹窗

5. 超大组件拆分

组件 原始行数 当前行数 抽出的子模块
MessageInput 1,659 444 MessageInputParts, SlashCommandPopover, hooks: useChatCommands, usePopoverState, useSlashCommands, useCliToolsFetch, useCommandBadge, libs: message-input-logic, file-utils
ChatView ~850 419 useStreamSubscription, useAssistantTrigger
ProviderManager 1,081 ~406 provider-presets, PresetConnectDialog, useProviderModels
AppShell 527 470 useUpdateChecker
AssistantWorkspaceSection 908 499 WorkspaceTabPanels, WorkspaceConfirmDialogs, WorkspaceStatusCards, workspace-types
ChatListPanel 677 484 SessionListItem, ProjectGroupHeader, chat-list-utils

6. 原生 HTML 控件迁移

38 个文件中 80 处原生 <button/input/textarea/select> 替换为 ui/Button、ui/Input、ui/Textarea、ui/Select。

7. Hook 依赖与死代码清理

  • 修复 react-hooks/exhaustive-deps 和 react-hooks/set-state-in-effect 共 22 处
  • 清除 unused imports/variables 共 7 处
  • AppShell 中 5 个 set-state-in-effect 错误通过 lazy initializer、derived state、eslint-disable-next-line 组合解决

新增文件清单

组件

  • src/components/patterns/CommandList.tsx
  • src/components/chat/MessageInputParts.tsx
  • src/components/chat/SlashCommandPopover.tsx(重构)
  • src/components/layout/SessionListItem.tsx
  • src/components/layout/ProjectGroupHeader.tsx
  • src/components/layout/chat-list-utils.ts
  • src/components/settings/provider-presets.tsx
  • src/components/settings/PresetConnectDialog.tsx
  • src/components/settings/WorkspaceTabPanels.tsx
  • src/components/settings/WorkspaceConfirmDialogs.tsx
  • src/components/settings/WorkspaceStatusCards.tsx
  • src/components/settings/workspace-types.ts

Hooks

  • src/hooks/useAssistantTrigger.ts
  • src/hooks/useChatCommands.ts
  • src/hooks/useCliToolsFetch.ts
  • src/hooks/useCommandBadge.ts
  • src/hooks/usePopoverState.ts
  • src/hooks/useProviderModels.ts
  • src/hooks/useSlashCommands.ts
  • src/hooks/useStreamSubscription.ts
  • src/hooks/useUpdateChecker.ts

Lib

  • src/lib/constants/commands.ts
  • src/lib/constants/command-icons.ts
  • src/lib/file-utils.ts
  • src/lib/message-input-logic.ts

当前 Lint 状态

  • 治理涉及文件:0 errors, 0 warnings
  • 全仓:0 errors, 80 warnings(均在未涉及的文件中:tests、api routes、lib、ai-elements)
  • Typecheck:通过
  • Unit tests:374 pass, 0 fail

已知遗留(不在本轮治理范围)

  1. ai-elements 层:prompt-input.tsx(1,345L)仍含 30+ thin re-export wrappers;file-tree.tsx 有可访问性 warning
  2. 全仓 80 warnings:主要是 tests 中的死代码、bridge adapters 中的 unused vars、stream-session-manager.ts 中的 unused imports
  3. CodeBlock 两套并存:chat/CodeBlock.tsx(Prism)和 ai-elements/code-block.tsx(Shiki),计划统一为 Shiki
  4. catch { // ignore } 模式:50+ 处静默错误,需逐步加 toast 或 console.warn

开发者须知

  • 新建业务组件时,从 @/components/ui/icon 导入图标,不要直接引 @phosphor-icons/react
  • 颜色使用语义 token(text-status-success-foreground),不用裸色值(text-green-500)
  • 新 pattern 组件放 patterns/,不引 hooks/lib(除 @/lib/utils 的 cn())
  • 组件超过 500 行时考虑拆分
  • 提交前运行 npm run test + npm run lint:colors