# API Diff Patterns

本报告基于 [api-diff.md](/Users/nancy/Documents/AICoding/VS_Code/tvu-design-system/docs/internal/api-diff.md) 做模式聚类，只归纳 `🟢 / 🟡 / 🟠` 项，不修改任何组件代码。

- 判定依据：[working-principles.md](/Users/nancy/Documents/AICoding/VS_Code/tvu-design-system/docs/working-principles.md) 原则 0–6
- 已登记并直接视为 `✅` 的项：`translation/` 下已存在的 prop alias、icon alias、documented divergences
- Foundation 跳过事实：`Color`、`Icon`、`Typography`、`Border`、`Effect` 不进入本报告；它们不适用标准 props / variants / events 审计，应走视觉层脚本审计

## 1. 仅代码侧 🟢 模式

| 模式名 | 影响范围（组件列表） | 典型签名 | 代码引用次数 | 测试覆盖 | 默认决策 | 是否需要我决策的边角情况 |
| --- | --- | --- | --- | --- | --- | --- |
| `v-model` 输入契约 | `Input`、`InputNumber`、`CheckBox`、`Radio`、`Switch`、`Select` | `modelValue` + `update:modelValue`，payload 分别为 `string` / `number` / `boolean` / `string \| number` | `modelValue refs=5/8/7/8/7/6` | `tests=2/1/1/2/1/1` | 按原则 6，先归入 Vue 生态强候选；默认保留，后续补组件级登记而不是直接删 | 需要你决定是否把 `Select` 的 `string \| number`、`Switch.live` 这类运行时语义继续放在统一 `v-model` 契约下 |
| `disabled` 运行时禁用开关 | `Button`、`Input`、`InputNumber`、`CheckBox`、`Radio`、`Switch`、`Select`、`Tooltip` | `disabled?: boolean` | `refs=6/7/16/12/15/8/8/3` | `tests=2/2/1/1/2/1/1/1` | 按原则 2 + 6，先视为 Vue / Web 通行命名，默认保留并登记 | `Tooltip.disabled` 是浮层行为禁用，不完全等同表单控件禁用；是否共用同一默认决策需要你确认 |
| `placeholder` 占位文案入口 | `Input`、`Select` | `placeholder?: string` | `refs=6/4` | `tests=2/1` | 按原则 6，先归入 Web 表单约定候选；默认保留，后续登记为运行时文案 API | 是否把它视为“代码侧组合便利”还是“应转成 slot / content token”需要你定 |
| `readonly / error` 局部交互状态布尔 | `Input`、`InputNumber`、`Select` | `readonly?: boolean`；`error?: boolean` | `readonly refs=4/6`；`error refs=5/5` | `readonly tests=1/1`；`error tests=2/1` | 按原则 6，先作为运行时行为候选挂起；不直接批量删除 | `error` 很像 Figma 的 `UX=status` 预览轴；`readonly` 又更偏运行时，不能一刀切，需要你决定是否分流 |
| 默认内容 slot | `Button`、`FormItem`、`CheckBox`、`Radio`、`Tooltip` | `slot:default` | 审计表未提供统一 refs 计数 | 审计表未提供统一 tests 计数 | 按原则 6，默认视为 Vue 组合层能力，不与 Figma `componentPropertyDefinitions` 强行一一对应 | `Tooltip` / `FormItem` 在 Figma 中同时存在 `SLOT` 属性，是否要把代码 slot 视为对它们的正式映射，需要你决定 |
| 关闭类事件 | `Notification`、`PromptMessage` | `event:close`; 另有 `Notification.cancel` / `Notification.confirm` | 审计表未提供 refs 计数 | 审计表未提供 tests 计数 | 按原则 1，事件明显是运行时 API；默认保留，不因 Figma 无事件定义而删除 | `Notification.cancel` / `confirm` 是否属于组件职责边界，还是应该留给组合层容器，需要你决定 |

## 2. 仅 Figma 侧 🟡 模式

