# 处方 — 让 Claude Design 用上**真实 TVU 组件**：可行性已实测确证，缺的是三件配套（不是能力）

- 日期：2026-08-28 · **入口 = owner 拍板**：「要不要让 Claude Design 也用上真实 TVU 组件？」→ **需要**
- 目标 sha：**`7c7199ed`**（DS 真源 `master`；本 session 内它已漂 2 步，DS 侧执行时按当时 HEAD 重核行号）
- 执行方：**DS 侧 session 在真源执行。lab 只出处方（§8 只读边界），⛔ 未改 DS 任何字节**
- 🔴 **本处方推翻 DS 现行的一条设计意图** —— bundle README 与 `CLAUDE_DESIGN_SETUP.md` 现在逐字写着「不复用 TVU Vue 运行时」「不提供 React 组件（`.jsx`/`.d.ts`）」。owner 已拍，那两条要改（§3.C）
- 关联：[`proposals/2026-08-28-webcomponents-types-entry.md`](./2026-08-28-webcomponents-types-entry.md)（**互补，非替代**，见 §5）
- 验证物：[`probes/claude-design-ce-smoke/`](../probes/claude-design-ce-smoke/)
- **执行状态**：🟢 **已由 DS 落地，三件配套全中** —— 亲验 pin **`7fcca917`**（2026-09-01，`lab:N93`），逐条对位 §3：
- **执行状态重取**：@`d07be8e9`（2026-09-11 §30 全量重取，量具 `metrics/proposal-execution-status-refresh.mjs`）—— 锚 4/5 仍成立；第 5 个 `assets/tvu-web-components.js` 是 **bundle 内目标路径**、⛔ 不是仓内路径 ⇒ 量具报的 missing 是假阳 ⇒ **结论不变**。
  - **A（bundle 加 CE 运行时）**：`scripts/export-claude-design-bundle.mjs:103` 逐字 `const CE_RUNTIME = ['dist-wc/tvu-web-components.umd.cjs', 'assets/tvu-web-components.js']` ⇒ UMD 单文件已进 bundle（与 §1.1「为什么是 UMD 不是 ES」的实测结论一致）
  - **B（加 `reference/17`）**：`:82` 逐字 `['docs/internal/web-components-usage.md', '17-web-components-usage.md']`，真源文件 `docs/internal/web-components-usage.md` 存在 ⇒ 且走的是 §4.1 要求的「`reference/` 编号是闸把守的共享命名空间」那条路（映射式，⛔ 不是随手加号）
  - **C（改掉三处旧声明）**：`:294` 逐字「**做可交互预览 / 原型（渲染真实组件）**：`reference/16`（上游 gate）→ SKILL.md → **`reference/17`**…运行时 = `assets/tvu-web-components.js`，`<script src>` 引入即自动注册，不用写 JS」⇒ 「不复用 TVU Vue 运行时」那条立场已翻转
  - ⚠️ §4.2（上传体积上限）与 §4.3（沙箱能否加载 bundle 内相对路径 JS）本就登记为「lab 实测不了」，~~**本轮仍未验**~~ ⇒ 🔴 **2026-09-15 订正，两条下场不同、⛔ 别合读**：**§4.2 已由 lab 实测答掉**（`DesignSync.write_files` 直传，3,157,175 B 写入成功 ⇒ ⛔ 不超限；读数与三条边界见 §4.2）· **§4.3 仍未验、仍不可达**（预览沙箱的 CSP / 资源解析是产品界面行为，工具契约里零字段）⇒ ⛔ 仍不因本行判 🟢 而视为已闭合

---

## 0. 一句话

🟢 **技术上已经成立，而且比预想的近得多。**

- **可行性不是推断，是实测**：纯 HTML + **一个 UMD 文件** + 一份 token CSS，
  在 `file://` 下渲染出了真实 TVU 组件，**含最难的 light-DOM 组件**（§1）。
- **bundle 里缺的不是「知识」** —— 组件目录 / props 语义 / 组合契约 / 视觉规格 / token / 644 图标**全都已经在包里**，
  连「生成代码（Vue）」的引导路径都写好了（§2）。**缺的只有运行时那一个文件**，加上教它用 CE 的说明。
- ⇒ 真正的工作量在 **三件配套**（§3）与 **三个坑**（§4），⛔ 不在「能不能做到」。

---

## 1. 可行性：已实测确证（⛔ 不是推断）

**探针**：[`probes/claude-design-ce-smoke/`](../probes/claude-design-ce-smoke/)，只给 Claude Design 预览环境
所能提供的条件 —— 一个 HTML + 若干静态文件，**无构建、无 npm、无 node**。

