# V4-1827 NDI Discovery & Apply Feedback States — Implementation Plan

> **For agentic workers:** Figma mockup 建帧任务（非代码 TDD）。每个 Task 以 `use_figma` 写操作为主，验证 = `get_screenshot` + `get_metadata` + `pnpm audit:mockup-conformance`。Steps 用 checkbox（`- [ ]`）追踪。

**Goal:** 在已交付的 V4-1827 AFTER 交付里补齐 7 个 NDI 状态补充态（发现源空态 / 扫描中 / 发现失败 / 连接中 / Apply 成功 / Apply 失败 / 超长源名），画进 Config-T 文件 `rJJjWWs51n2iFOlCIC7aYG`。

**Architecture:** 新建独立 Section `V4-1827 · AFTER (states) — NDI discovery & apply feedback`（AFTER Section 正下方），内含 7 个局部态帧，均 clone 自现有 NDI 帧② `7892:790` 改局部；每帧配 cyan caption（延续 ①②③，编号 ④–⑩）；反馈控件全部取库组件 instance，不自画。

**Tech Stack:** Figma MCP `use_figma`（写）/ `get_metadata` / `get_screenshot`；TVU UX Design System library（key `lk-057f6ba0…`）；tvu-design-system repo audit 脚本。

## Global Constraints

- 目标文件 fileKey：`rJJjWWs51n2iFOlCIC7aYG`（Config-T rJJj），工作页 canvas `7452:2`。
- 库 fileKey `YbsPRUVmNdsbN40NNwh1Gn`；library key `lk-057f6ba0f771bfa7f63a6a197502999462c2974f995488b381708ca5faadb7f9f6675e04aa442b3a5ffb31732150a7f5aa8ca3102073ab210edc684022fc6c21`。
- 产品区文案 = **EN 单语**（M33）；注释区（caption）= EN + ZH 双语，字体 EN `Roboto` / ZH `Noto Sans SC`（M23 §字体）。
- 改既有帧内文本若碰 `Helvetica`（不可 load）→ 走 `setRangeFontName` → `Roboto` 绕墙（design-record §7 实证）。
- 颜色全绑 Color Variable（M-COLOR C1）；state color 按 C2（success=brand green / error=red）；grey 分 BG vs text（C3）。
- 反馈类 UI 一律取 Notification/Message set variant，不自画（M52.3）。
- 每个写操作批次后跑 `pnpm audit:mockup-conformance --file rJJjWWs51n2iFOlCIC7aYG --node <id> --non-blocking`（增量场景），findings 进 handoff（M-LIFECYCLE ②）。
- 任何 create/move 前 probe 目标坐标区防叠加（I6）；clone 后 probe brand-color paint 绑定（M42）。

---

## M0 — Element-to-Component Mapping

| Element | 库 component (key) | Variant/用法 | Status | 来源 |
|---|---|---|---|---|
| NDI 帧壳（顶栏 4 icon + radio + divider + 表单） | clone 自 `7892:790`（现有 NDI 帧②） | 整帧 clone 改局部 | ✅ 就地 clone | AFTER §7892:790 |
| Source Name / URL 输入 | `WebUI/Form/Item/Input` + `input box/filled` / `select box/filled` | 继承 clone | ✅ verified | 现帧在用 |
| combobox 展开浮层 | `Drop down List/Select`（catalog §455/476） | 内容态 override（空/加载/错误行） | ✅ verified | 现帧 `7898:924` |
| Apply 按钮 | `Button/dark M`（`12b38c8b68cd67dd3978e0d68712bc646f1e97c1`） | `status=loading`（连接中）/ `status=disable`/`default` | ✅ verified | catalog §88 |
| Apply 结果反馈 | `Message`（node `1408:17153`） | `status=success` / `status=error`，`size=M` | ✅ verified | catalog §204 |
| 超长源名悬浮 | `Tooltips`（catalog §240，key 建时 probe） | 展示截断源名全文 | 🟡 建时 import 验 | catalog §240 |
| loading / error 图标（扫描中/失败行内） | 库 icon（catalog §597 已知 icon / affordance search M35） | spinner / warning icon | 🟡 建时 grep catalog 定 key | catalog §597 |

> ⚠️ Notification/Message `type` 改名进行中（secondary warning→danger / none→default），用前 live probe `componentPropertyDefinitions`（catalog §175）。
> ⚠️ Message 若 Title/Description 在嵌套 SLOT → 改文本走精化协议（catalog §191-197：先 loadFont、遍历拿 fresh ref、set 完即 return、一文本一脚本）。

## M48 — Startup Rule-Coverage Self-Check

