Files
ycode/docs/界面基础组件规范-2026-10-18.md
T
xiaohan.yuan febb28aa6f docs: add base UI component contract
Define the P1 component APIs, token mappings, and settings-page pilot scope for parallel implementation.
2026-10-18 18:36:44 +08:00

252 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# YCode 界面基础组件规范(2026-10-18)
本文件是第一轮组件统一的接口契约。后续子代理按此实现,不得各写一套。
## 总则
- YCode 自有界面只消费 `--yc-*`;`--wb-*` 只允许出现在恢复模块的兼容层。
- 组件样式只能写 `var(--yc-*)`,组件内部不得写裸 hex、裸 `px`、`!important`。
- 组件不得带外部 margin;间距由调用方决定。
- 每个组件顶层元素必须接受并合并 `className`。
- 第一轮不引入第三方 UI 库,不引入 Tailwind,不引入 `clsx`。
- 组件用显式 variant/size props,禁止调用方拼 `wb-button--*` 类名。
## 命名
```text
--yc-<component>-<property>[-<state>][-<size>]
```
示例:
```text
--yc-button-primary-bg
--yc-button-primary-bg-hover
--yc-button-height-md
--yc-input-height-md
--yc-switch-track-width-lg
```
组件 token 定义在 `src/renderer/src/styles/tokens.css` 或独立的 `src/renderer/src/ui/tokens.css`,二者只能有一处。页面作用域不得重定义。
## 需要补的 L0 基础色
现有 `styles/tokens.css` 缺少原版控件的部分透明度阶。第一轮组件需要补齐:
| token | 浅色 | 深色来源 |
| --- | --- | --- |
| `--yc-black-90` | `rgba(0, 0, 0, 0.9)` | `rgba(255, 255, 255, 0.9)` 由组件层重映射 |
| `--yc-black-75` | `rgba(0, 0, 0, 0.75)` | `rgba(255, 255, 255, 0.75)` 由组件层重映射 |
| `--yc-white-90` | `rgba(255, 255, 255, 0.9)` | 同 |
| `--yc-white-75` | `rgba(255, 255, 255, 0.75)` | 同 |
| `--yc-white-30` | `rgba(255, 255, 255, 0.3)` | 同 |
| `--yc-white-20` | `rgba(255, 255, 255, 0.2)` | 同 |
| `--yc-white-10` | `rgba(255, 255, 255, 0.1)` | 同 |
这些值来自当前 `--wb-palette-*` 实际使用值,不是新设计。
## P1 组件
### Button
接口:
```ts
type ButtonVariant = 'primary' | 'secondary' | 'grey' | 'ghost' | 'link' | 'danger'
type ButtonSize = 'sm' | 'md' | 'lg'
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant
size?: ButtonSize
iconOnly?: boolean
loading?: boolean
iconLeft?: React.ReactNode
iconRight?: React.ReactNode
}
```
当前对应的 `wb-button` 类名到 props 的映射:
| 现类名 | 新写法 |
| --- | --- |
| `wb-button wb-button--primary wb-button--medium` | `<Button variant="primary" size="md">` |
| `wb-button wb-button--secondary wb-button--medium` | `<Button variant="secondary" size="md">` |
| `wb-button wb-button--ghost wb-button--medium wb-button--icon-only` | `<Button variant="ghost" size="md" iconOnly>` |
| `wb-button wb-button--small` | `<Button size="sm">` |
几何值来源(当前 `--wb-button-*`):
| 属性 | sm | md | lg |
| --- | ---: | ---: | ---: |
| height | 24px | 32px | 48px |
| padding-x | 8px | 12px | 32px |
| gap | 2px | 4px | 8px |
| font-size | 12px | 14px | 14px |
| line-height | 20px | 22px | 22px |
| radius | 6px | 8px | 12px |
| icon | 12px | 16px | 16px |
状态值来源:
| 状态 | 浅色来源 | 深色来源 |
| --- | --- | --- |
| primary bg | black-90 | white-90 |
| primary bg hover | black-75 | white-75 |
| primary fg | white-100 | black-90 |
| secondary bg | white-100 | `#262626` |
| secondary bg hover | gray-2 `#f7f7f7` | `#2e2e2e` |
| secondary border | `#dedede` | `#3d3d3d` |
| grey bg | gray-4 `#ebebeb` | `#383838` |
| grey bg hover | gray-5 `#e6e6e6` | `#404040` |
| ghost hover | `#0000000f` | `#ffffff14` |
| link fg | `#1470b4` | `#46a8f0` |
| danger bg | red-7 `#e85c5c` | `#af191a` |
| danger hover | red-6 `#ec7474` | `#a31717` |
深色值来自当前 `capability-center-source.css` 与 `subagents/theme.css` 的已验证闭包;实现时先取现有计算值,不得自创。
### IconButton
`IconButton` 是 `Button` 的 `iconOnly` 语法糖,不单独维护尺寸表。
- 必须有 `aria-label` 或 `title`。
- 默认 `size="md"`、`variant="ghost"`。
- 图标容器固定为对应 size 的 height/width。
### Input
接口:
```ts
type InputSize = 'sm' | 'md' | 'lg'
interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
size?: InputSize
invalid?: boolean
filled?: boolean
prefix?: React.ReactNode
suffix?: React.ReactNode
wrapperClassName?: string
}
```
几何值来源:
| 属性 | sm | md | lg |
| --- | ---: | ---: | ---: |
| height | 28px | 32px | 48px |
| padding-x | 8px | 8px | 12px |
| radius | 6px | 8px | 12px |
| font-size | 12px | 14px | 14px |
状态值:
| 状态 | 浅色 | 深色 |
| --- | --- | --- |
| bg | white-100 | `#242424` |
| border | gray-5 `#e6e6e6` | `#454545` |
| border hover/focus | black-30 `#0000004d` | white-30 `#ffffff4d` |
| filled bg | gray-3 `#f2f2f2` | `#242424` |
| disabled bg | gray-2 `#f7f7f7` | `#171717` |
| placeholder | black-30 | white-30 |
### Switch
接口:
```ts
interface SwitchProps {
checked: boolean
onCheckedChange?(checked: boolean): void
disabled?: boolean
readOnly?: boolean
size?: 'sm' | 'md' | 'lg'
'aria-label': string
className?: string
}
```
几何值来源:
| 属性 | sm | md | lg |
| --- | ---: | ---: | ---: |
| track width | 24px | 26px | 34px |
| track height | 14px | 16px | 20px |
| thumb | 10px | 12px | 16px |
| thumb translate | 8px | 8px | 12px |
状态值:
| 状态 | 来源 |
| --- | --- |
| track bg | gray-5 `#e6e6e6` |
| track bg checked | brand `#00c29a` |
| track bg checked hover | brand-7 `#40d1b3` |
| thumb bg | white-100 |
| focus outline | 2px / offset 2px |
| disabled | 保持几何,降低不透明度 |
### Select
第一轮不重写行为,只统一外观壳。
- 复用 `src/renderer/src/components/source-select/SourceSelect.tsx` 与 `RecoveredSelect`。
- 闭合态必须支持 `value`、`placeholder`、`invalid`、`disabled`、`size`、`loading`。
- 几何沿用 `--wb-select-*`:height 32 / radius 8 / font-size 14 / caret 16。
- 设置页不可用行里的“假 Select”也必须走同一视觉壳,禁止再手写 `wb-select`。
### Dialog / ConfirmDialog
第一轮先统一壳,不改业务流程。
- `Dialog`:标题、内容、footer actions、关闭按钮、遮罩。
- `ConfirmDialog`:`title`、`description`、`confirmLabel`、`cancelLabel`、`danger`、`busy`。
- 几何来源:radius 24、max-width 480/640/960、遮罩 `#00000026` 浅色 / `#0006` 深色。
- 已存在 `task-confirm-dialog.tsx` 与 `wb-modal` 结构,优先抽壳而不是重写。
### Popover / Menu
第一轮统一:
- `Popover`:定位、边界收敛、关闭策略、`role`。
- `Menu` / `MenuItem` / `MenuSeparator`:键盘导航、选中态、危险态。
- 几何来源:radius 16、padding 8、shadow 沿用当前 `--wb-popover-shadow`。
### Segmented / Tabs
- `Segmented`:track padding 2、item gap 4、item radius 6、height 32、active weight 600。
- `Tabs` 用于设置导航、能力中心一级导航;选中态只改颜色与字重,不跳尺寸。
## 设置页试点范围
试点只迁移设置页里由 YCode 自己渲染的控件,不改恢复面板的布局规则。
第一批替换点:
| 文件 | 当前 | 目标 |
| --- | --- | --- |
| `GeneralSettingsPanel.tsx` | 本地 `Switch` / `UnavailableSwitch` | `ui/Switch` |
| `GeneralSettingsPanel.tsx` | `UnavailableSelect` 手写 `wb-select` | `ui/Select` 的只读/禁用态 |
| `GeneralSettingsPanel.tsx` | `wb-button` 打开目录/更改/前往系统设置 | `ui/Button` |
| `GeneralSettingsPanel.tsx` | `wb-input` 路径输入 | `ui/Input` |
| `SettingsDialog.tsx` | 本地 `.btn` 工作空间按钮 | `ui/Button` |
| `ModelSettingsPanel.tsx` | 本地 `Button` / `Field` 包装 | `ui/Button` + `ui/Input` |
不允许改动的部分:
- `source-*-recovered.css` 与任何 REA 生成文件。
- `.general-settings-panel__*` 布局类名,迁移后仍保留外层结构。
- 已确认的 1:1 几何、文案、禁用态与 `aria-disabled` 语义。
## 验收
每个组件落地时必须提供:
1. `npm run typecheck` 通过。
2. 组件自身的最小回归或渲染 fixture。
3. 设置页浅色/深色截图对比,差异必须可解释。
4. `npm run check:tokens` 不因新增文件失败。
5. 报告修改文件、映射了哪些旧类名、未迁移的调用点、剩余风险。
在 `scripts/verify-ui.mjs` 假桥修复前,渲染断言可以由主上下文代跑;组件实现者不得改用其它采集渠道查看 WorkBuddy 原版。