**跑法**：DS 的 playwright + chromium，`file://` 协议（比 http 更严格）。⛔ 未在 DS 上跑 build，
两个输入文件是从 DS 的现有产物**只读复制**的。

| # | 判据 | 实测 |
|:-:|---|---|
| 1 | UMD 全局对象挂上 | ✅ `typeof window.TvuWebComponents === "object"` |
| 2 | CE 进了 `customElements` | ✅ **5/5** 全部注册 |
| 3 | 首个 `<tvu-button>` shadowRoot **真有内容** | ✅ `shadowHtmlLen = 3015`（⛔ 防「注册了但渲染空」的假绿） |
| 4 | `:root` 取得到 token | ✅ `--brand = #2fb54e` |
| — | 浏览器侧错误 | ✅ **0**（console error / pageerror / requestfailed 三路都挂了监听） |
| — | 外部独立复核 | ✅ 不依赖页面脚本另跑一遍，五项一致；`buttonBox = 26x21`（真有盒子） |

**截图亲验**（⛔ 数字通过 ≠ 视觉正确）：Button 四档尺寸递增、`filling`/`ghost`/`rimless` 三种视觉可辨、
品牌绿与红配色正确、Badge / PillStatus / Progress 正常。

> 🟢 **最有说服力的一格是 Pagination** —— 它是 `shadowRoot: false` 的 **light-DOM 组件**，
> 样式不走 shadow DOM、靠 `register.ts` 里的 `injectLightDomStyles()` 注入 `document.head`。
> 它在纯 HTML 环境下**完整渲染**（21-30 of 120 / 10-Page 下拉 / 页码 / Go to 输入框）
> ⇒ 说明那条注入机制**不依赖任何打包器或框架运行时**。

### 1.1 为什么是 UMD 不是 ES（实测，`dist-wc` @ `7c7199ed`）

| | ES `tvu-web-components.js` | **UMD `.umd.cjs`** |
|---|:-:|:-:|
| 文件数 | **29**（主文件 + **28 个动态 `import()` 分片**） | **1** |
| 全部体积 | 3 496 582 B | **3 133 692 B** |
| 图标 | 懒加载分片 | **已内联**（抽查 `arrow-dropdown` 命中） |
| 外部裸包 `import` | **0**（Vue 已打进去） | **0** |
| 浏览器 `<script src>` | ❌ 需 module + 28 个分片运行时可达 | ✅ 挂 `window.TvuWebComponents` |

⇒ **UMD**：单 entry、图标内联、**不依赖运行时 fetch 分片**（沙箱里动态 import 极可能取不到）。
且 `register.ts` 末行自动调 `registerTvuElements()` ⇒ **加载即注册，预览页一行 JS 都不用写**。

---

## 2. 现状盘点：bundle 里**已经有**什么（⛔ 别重复造）

实读 `scripts/export-claude-design-bundle.mjs`（30 707 B）：

| 已有 | 内容 |
|---|---|
| `SKILL.md` | 隐性规则浓缩（真源 `docs/CLAUDE_DESIGN_RULES.md`） |
| `reference/01`–`16` | 组件目录 · **组件语义/props** · mockup 规范 · 设计流程 · TVU 业务 · 工作原则 · 安装用法 · PROJECT_GOAL · 代码约定 · 组件审查 · 生成避坑 · 图标命名 · 上游 gate |
| `reference/composition.json` | **37 canonical 组件**的组合契约（`contains`/`contained_by`/`do_not_hand_compose`/`built_in_features`），机器可读 |
| `reference/components-spec/<组件>.md` | 视觉规格 —— 脚本头注释逐字「目的是让 **Claude Design 预览**能用上」 |
| `tokens/` | `variables.css`（305 token）+ `tokens.dtcg.json` + `figma-variables.json` |
| `assets/icons/sprite.svg` | 644 真实矢量，`symbol id` = canonical registry 名 |

🔴 **而且 README 里已经有一条「生成代码（Vue）」引导路径**：
`reference/16`（上游 gate）→ `SKILL.md` → `08`（安装）→ `02`（canonical 组件 + props）
→ `composition.json` → `tokens` → `icon-keys-*`

⇒ **知识面基本齐了。** 缺的是：① 运行时那一个文件 ② 教它 **CE 标签**怎么写（现有 `02` 讲的是 Vue 组件名/props，不是 `<tvu-button>` 这套标签 + attribute）③ 把「别用真实组件」的旧规则改掉。

---

## 3. 处方：三件配套

### A. bundle 加 CE 运行时（1 个文件）