| Figma 属性名 | 出现组件 | property type | 默认决策依据（原则 1） | 默认决策 | 是否需要我决策的边角情况 |
| --- | --- | --- | --- | --- | --- |
| `dark theme` | `Input`、`CheckBox`、`Radio`、`Switch`、`Select`、`Tooltip` | `VARIANT`，选项基本为 `on/off` | 单值开关 + 主题预览，优先按设计态理解 | 默认不映射新 prop；若代码确有运行时主题切换，再按组件补充 | `Notification.theme` / `PromptMessage` 已经证明“theme”不总是设计态；是否把这组也提升为运行时，需要你定 |
| `enable` | `Input`、`CheckBox`、`Radio`、`Switch`、`Select` | `VARIANT`，选项为 `on/off` 或 `yes/no` | 单值开关，且代码侧已普遍有 `disabled` | 默认不新增平行 `enable` prop，先视为设计态 / disabled 的设计预览开关 | `enable=yes/no` 与代码 `disabled` 是否完全互斥，需要你确认是否要做正式映射表 |
| `status`（Figma 预览轴） | `Input`、`CheckBox`、`Radio`、`Switch`、`Select` | `VARIANT`，多值；如 `default/normal/Filled`、`off/on/some`、`live/off/on`、`multi select` | 多值且部分值有业务语义，不能批量判设计态 | 默认不直接补代码；先进入待判队列 | `Switch.status=live`、`Select.status=multi select`、`Input.status=Filled` 可能是运行时能力，不适合一刀切，需要你逐类判断 |
| `UX` | `Input`、`Select` | `VARIANT`，`click/default/hover/error/editable` | 明显含 hover / click 预览值，优先按设计态理解 | 默认不映射；后续如 `editable` 被确认有业务语义，再拆单独 prop | `Select.UX=editable` 可能不是纯设计态，是否拆出运行时 API 需要你定 |
| `feature` | `Input`、`Select` | `VARIANT`；`Input=no/yes/text count`，`Select=default/time/date` | 多值且业务语义强，不能直接判设计态 | 默认不批量决策，进入组件级 re-scope | `Select.feature=time/date` 已在组件级映射里拆给 `DateTime`；`Input.feature=text count` 是否需要运行时 API 仍需你定 |
| `Content` / `Notification_content` SLOT | `Input`、`FormItem`、`Notification`、`PromptMessage` | `SLOT` | Figma 内容插槽常是排版预览容器，不等同运行时 prop | 默认不新增裸 prop；优先看代码是否已有 slot / text prop 承接 | `Notification_content` 更像结构插槽，`FormItem.Label` 又带命名含义，是否统一映射策略需要你决定 |
| `Icon` / `Show Icon` / `Show Option` | `CheckBox`、`Radio` | `SLOT` + `BOOLEAN` + `BOOLEAN` | 单值装饰开关，优先按设计态处理 | 默认不额外暴露 prop，除非产品要求替换 glyph 或隐藏 option 文案 | `Show Option` 是否应落成运行时“纯图标模式”API，需要你决定 |

## 3. 命名差异 🟠 模式

| 差异类型 | 组件列表 | 典型对子 | 默认决策 | 是否需要我决策的边角情况 |
| --- | --- | --- | --- | --- |
| Title Case / 空格 / 编号后缀 转 camelCase | `InputNumber`、`FormItem`、`Tooltip` | `property1 ↔ Property 1`、`labelWidth ↔ Label Width`、`content ↔ Content` | 按原则 2，默认保留 Vue 命名，再补登记 | `Tooltip.content ↔ Content` 与 `FormItem.label ↔ Label` 还夹着 `prop ↔ SLOT` 结构差，不宜直接登记为纯命名差异 |
| 枚举值大小写不一致 | `Input`、`Select` | `m ↔ M`、`l ↔ L` | 按原则 2，默认保留代码侧小写值，后续登记 value mapping | `Input` 还有 Figma 独有 `XL`，不只是大小写问题；需要你决定是否扩容代码 size |
| 文本 prop ↔ Figma SLOT | `FormItem`、`Tooltip` | `label ↔ Label SLOT`、`content ↔ Content SLOT` | 默认不直接归为“已对齐”；先保持 `🟠`，等待你决定是 prop 还是 slot 才算正式映射 | 这是 API 形态问题，不只是命名问题，需要你拍板 |

### 未形成重复模式但仍需单独决策的 🟠 项

| 组件 | 典型对子 | 问题性质 | 为什么不能并入通用模式 |
| --- | --- | --- | --- |
| `Badge` | `type ↔ Type` | 代码侧 `type` 实际承载颜色；Figma `Type` 承载形状（`Circle/Rectangle`） | 这不是简单的命名差异，更像同名异义；需要单独决策是改名、拆 prop，还是补新的形状轴 |

## 4. 视觉层硬错误汇总

以下项不属于 `🟢 / 🟡 / 🟠` 三色决策，但已在 [api-diff.md](/Users/nancy/Documents/AICoding/VS_Code/tvu-design-system/docs/internal/api-diff.md) 的“异常”小节中出现，按原则 4 视为硬错误候选。

