# Phase 6.4 — Input.showCount + Select.multiple/editable runtime additions

> **触发方式**：用户对 executor 说 `请按 docs/internal/_prompts/phase-6.4-runtime-additions.prompt.md 执行`
> **角色**：executor
> **依赖**：
> - Tier 1-A + Step 1-3.5 + BRIDGE-005 全 ship（HEAD = `201895c4`）
> - Spec 真源：[`docs/internal/runtime-additions.md`](../runtime-additions.md) §Input.showCount / §Select.multiple / §Select.editable
> **完成后 STOP**，列改动 + 未解决项给 plan owner 复审

> ⚠️ **scope**：non-breaking 新增 3 个 props（无既有 API 改动）。预估 ~4-6h。
> Input.XL（runtime-additions.md 末段标 "待决"）**不在本 sprint scope**——保留 TODO 状态。

---

## 0. 起手 Pre-flight check

**Pre-flight 判断规则**（同 Sprint 3+ 范式）：

只有当**以下任意一项**为真时才 STOP：

1. 本任务输出路径已 dirty：
   - `src/components/Input/Input.vue` modified
   - `src/canonical/SelectBoxBase.vue` modified
   - `src/canonical/SelectBoxLine.vue` modified
   - `src/canonical/SelectBoxFilled.vue` modified
   - `src/design-system/translation/prop-aliases.json` modified

2. 已 ship 产物被改动（回归风险）：
   - `src/canonical/*.vue` 除 SelectBoxBase/Line/Filled 外被改
   - `figma-sync/audit-translation-completeness.mjs` modified
   - `figma-sync/generate-docs-figma-members.mjs` modified（BRIDGE-005 已 ship）
   - `src/design-system/translation/*.json` 除 prop-aliases.json 外被改

3. 本 sprint 明示不动：
   - `figma-data/raw/` / `figma-data/normalized/` 任何文件 modified（read-only baseline）
   - `src/components/Input/` 外其它 base 文件被改（如 InputNumber.vue 不动）

4. baseline 不绿（见下）

**其它无关 dirty/untracked** 正常继续（如 `.claude/`、`docs/STATUS.md`、`docs/internal/_metrics/`、本 prompt 文件）。

```bash
git status --short
git log --oneline -3
# 顶部应是 201895c4 feat(bridge-005): ...

pnpm test                                  # 105 passed | 1 skipped
pnpm exec vue-tsc --noEmit                 # 0 错
pnpm run audit:translation-completeness    # 9 findings / 0 active / 9 allowlisted
pnpm run prepublishOnly                    # 10 strict gates 全通过
```

任一不绿 → **STOP**。

---

## 1. 任务摘要

按 [`docs/internal/runtime-additions.md`](../runtime-additions.md) §§ 1-3 实现 3 个 runtime addition props：

| Prop | 加在哪 | Figma 来源 | API |
|---|---|---|---|
| **Input.showCount** | `src/components/Input/Input.vue` (base) | `feature=text count` | `showCount?: boolean = false`（+ `maxlength?: number`） |
| **Select.multiple** | `src/canonical/SelectBoxBase.vue` (+ Line/Filled forward) | `status=multi select` | `multiple?: boolean = false` |
| **Select.editable** | `src/canonical/SelectBoxBase.vue` (+ Line/Filled forward) | `UX=editable` | `editable?: boolean = false` |

**Non-breaking 原则**：
- 既有 prop（Select 的 `status='multi select'` 和 `ux='editable'`）继续工作
- 新 boolean prop 是 sugar shortcut；底层 derived state 用 `||` 合并两个来源
  - `isMultiSelect = multiple || status === 'multi select'`
  - `isEditable = editable || ux === 'editable'`

---

## 2. 实施指导

### 2.1 Input.showCount

**Spec** ([runtime-additions.md §Input.showCount](../runtime-additions.md)):
- 来源：Figma `feature=text count`
- 行为：开启后在 input **右下角**显示 `当前字符数 / maxLength`
- 依赖：`maxlength` HTML 标准属性

**实现位置**：`src/components/Input/Input.vue`（base 层；canonical InputBoxLine/Filled 通过 InputBoxBase wrap 此 base，无需改 canonical wrapper）

**改动**：
1. defineProps 加 `showCount?: boolean = false` + `maxlength?: number`（HTML 标准属性，type number 跟 input.maxLength 对齐）
2. Template 改：
   - input 元素 bind `:maxlength="maxlength"` (HTML 接受 number)
   - 当 `showCount && maxlength != null` → wrapper 容器套 input + counter span，counter 显示 `${modelValue.length}/${maxlength}`
3. CSS：
   - wrapper 用 position relative
   - counter span position absolute 右下角（如 `position: absolute; right: var(--sp-s); bottom: var(--sp-xxs);`）
   - counter 字号小 + token color（`--text-tips` 或 `--text-2`）
   - 不破坏既有 `.tvu-input` styling
4. 跑 `pnpm test` 确保 Input.test.ts 9 tests 仍 pass