- 从 `dist-wc/tvu-web-components.umd.cjs` 复制进包，**改名为 `.js`**（内容一字不动）
  - ⚠️ **改名的理由是推断、未在沙箱实测**：`.cjs` 在 `<script src>` 下取决于服务器 MIME，易被判成非 JS 而拒绝执行。改名零成本，先规避
- 落位建议：`assets/tvu-web-components.js`（与 `assets/icons/sprite.svg` 同级，语义一致）
- ⛔ **别放 ES 版** —— 28 个动态分片（§1.1）

### B. 加一份 `reference/17-web-components-usage.md`（教 CE 用法）

**⛔ 别手写，从真源生成**：`src/web-components/components.config.ts` 已带齐 `tag` / `name` /
`PropConfig.tsType` / `VModelConfig` / `EventConfig` —— 与 `.d.ts` 处方**同一个真源、同一个 generator**
（`scripts/generate-react-bindings.mjs`）。⇒ **零新真源**，多一个产物而已。

> ### 🔴 3.B.1 执行顺序是硬的：**先做 `.d.ts` 处方，再做本处方**，⛔ 别并行
>
> 两份处方的改动文件求交集，命中 **`scripts/generate-react-bindings.mjs`** —— 两边都要让它多写一个产物：
>
> | 处方 | 要 generator 多产什么 |
> |---|---|
> | [`.d.ts` 处方](./2026-08-28-webcomponents-types-entry.md) §3.4 | 随包类型声明（`dist-wc` 的 `.d.ts`） |
> | **本处方** §3.B | `reference/17-web-components-usage.md` |
>
> ⛔ **别开两个 session 并行做** —— 同一个文件两边改，必冲突。
> ✅ **先 A 后 B** 还有一个额外好处：A 会把「从 `components.config.ts` 渲染出 CE 标签 + attribute」
> 这段逻辑写进 `react-binding-templates.mjs`（A 的 JSX 增广正需要 tag → props 映射），
> **B 直接复用那段，只是换个输出格式**（`.d.ts` → markdown）。反过来做会写两遍。
>
> ⚠️ 另：DS 仓同期有**落地线 / 并行线**两条工作线在跑（`STATUS.md` 顶部摘要逐字「并行线连续第六轮落地……
> 起手就开 worktree 第二次证明是对的」）⇒ **执行本处方的 session 起手就开 worktree**，
> ⛔ 别只靠 push 前 `git fetch` 事后补救。

内容应含：

1. **37 个 CE 标签名** ← `config.tag`
2. **每个 tag 的 attribute 与取值** ← `PropConfig.name` + `tsType`。
   ⚠️ **必须写明 camelCase → kebab-case 的映射**（`fixedWidth` → `fixed-width`），
   否则 Claude Design 会照 Vue prop 名写 attribute，静默失效
3. **一段可直接抄的最小骨架**：
   ```html
   <link rel="stylesheet" href="./tokens/variables.css">
   <script src="./assets/tvu-web-components.js"></script>
   <tvu-button size="M" fill="filling">Save</tvu-button>
   ```
4. **⛔ 反模式**：别用 `<button class="tvu-…">` 自己拼；别硬编码颜色/间距（token 纪律已在 `SKILL.md`）
5. 指回 `composition.json`（哪些能力组件自带、别手拼）

### C. 改掉三处「别用真实组件」的旧声明（owner 已推翻）

| 文件 | 现在逐字写着 | 要改成 |
|---|---|---|
| `export-claude-design-bundle.mjs:269`（写进 bundle README） | 「**不提供 React 组件（`.jsx`/`.d.ts`）**……组件真源 = 连接的 codebase 里的 `src/canonical/*.vue`」 | 保留「不提供 React 翻译副本」的理由（仍成立），但**加上**「提供 CE 运行时 + `reference/17`，预览请用真实 `<tvu-*>` 标签」 |
| 同文件「按场景用」段 | 只有「画设计稿 / mockup」与「生成代码（Vue）」两条路径 | **加第三条「做可交互预览 / 原型」**：`16` → `SKILL.md` → **`17`（CE 用法）** → `composition.json` → `tokens` |
| `docs/CLAUDE_DESIGN_SETUP.md` §2「有意偏离」 | 「Vue 实现源码也不进包：Claude Design 生成设计稿/HTML 原型、**不复用 TVU Vue 运行时**」 | 🔴 **这条被 owner 推翻，必须改** —— 改为「**不进 Vue 源码，但提供编译好的 CE 运行时**」，并说明为什么是 UMD（§1.1） |