| 来源 | 命中项 | 落地计划 |
|---|---|---|
| locked spec | design-record §2 non-goals 列的 7 态 | 本 plan Task 1-7 逐帧覆盖 |
| locked spec | 现有帧 950 宽 mirror + cyan caption ①②③ 范式 | 新帧同宽同 caption 风格（④-⑩） |
| M0 | element mapping 全表 | 上表，画前 import 验 🟡 项 |
| M1 | Top bar — **N/A**：现有 NDI 帧用 `Frame 1984`(4 button) 顶栏非 Top bar 组件，本轮 clone 沿用不新建顶栏 | 沿用 clone，不触 |
| M-COLOR C1/C2/C3 | 反馈色 success=green/error=red、grey BG vs text | 全绑 Variable，机检 colors |
| M-INTEGRITY I5 | clone 源帧最小 delta、不动现有交付 | 只 clone 新建，不改 ①②③ 帧本体 |
| M-INTEGRITY I6 | 新 Section + 7 帧 place 前 probe 防叠加 | Task 1 probe 落点；每帧堆叠前 probe |
| M23（caption）+ M33（产品 vs 注释语言隔离） | caption 双语、产品文案 EN 单语 | 见 Global Constraints |
| M32 / M32.3 | 所有 UI 元素库 instance，search 带 includeLibraryKeys | M0 表；import 验 |
| M42 | clone 后 probe brand-color paint 绑定 | 每 clone 后 probe |
| M52.3（反馈控件同源） | Message success/error 同 semanticClass | M49 §3 inventory（下） |
| state-completeness | 本 7 态即 NDI 数据流 edge/feedback 全枚举（空/中/失败×discovery + 前/中/后×apply + 边界超长名） | 逐态成帧 |
| M-FONT / typography-icon | 新增文本落 textStyle grid、无 Unicode glyph 充数 | 文案用库/Roboto，图标用库 instance |

**M49 §3 Feedback UI Inventory（同源锁定）**：

| feedbackState | semanticClass | component | 一致性规则 |
|---|---|---|---|
| discovery-empty / scanning / discovery-failed | dropdown 内联态 | `Drop down List/Select` 展开浮层内容行 | 同宽（442）同位，居中单/双行，no-action |
| apply-success | status line | `Message status=success size=M` | 表单顶，no-action |
| apply-failed | status line | `Message status=error size=M` | 表单顶，同宽同位 success，no-action |
| connecting | 按钮态 | `Button/dark M status=loading` | 归属 Apply 按钮，非独立反馈面 |

---

## 落点坐标（I6 probe 结论）

- 新 Section：x=**3620**, y=**2100**, 宽 **1120**, 高 ≈ **3400**（建后按内容 resize + I2 复检）。
- probe 结论：`_backup`(x≤3420) / `Undo` instance(右边缘 3620) 均不与 x≥3620,y≥2100 区重叠 ✓。
- 帧内布局（Section-relative，注意 SECTION 子坐标 = section 原点相对，赋值前减原点、落点 probe `absoluteBoundingBox` 验，figma-technical-reference Q15）：
  - 顶部 Section title 文本 y≈20。
  - 7 帧垂直堆叠，每帧 950 宽，帧间 gap 120，各帧上方 36px 处放 cyan caption。
  - 帧④ y≈290；后续每帧 = 前帧 y + 前帧高 + ~150。建时按实际帧高顺序 probe 堆叠。

---

### Task 1: 建 Section + probe 落点

**Files:** Figma `rJJjWWs51n2iFOlCIC7aYG` canvas `7452:2`

- [ ] **Step 1: probe 落点区** — `get_metadata` 已确认（本 plan 落点段）；建前再 probe 一次 x=3620,y=2100,w=1120,h=3400 区与全 sibling section/Undo instance 求交，确认空。
- [ ] **Step 2: 建 Section** `createSection`，name=`V4-1827 · AFTER (states) — NDI discovery & apply feedback`，x=3620 y=2100 w=1120 h=3400，背景对齐现有 AFTER section。
- [ ] **Step 3: Section title 文本** — clone 现有段名范式（cyan `#33A4FD` Medium），文本 `AFTER (states) · NDI discovery & apply feedback / 状态补充态`（双语），Roboto+Noto。
- [ ] **Step 4: 验证** `get_screenshot` 新 section，确认空壳 + title 就位、无叠加。

### Task 2: 帧④ 发现源空态

**Interfaces:** clone 源 = `7892:790`（NDI 帧②，含展开 combobox `7898:924`）