### 2.2 Select.multiple

**Spec** ([runtime-additions.md §Select.multiple](../runtime-additions.md)):
- 来源：Figma `status=multi select`
- 行为：开启后允许多选；modelValue 类型变 array
- API: `multiple?: boolean = false`

**实现位置**：`src/canonical/SelectBoxBase.vue`

**改动**：
1. defineProps 加 `multiple?: boolean = false`
2. 当前 line 56: `const isMultiSelect = computed(() => props.status === 'multi select')`
   → 改为: `const isMultiSelect = computed(() => props.multiple || props.status === 'multi select')`
3. Line 49: `const listType = computed(() => (props.status === 'multi select' ? 'Multi' : 'Radio'))`
   → 改为: `const listType = computed(() => (isMultiSelect.value ? 'Multi' : 'Radio'))`
4. `SelectBoxLine.vue` + `SelectBoxFilled.vue` 也加 `multiple` prop 透传给 `<SelectBoxBase :multiple="multiple">`（这两个 wrapper 通常字段透传给 SelectBoxBase）
5. 跑 `pnpm test` 验 SelectBox 相关 tests 仍 pass

### 2.3 Select.editable

**Spec** ([runtime-additions.md §Select.editable](../runtime-additions.md)):
- 来源：Figma `UX=editable`
- 行为：combobox 模式，允许输入自定义值
- API: `editable?: boolean = false`

**实现位置**：`src/canonical/SelectBoxBase.vue` + Line + Filled

**改动**：
1. defineProps 加 `editable?: boolean = false`
2. 找内部 derive editable behavior 的位置（grep `ux === 'editable'`），引入 `const isEditable = computed(() => props.editable || props.ux === 'editable')` 并替换所有 `props.ux === 'editable'` 命中为 `isEditable.value`
3. SelectBoxLine + Filled 加 `editable` prop 透传
4. 行为：editable 模式让 combobox / 接受自由输入。**注意**：spec 说 "combobox 模式"，executor 不需要从零实现 combobox UI——如果当前 `ux === 'editable'` 已有视觉/交互逻辑，复用；如**没有**任何 combobox 实现 → 报告 plan owner（标 `incomplete-editable-impl` finding，本 sprint 仅暴露 prop，combobox UI 实现留后续）

### 2.4 prop-aliases.json 登记 3 entries

新增 3 个 scope=runtime-addition entries（按既有 runtime-addition entries 格式）：

```json
{
  "id": "runtime-addition-005",
  "scope": "runtime-addition",
  "component": "Input",
  "components": null,
  "codeName": "showCount",
  "figmaName": "feature=text count",
  "canonicalAxis": null,
  ...
  "status": "runtime-addition",
  "notes": "Phase 6.4 (2026-05-14): Figma feature=text count value 提升为运行时 prop showCount?: boolean = false。开启后渲染 'currentLen/maxLength' counter 在 input 右下角。依赖 maxlength HTML 属性。"
}
```

3 个 entry：
- `Input.showCount` ↔ `feature=text count` (runtime-addition)
- `Select.multiple` ↔ `status=multi select` (runtime-addition)  
- `Select.editable` ↔ `UX=editable` (runtime-addition)

**ID 选择**：grep 现有 runtime-addition-* IDs → 用最大 +1 起编号（应该是 -005 起或更高）

### 2.5 runtime-additions.md update

把 3 个 section 末尾 "TODO: 实现待 Phase 6.4" 改为：

```markdown
- ✅ 已实现于 Phase 6.4 (2026-05-14)，commit <hash>
```

不动 §Input.XL 段（"待决"，本 sprint out of scope）。

### 2.6 Tests

加 / 更新 vitest tests:
- `tests/Input.test.ts` 加 1-2 个 case 测 showCount + maxlength counter rendering
- `tests/Canonical.test.ts` 或新 SelectBox 测试 — 测 multiple / editable 透传 + 行为

若加 test 让 105 → 107 passed 是正常增量。

---

## 3. 任务输出（5 个 deliverable）

### D1: Input.showCount 实现
- `src/components/Input/Input.vue` defineProps + template + CSS counter

### D2: Select.multiple + Select.editable 实现
- `src/canonical/SelectBoxBase.vue` defineProps + isMultiSelect / isEditable computed update
- `src/canonical/SelectBoxLine.vue` defineProps + props 透传
- `src/canonical/SelectBoxFilled.vue` defineProps + props 透传

### D3: prop-aliases.json 3 新 entries (scope=runtime-addition)
- `src/design-system/translation/prop-aliases.json`

### D4: runtime-additions.md 标已实现
- `docs/internal/runtime-additions.md`

### D5: Tests
- 至少 Input.test.ts 加 showCount 测试
- 至少 Canonical.test.ts 或新 SelectBox 测试加 multiple/editable 测试

---

## 4. 执行步骤（按顺序）