> ⚠️ **C 不是文书工作**：那三条声明是**写给 Claude Design 读的**（bundle README 会被它当输入编译）。
> 不改 = 一边给运行时、一边告诉它别用 —— 规则自相矛盾时它会照旧自画。

---

## 4. 三个坑

### 4.1 🔴 `reference/` 编号是**闸把守的共享命名空间**，⛔ 不能随手加号

`export-claude-design-bundle.mjs:53-56` 头注释逐字：

> `NN-` 编号是**跨本表 + 跨远端策展的共享命名空间**……占用真源 =
> `docs/internal/claude-design-reference-registry.json`，由 `pnpm audit:reference-numbering`
> 机械闸把守（pre-commit + pr-checks）。**改本表编号必须同步改 registry**，加新号还必须先
> DesignSync `list_files` 目标项目核号并把实况写回 registry —— 否则闸红。

**实读 registry @ `7c7199ed`**：远端实况 **15 个编号** ——
`01 02 03 04 05 07 08 09 10 11 12 13 14 15 16`（`06` 退役⛔不得复用；`14`/`15` 归 Sync A 策展）
⇒ **下一个可用号 = `17`** ✅

**执行顺序是硬的**：① 先 `DesignSync list_files` 核远端实况（registry 的 `method` 字段逐字要求
「**活源直读，非报告转述**」）→ ② 写回 registry → ③ 再加 `17` 进 `REFERENCE_DOCS`。
⛔ 顺序反了，`audit:reference-numbering` 在 pre-commit 就红。

### 4.2 ~~⚠️ 上传体积上限**未知**，必须人工实测~~ ⇒ ✅ **2026-09-15 lab 已实测：不超限**

已知 Claude Design **单次上传 ≤ 500 entries**（DS 2026-06-11 实测，644 个图标撑爆过 → 才改的 sprite 方案）。
加 1 个文件**不触 entry 上限**（现约 40+ entries）。
~~🔴 **但「单文件 3.13 MB」是否超限，DS 文档里没有任何记录** ⇒ **未知，lab 无法实测**。~~

🟢 **2026-09-15 实测（owner 当轮逐字批「先小后大」后做的）—— 那个 UMD 单文件传得上去。**

| 臂 | 文件 | 字节 | 结果 |
|:-:|---|--:|---|
| **1（小）** | `dist-wc/tvu-web-components.d.ts` | **26,373** | ✅ 写入成功 |
| **2（大）** | `dist-wc/tvu-web-components.umd.cjs` | **3,157,175** | ✅ **写入成功 ⇒ ⛔ 不超限** |

- **通道**：`DesignSync.write_files` 的 `localPath`（工具直接读盘上传）⇒ 本节原话「**lab 无法实测**」
  与 §4.3 那句「只能由人在界面里试」**在体积这一半上已不成立**。
- **为什么要两臂**：两臂**同目录、同 `localPath` 通道、同为 gitignored 产物** ⇒ 万一臂 2 失败，
  臂 1 的成功能把归因锁死在**体积**上，⛔ 不会被误读成「通道 / 参数没接对」
  （`AGENTS §3.9`：装置故障会伪装成被测对象的行为）。
- **证据⛔ 不取自工具自述**（`AGENTS §4`）：写后 `list_files` **独立列出**两个路径；
  清理后 `get_file` 两个都 **404**、`list_files` 里连探针目录都不剩 ⇒ **owner 项目零残留**。
  ⚠️ 404 单独看也可能是「从来没写成功过」（第二种 0），靠**写后那次 `list_files`** 才闭合。
- ⚠️ **口径**：本节写「3.13 MB」，现取该文件 = **3,157,175 B = 3.01 MiB = 3.16 MB**
  （两个进制口径都给，⛔ 别混）。量级相同 ⇒ 判据不变，⛔ 不改上面那句原文。
- ⛔ **三条边界，⛔ 别外推**：① 只证明**服务端接受并在索引里列出**，
  ⛔ 未证明远端字节与本地逐字节相同（`get_file` 上限 256 KiB，读不回来比对）；
  ② 这是**一次观测** —— 只知道 3.16 MB **在限内**，⛔ 不知道限在哪；③ 与 §4.3 **无关**。
- ⚠️ **另一个对不上的数，并排登记、⛔ 不改原句**：本节写「≤ 500 entries」，
  而 `DesignSync` 工具契约逐字 **max 256 files per call** ⇒ 差 **1.95×**，
  但**主语可能不同**（`entries` vs `files per call`）且相隔 3 个月（`lab:E50` 主语错的预防）。

### 4.3 ⚠️ 沙箱能否加载 bundle 内的相对路径 JS —— **lab 实测不了**