- [ ] **Step 1:** clone `7892:790` → 新帧，name=`AFTER(state) · NDI discovery — empty (no sources)`，放入 Section，y 顶部区（probe 防叠加）。
- [ ] **Step 2:** clone 后 probe brand-color paint 绑定（M42），缺则二步法补绑。
- [ ] **Step 3:** combobox 展开浮层 `Select Item` slot：3 个 Drop down Item → 替换为**单行空态**：居中灰字 `No NDI sources found on the network`（`Color Type/Text/Tips`），下方副行 `Type an ndi:// address manually`（更浅）。删多余 Item。
- [ ] **Step 4:** Source Name / Apply 保持（Apply disabled）。
- [ ] **Step 5: caption** `④ NDI discovery — empty: no sources found on the network; manual ndi:// entry still available. / ④ 发现源空态：网络无 NDI 源，仍可手填 ndi://`（cyan）。
- [ ] **Step 6: 验证** `get_screenshot` + `get_metadata`；跑 conformance `--node <帧id>`。

### Task 3: 帧⑤ 扫描中

- [ ] **Step 1:** clone `7892:790` → `AFTER(state) · NDI discovery — scanning`，堆在帧④下（probe）。
- [ ] **Step 2:** M42 probe。
- [ ] **Step 3:** combobox `Select Item` slot：替换为 loading 行 = 库 spinner/loading icon（grep catalog §597 / M35 affordance search 定 key）+ 文案 `Scanning for NDI sources…`（Tips 色）。
- [ ] **Step 4: caption** `⑤ NDI discovery — scanning the network for NDI sources. / ⑤ 扫描中：正在扫描网络上的 NDI 源。`
- [ ] **Step 5: 验证** 截图 + metadata + conformance。

### Task 4: 帧⑥ 发现失败

- [ ] **Step 1:** clone `7892:790` → `AFTER(state) · NDI discovery — failed`，堆在帧⑤下（probe）。
- [ ] **Step 2:** M42 probe。
- [ ] **Step 3:** combobox `Select Item` slot：替换为 error 行 = 库 warning/error icon（红，绑 `UX/Red/Default`）+ 文案 `Discovery unavailable — check the NDI Discovery server or network`（Tips 色）。
- [ ] **Step 4: caption** `⑥ NDI discovery — failed: discovery server/network unreachable; manual entry still available. / ⑥ 发现失败：Discovery 服务/网络不可达；仍可手填。`
- [ ] **Step 5: 验证** 截图 + metadata + conformance。

### Task 5: 帧⑦ 连接中

- [ ] **Step 1:** clone `7892:790` → `AFTER(state) · NDI apply — connecting`，堆在帧⑥下（probe）。combobox 收起（隐藏展开浮层），表单填妥示例源（Source Name + URL 已选 STUDIO-CAM-01）。
- [ ] **Step 2:** M42 probe。
- [ ] **Step 3:** Apply 按钮 swap → `Button/dark M status=loading`，label `Connecting…`。（若现帧 Apply 是 `Button/Disable` instance，swap component 或改 variant property；probe 现 instance 的 component property 定义。）
- [ ] **Step 4: caption** `⑦ NDI apply — connecting: Apply pressed, establishing the NDI source. / ⑦ 连接中：已点 Apply，正在建立 NDI 源连接。`
- [ ] **Step 5: 验证** 截图 + metadata + conformance。

### Task 6: 帧⑧ Apply 成功 + 帧⑨ Apply 失败

**Interfaces:** `Message` set node `1408:17153`（`status` × `size`）

- [ ] **Step 1:** import `Message` set（`importComponentSetByKeyAsync` 或按 node），live probe `componentPropertyDefinitions` 确认 status 值（success/error 现名）。
- [ ] **Step 2:** clone `7892:790` → `AFTER(state) · NDI apply — success`，堆在帧⑦下（probe），表单填妥示例源，Apply 恢复 default。
- [ ] **Step 3:** 表单顶部插入 `Message status=success size=M`，文本 `NDI source applied to Transmitter 1`（SLOT 改文走精化协议）。
- [ ] **Step 4: caption** `⑧ NDI apply — success: source bound to the Transmitter. / ⑧ Apply 成功：源已绑定到该 Transmitter。`
- [ ] **Step 5:** clone `7892:790` → `AFTER(state) · NDI apply — failed`，堆在帧⑧下（probe）。
- [ ] **Step 6:** 表单顶部插入 `Message status=error size=M`，文本 `Failed to apply — source unreachable. Check the source and try again.`
- [ ] **Step 7: caption** `⑨ NDI apply — failed: source unreachable; re-check and retry. / ⑨ Apply 失败：源不可达；复查后重试。`
- [ ] **Step 8: 验证** 两帧截图 + metadata + conformance。

