Define the P1 component APIs, token mappings, and settings-page pilot scope for parallel implementation.
8.2 KiB
8.2 KiB
YCode 界面基础组件规范(2026-10-18)
本文件是第一轮组件统一的接口契约。后续子代理按此实现,不得各写一套。
总则
- YCode 自有界面只消费
--yc-*;--wb-*只允许出现在恢复模块的兼容层。 - 组件样式只能写
var(--yc-*),组件内部不得写裸 hex、裸px、!important。 - 组件不得带外部 margin;间距由调用方决定。
- 每个组件顶层元素必须接受并合并
className。 - 第一轮不引入第三方 UI 库,不引入 Tailwind,不引入
clsx。 - 组件用显式 variant/size props,禁止调用方拼
wb-button--*类名。
命名
--yc-<component>-<property>[-<state>][-<size>]
示例:
--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
接口:
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
接口:
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
接口:
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语义。
验收
每个组件落地时必须提供:
npm run typecheck通过。- 组件自身的最小回归或渲染 fixture。
- 设置页浅色/深色截图对比,差异必须可解释。
npm run check:tokens不因新增文件失败。- 报告修改文件、映射了哪些旧类名、未迁移的调用点、剩余风险。
在 scripts/verify-ui.mjs 假桥修复前,渲染断言可以由主上下文代跑;组件实现者不得改用其它采集渠道查看 WorkBuddy 原版。