§1 的探针在 `file://` 下跑通，证明的是**充分性的下界**：条件齐备时组件确实能渲染。
Claude Design 预览沙箱的 CSP / 资源解析规则是产品行为，⛔ 只能由人在界面里试。

~~⇒ **4.2 + 4.3 合并成一个 10 分钟的人工动作**（§6 判据 1）。~~
🔴 **2026-09-15 订正：§4.2 已由 lab 用 `DesignSync` 实测答掉 ⇒ 人工动作只剩 §4.3。**

---

## 5. 与 `.d.ts` 处方的关系：**互补，不是替代**

| | 解决什么 |
|---|---|
| 本处方 | 让 Claude Design **知道该生成** `<tvu-button size="M">`，且预览里**真能渲染** |
| [`.d.ts` 处方](./2026-08-28-webcomponents-types-entry.md) | 让那段生成出来的代码，进了开发者的 **React + TS 项目**后**不报错**（`TS2339`/`TS7016`） |

🔴 **两者串起来才是完整链路**：AI 工具产出 `<tvu-button>` → 开发者接手 → TS 项目里需要 JSX 增广。
⇒ 只做本处方，产出的代码到了 TS 项目里照样红；只做 `.d.ts` 处方，AI 压根不知道要生成 CE。

---

## 6. 验收判据（⛔ 文字总结不算数）

| # | 动作 | 通过判据 |
|:-:|---|---|
| 1 | 🔴 **人工**：把加了运行时的 bundle 传进 Claude Design，让它生成一个含 `<tvu-button>` 的预览 | 预览里渲染出**真实 TVU 按钮**（品牌绿 `#2fb54e`、四档尺寸有别），⛔ 不是它自画的方块。~~**这一条覆盖 §4.2 + §4.3 两个未知**~~ ⇒ 🔴 **2026-09-15：§4.2 已由 lab 实测答掉（不超限）⇒ 本条现在只覆盖 §4.3** |
| 2 | `pnpm audit:reference-numbering` | exit 0（证明 `17` 已按 §4.1 顺序正确登记） |
| 3 | `pnpm export:claude-design-bundle` | 产物含 `assets/tvu-web-components.js` 与 `reference/17-*.md`；`MANIFEST.json` 的 `gitCommit` = 当时 HEAD |
| 4 | 拿产出的 bundle 跑一遍 [`probes/claude-design-ce-smoke/`](../probes/claude-design-ce-smoke/) | 四条自检全绿（把探针里那两个文件换成 bundle 内的） |

---

## 7. 覆盖面如实声明

**已实测**：CE 在纯 HTML `file://` 下的可渲染性（4 判据 + 外部复核 + 截图亲验，含 light-DOM 组件）·
ES vs UMD 的分片/体积/自包含性 · bundle 现有产物清单 · reference 编号占用与下一个可用号 · 把守它的闸与顺序要求。

**⛔ 未实测（DS 侧执行时必须先验）**：

| # | 未验项 | 为什么 |
|:-:|---|---|
| 1 | Claude Design 沙箱能否加载 bundle 内相对路径 JS | 产品行为，lab 无法实测（§4.3）。⇒ **§6 判据 1** |
| ~~2~~ | ~~3.13 MB 单文件是否超上传限~~ ⇒ ✅ **2026-09-15 lab 已实测：3,157,175 B 写入成功 ⇒ ⛔ 不超限** | ~~DS 文档只记了 500 entries，没记体积（§4.2）~~ ⇒ 两臂读数、证据链与**三条边界**见 **§4.2** |
| 3 | `.cjs` → `.js` 改名是否必要 | 推断（MIME 风险），改名零成本先做 |
| 4 | Claude Design 会不会**照做** | 给了运行时和规则，它是否真去用 `<tvu-*>` 而非自画，属推断能力问题 ⇒ 只能靠 §6 判据 1 观察 + re-prompt 纠 |
| 5 | 37 个组件在纯 HTML 下**全部**可渲染 | 探针只覆盖 **5 个**（Button/Badge/PillStatus/Progress/Pagination）。选 Pagination 是因为它是 light-DOM 最难的一个；⛔ 但**没有覆盖全部 37 个** |

**本处方不触碰**：npm 分发轨道本身 · Figma 轨道 · `.d.ts` 那件（独立处方）· §29.2 那 8 条结构性盲区。

---

## 8. ⇒ 登记

本处方**不属于第二轮评审**（§30.4 已冻结），是 **owner 2026-08-28 新拍板的落地方向**，
按 §30.5 体例登记为 **N72**，⛔ 不开新格。