### Task 7: 帧⑩ 超长源名

- [ ] **Step 1:** clone `7892:790` → `AFTER(state) · NDI — long source name (truncation)`，堆在帧⑨下（probe）。combobox 展开。
- [ ] **Step 2:** M42 probe。
- [ ] **Step 3:** combobox 一个 Option + Source Name/URL 输入内填超长源名（如 `STUDIO-CAM-ARENA-NORTH-UPPER-DECK-CAM-07`），呈现 tail 截断（`…`）。dev-side 截断规范写进 caption/handoff（Figma TEXT 无 native ellipsis，dev 走 CSS）。
- [ ] **Step 4:** 加 `Tooltips`（catalog §240）instance 悬浮显示全名（放帧内近 Option，或帧外连线 M23.9——超长名 tooltip 属 hover 态预览，倾向帧内小 tooltip 靠 Option）。
- [ ] **Step 5: caption** `⑩ Long source name — tail-truncated with tooltip on hover; dev uses CSS ellipsis. / ⑩ 超长源名：尾部截断 + 悬浮显示全名；开发用 CSS 省略。`
- [ ] **Step 6: 验证** 截图 + metadata + conformance。

### Task 8: Section resize + 全 Section 复检

- [ ] **Step 1:** 7 帧建完，按内容 resize Section 高度（hug 到最后一帧底 + padding）。
- [ ] **Step 2:** resize 后跑 `audit:mockup-overlap`（I2）+ integrity 全扫，确认 Section 内 7 帧 + caption 两两不叠、无 overflow。
- [ ] **Step 3:** `get_screenshot` 整 Section，肉眼 + metadata 双核对齐、caption 对位。

### Task 9: 交付层同步

**Files:** Figma 卡 `7840:2`（Journey）/ `7839:2`（UX Delivery）；`docs/specs/2026-07-21-v4-1827-design-record.md`

- [ ] **Step 1: Journey 卡 `7840:2`** — stage 3 `🟡 empty / not-found state not yet designed` → `✅ empty / scanning / failed states designed (frames ④-⑥)`；stage 5 `🔴 Discovery-failure / connecting / apply-result feedback not covered` → `✅ connecting / apply success·failed designed (frames ⑦-⑨)`；stage 4 的 `🟡 cross-Tx overview` 保持（本轮外）。改文走 SLOT 精化协议 / setRangeFontName 绕 Helvetica。
- [ ] **Step 2: UX Delivery 卡 `7839:2`** — Changes 增补一条状态态；Acceptance 增 `☐ NDI discovery empty/scanning/failed states shown; ☐ Apply connecting/success/failed feedback shown; ☐ long source name truncates with tooltip`。
- [ ] **Step 3: design-record** — §2 把 7 态从 non-goals 移出，新增 §状态态设计段（组织 + M49 inventory + 7 帧 node id），§8 交付链接加新 Section id。
- [ ] **Step 4: 验证** 卡截图确认双语行距（M23.14）未破、文本无 Helvetica 残留。

### Task 10: 收尾

- [ ] **Step 1:** 在 tvu-design-system repo 跑总闸 `pnpm audit:mockup-conformance --file rJJjWWs51n2iFOlCIC7aYG --non-blocking`，机器输出贴 handoff。
- [ ] **Step 2:** 写/更新 handoff（沿用现有 handoff 文件，加"Round 3.1 状态态补充"段 + Rule checklist 机检原文）。
- [ ] **Step 3:** 更新 tvu-design-system `docs/STATUS.md`。
- [ ] **Step 4:** 新术语（NDI Discovery / Scanning / discovery-failed 等）回流词库 `vocabulary.md`（shared-vocab-rules）。
- [ ] **Step 5:** 更新 memory `v4-1827-ndi-restart-8.4.md`（Round 3.1 状态态已交付）。
- [ ] **Step 6:** Jira 是否补评论 = 先问 owner（slack-jira-write-confirm），本轮默认不自动发。

---

## Self-Review

- **Spec coverage**：7 态 = Task 2-7 逐帧（④空/⑤扫描/⑥失败/⑦连接中/⑧成功/⑨失败/⑩超长名）✓；交付层同步 Task 9 ✓；4-Tx 总览已拎出本轮（不在 plan）✓。
- **Placeholder scan**：🟡 项（Tooltips key / loading·error icon key）标"建时 grep/import 验"，非 TBD——属 M0 允许的 import-verify 流程。
- **一致性**：反馈控件命名 Message success/error 全 plan 统一；clone 源统一 `7892:790`；坐标统一 Section-relative + probe。