| 组件 | hardcoded hex count | undefined token count | 备注 |
| --- | --- | --- | --- |
| `Button` | 0 | 0 | 无表外异常 |
| `Badge` | 0 | 0 | 无表外异常 |
| `Input` | 2 | 0 | `#353535`, `#ffffff` |
| `InputNumber` | 0 | 0 | 无表外异常 |
| `FormItem` | 11 | 2 | 未定义 token：`--form-item-root-width`, `--form-item-label-width` |
| `CheckBox` | 0 | 0 | 无表外异常 |
| `Radio` | 0 | 0 | 无表外异常 |
| `Switch` | 0 | 0 | 无表外异常 |
| `Select` | 0 | 0 | 无表外异常 |
| `DateTime` | N/A | N/A | 代码文件缺失，本轮不做视觉层统计 |
| `Notification` | 10 | 0 | 含 `#33ab4f`, `#ec5050` 等组件内硬编码颜色 |
| `PromptMessage` | 9 | 2 | 未定义 token：`--prompt-bg`, `--prompt-text` |
| `Tooltip` | 6 | 0 | 颜色值全部直接写死在组件内 |

## Button 双 API 情况

### 现状

`Button.vue` 当前并存两套 API：

- 旧 API：`variant` / `size` / `disabled` / `loading`
- canonical bridge：`canonicalColor` / `canonicalStyle` / `canonicalRadius` / `canonicalFixedWidth` / `canonicalStatus` / `canonicalIcon` / `canonicalSize`

从实现上看，旧 API 会先映射到 canonical contract：

- `variant -> { color, style }`
- `size -> canonicalSize`
- `disabled/loading -> canonicalStatus/canonicalIcon` 的默认推导

然后再由 canonical contract 回落到旧样式 class。

### 旧 API 与 canonical API 的关系

| 关系维度 | 观察 |
| --- | --- |
| 语义覆盖 | 旧 `variant` 把 Figma 的 `color + style` 压成 9 个组合值；canonical API 把它们拆回显式轴 |
| 状态覆盖 | 旧 API 只显式提供 `disabled/loading`；canonical API 才显式承接 `status` 与 `icon` 轴 |
| 尺寸覆盖 | 旧 `size=xs/s/m/l` 对应 canonical `XS/S/M/L` |
| 未完成项 | Figma 是 8 个 `Button/{theme} {size}` set 聚合；当前 canonical API 仍没有显式 `theme` 轴，set 级维度没有完全显式化 |
| 并存结果 | 当前属于 bridge 态：两套 API 可同时使用，但 source of truth 并不唯一 |

### canonical API 是否已经完成全部 Figma 属性映射

结论：**没有完全完成。**

已覆盖的 Figma 属性轴：

- `icon`
- `style`
- `color`
- `status`
- `radius`
- `fixed width`
- `size`（通过 `canonicalSize` 表达）

尚未完全显式化的部分：

- `dark/light` 这一层 component set 维度没有独立 code prop
- 旧 `variant` 仍在主 API 中生效，意味着 Figma 轴与 legacy 组合值同时存在
- Figma 数据里还存在 `Radius` / `radius` 双写现象，当前 bridge 并未在 API 层消歧

### 可选策略（不代替你决策）

| 选项 | 做法 | 好处 | 代价 / 风险 |
| --- | --- | --- | --- |
| 完成迁移 | 以 canonical API 为准，逐步废弃 `variant/size/disabled/loading` 这套 legacy 入口 | Figma 对齐路径最清晰；后续 audit 最容易自动化 | 破坏面最大，需要迁移调用方和文档，且 `theme` 轴还要补完 |
| 回滚 canonical* | 放弃 bridge，保留旧 API，改在 docs / translation 层说明“代码是开发友好抽象，不逐轴映射 Figma” | 对现有调用方最稳，API 面最小 | Figma-first 目标会后退；每次 audit 都要手工解释压缩逻辑 |
| 维持现状 | 保留双 API，继续在 bridge 态运行 | 短期最少改动，可边用边审 | 长期 source of truth 不唯一；调用方容易混用；审计成本最高 |

## 本报告建议的使用方式

- 先拿本文件决定“哪些模式可以默认批量处理，哪些必须逐组件拍板”
- 再回到 [api-diff.md](/Users/nancy/Documents/AICoding/VS_Code/tvu-design-system/docs/internal/api-diff.md) 只看被这份报告标记为“需要你决策的边角情况”
- `Input`、`Select`、`Button`、`Badge` 仍然是高风险区，因为它们不仅有单条 drift，还有 scope / component-level translation 问题