1. 起手 §0 pre-flight 全绿
2. Read 4 个目标源文件（Input.vue / SelectBoxBase.vue / SelectBoxLine.vue / SelectBoxFilled.vue）
3. Read runtime-additions.md 全文
4. 实施 D1 → 跑 `pnpm test` Input 相关 tests + 手测 docs site Input 渲染
5. 实施 D2 → 跑 tests + 手测 docs site SelectBox 渲染（multiple / editable 行为）
6. 实施 D3 + D4
7. 实施 D5（加 vitest test）
8. 跑全套验收 §5
9. STOP + 报告

---

## 5. 验收命令

```bash
# D1 — Input.showCount
grep -n "showCount\|maxlength" src/components/Input/Input.vue
# 预期：>=2 命中

# D2 — SelectBox multiple/editable
for f in SelectBoxBase SelectBoxLine SelectBoxFilled; do
  echo "--- $f ---"
  grep -n "multiple\|editable" src/canonical/$f.vue | head -5
done
# 预期：每文件 >=2 命中

# D3 — prop-aliases entries
node -e "
const d = require('./src/design-system/translation/prop-aliases.json');
const new3 = d.entries.filter(e => e.scope === 'runtime-addition' && ['showCount','multiple','editable'].includes(e.codeName));
console.log('runtime-addition Phase 6.4 entries:', new3.length, '/ expected 3');
new3.forEach(e => console.log('  ', e.id, e.component, e.codeName, '↔', e.figmaName));
"
# 预期：3 entries

# D4 — runtime-additions.md 标已实现
grep -c "✅ 已实现于 Phase 6.4" docs/internal/runtime-additions.md
# 预期：>=3

# D5 — tests
pnpm test 2>&1 | grep -E "passed|failed"
# 预期：>= 105 passed (可能升到 107-108)

# 全套 audit + typecheck 0 回归
pnpm exec vue-tsc --noEmit                # 0 错
pnpm run audit:translation-completeness   # 0 active findings
pnpm run prepublishOnly                    # 10 strict gates 全通过
```

**idempotent verify**:
```bash
pnpm run audit:translation-completeness
# 第二次 → "(skipped — no content change)"
```

任一 fail → STOP 报告。

---

## 6. 报告格式（STOP 后必交）

```markdown
## Phase 6.4 完成报告

### 改动文件清单
- [ ] D1 src/components/Input/Input.vue (+X 行)
- [ ] D2 src/canonical/SelectBoxBase.vue / Line.vue / Filled.vue (+Y 行 total)
- [ ] D3 src/design-system/translation/prop-aliases.json (+3 entries)
- [ ] D4 docs/internal/runtime-additions.md (3 段标 ✅)
- [ ] D5 tests/Input.test.ts (+N case) / tests/Canonical.test.ts (+M case)

### 行为验证（手测描述）
- Input.showCount + maxlength: [docs site 渲染 character counter 在右下角 ✓ / fail 详情]
- Select.multiple: [docs site multiple=true 时多选 chip + array modelValue ✓]
- Select.editable: [docs site editable=true 时 combobox 行为 / 或仅 prop 暴露未实现 UI]

### 验收结果
- vitest: [X passed | Y skipped]
- vue-tsc: [0 错 / 报错]
- audit:translation-completeness: [9 findings / 0 active / 9 allowlisted]
- prepublishOnly: [10 strict gates 全通过 / fail]
- idempotent: [audit 2nd run skipped OK]

### 实际耗时
[Xh, 对比预估 4-6h]

### Editable combobox 实现状态
- 当前 `ux === 'editable'` 是否已有 combobox UI 实现？
- 若**没有**: 仅暴露 prop, 不实现 combobox UI（本 sprint scope 限制），列 `incomplete-editable-impl` finding 留 plan owner 决策

### 未解决 / 待 plan owner 决策项
- [其它边界 case]
```

---

## 7. 严格不做的事（executor 边界）

- ❌ Input.XL 扩 size enum（runtime-additions.md 标"待决"，本 sprint out of scope）
- ❌ 改 InputNumber.vue（不在 Phase 6.4 scope）
- ❌ 改 figma-data / mcp-cache / extract pipeline
- ❌ 改既有 prop API（non-breaking 原则，仅新增）
- ❌ 改 audit / generator 逻辑
- ❌ 改 STATUS.md / tracker.md（plan owner wrap-up 时做）
- ❌ commit — plan owner 审完后用户决定
- ❌ 自创 combobox UI 实现（如果 editable UI 不存在，仅暴露 prop + 报 plan owner）

如发现 Select base 文件 `multi select` 字符串有空格 vs 无空格差异 / status 字段 typo 等 inconsistency → **STOP** 报告，不要自决修正。

---

## 8. 完成后 STOP

把报告交给 plan owner，由 plan owner 复审 + 用户决定：

- 是否 commit Phase 6.4
- 是否进 v0.3.0 release wrap-up（v0.3.0 主线 Tier 1-A ✅ + BRIDGE-005 ✅ + Phase 6.4 ✅ 全 done）
- 是否合并 v0.3.0 release（changeset / version bump / tag push / Packages 页 verify）
