# Video Sync · Phase B Step 2 Componentization · Next Session Pickup (2026-05-26)

> 接 [2026-05-26 Phase B 启动 handoff](./2026-05-26-video-sync-cap-confirm-pickup.md)。
>
> **更新（同 session 后段）：Step 1 + Step 2 全部完成 ✅。Session Row 12-variant component set 已发布在 Figma `4559:3148`。下个 session 从 Step 3 (replace M0-M4 row clones with instances) 开始。**

## 1. 本 session 完成 — Step 1 + 完整 Step 2

### Step 1 — Row 架构 inspect ✅
- 确认 row 是裸 FRAME 不是 instance（必须组件化）
- 发现 Inactive 有两个子型（NeverStarted vs Idle）→ 12 variants 而非 8
- 关键架构发现已回流到 [memory/figma-video-sync-source.md](../../../../.claude/projects/-Users-nancy-Documents-AICoding-VS-Code-MicroApps/memory/figma-video-sync-source.md)

### Step 2 ✅ — **Session Row 12-variant component set 已发布**

**Component set ID：`4559:3148`**（位于 Plan A section 内的 Variant Lab `4538:2714` 下）

| state | selected | expanded | h | component ID |
|---|---|---|---|---|
| Syncing | Yes | No | 72 | 4558:2952 |
| Syncing | No | No | 72 | 4558:2970 |
| Inactive-Idle | No | No | 72 | 4558:2984 |
| Inactive-NeverStarted | No | No | 72 | 4558:2993 |
| Syncing | Yes | Yes | 192 | 4558:3025 |
| Syncing | No | Yes | 192 | 4558:3054 |
| Inactive-Idle | Yes | No | 72 | 4558:3071 |
| Inactive-NeverStarted | Yes | No | 72 | 4558:3083 |
| Inactive-Idle | No | Yes | 192 | 4558:3108 |
| Inactive-Idle | Yes | Yes | 192 | 4558:3136 |
| Inactive-NeverStarted | No | Yes | 192 | 4558:3156 |
| Inactive-NeverStarted | Yes | Yes | 192 | 4558:3179 |

Variant property schema：`state` (3 opts) × `selected` (Yes/No) × `expanded` (Yes/No) = 12 全覆盖。

**关键技术细节（下个 session 复用 instance 时要知道）：**
- Selected 视觉 = 绿色 fill `{r:0.043, g:0.169, b:0.075}` 绑 var `3010:103` + 4px LEFT-only stroke INSIDE align (color `{r:0.184, g:0.71, b:0.306}`)
- Unselected 视觉 = empty fill + 1px BOTTOM-only stroke gray（顺带做 divider）
- Arrow icon 在 collapsed = `icon/Arrow/Next` (▶)，expanded = `icon/Arrow/Dropdown` (▼) — variant 内已正确
- remove-control 在 collapsed selected 是 row 末尾子节点；在 expanded selected 是 outer wrapper 末尾子节点
- Inactive-NeverStarted 行无 `route` frame，无 `delta` frame，只有 "Set Stream A and Stream B" CTA text
- Inactive-Idle 行有 `route` 无 `delta`

### ~~Step 2 一半 — 5/12 anchor 变体已建 ⏳~~（已完成，保留作历史） 
- **Component Variant Lab 容器**：`4538:2714`（位于 Plan A section 内，page 坐标 1740,4367）
- 已有 5 个 anchor 变体（直接 clone 自 M0/M1 源 row，已 resize 到 1168 宽）：

| Lab id | 来源 | 变体 |
|---|---|---|
| `4538:2719` | M1 row#1 (`4122:162`) | Syncing × selected × collapsed |
| `4538:2741` | M1 row#2 (`4122:180`) | Syncing × unselected × collapsed |
| `4538:2760` | M1 row#3 (`4122:198`) | Inactive-Idle × unselected × collapsed |
| `4538:2775` | M1 row#5 (`4122:233`) | Inactive-NeverStarted × unselected × collapsed |
| `4538:2785` | M0 expanded (`4139:1626`) | Syncing × selected × expanded（含 Stream A/B picker） |

截图验证：5 个变体视觉全部正确（selected 绿框、Inactive 无 delta、NS 只有 CTA、expanded 含 Stream picker）。

## 2. Phase B Step 2 剩余 7 变体（精确 mutation 指令）

### 命名 convention（preempt combineAsVariants 阶段）
所有变体 COMPONENT name 格式：`state=<X>, selected=<Y>, expanded=<Z>` 其中：
- `state` ∈ {`Syncing`, `Inactive-Idle`, `Inactive-NeverStarted`}
- `selected` ∈ {`Yes`, `No`}
- `expanded` ∈ {`Yes`, `No`}

### 7 个待建变体

**(A) Syncing · Sel=N · Exp=Y** — clone `4538:2785`，操作：
- 移除外层 FRAME 的绿色 stroke (设 `node.strokes = []`)
- 找到子层 `remove-control`（在 header 内），设 `visible = false` 或 delete
- 内部 header (collapsed 部分) 不变；expand-panel 保留

**(B) Inactive-Idle · Sel=Y · Exp=N** — clone `4538:2760`，操作：
- 加绿色 stroke：从 `4538:2719` 复制 strokes 数组（含 SOLID green + variable binding）
- 添加 `remove-control`：从 `4538:2719` 的 header 内 clone `remove-control` 子节点，appendChild 到当前 row 末尾
- 注意：原 row 是 1168 fixed width HORIZONTAL auto-layout，appendChild 加在最右

**(C) Inactive-NeverStarted · Sel=Y · Exp=N** — 同 (B)，源 `4538:2775`

**(D) Inactive-Idle · Sel=N · Exp=Y** — 较复杂：
1. 创建 VERTICAL auto-layout FRAME（W=1168, gap=0），命名为新变体外壳
2. Clone `4538:2760` (collapsed Idle row) 作为 header，appendChild 到 wrapper
3. 把 header 内的 `icon/Arrow/Next` swap 成 `icon/Arrow/Dropdown`（参考 expanded 变体内的 dropdown 实例做 component swap）
4. Clone expand-panel `4139:1632` 内的子结构，appendChild 到 wrapper（Stream A/B picker 完整）
5. wrapper 整体 H=192

**(E) Inactive-Idle · Sel=Y · Exp=Y** — 在 (D) 基础上：加绿色 stroke + 加 remove-control 到 header 内

**(F) Inactive-NeverStarted · Sel=N · Exp=Y** — 同 (D)，源 `4538:2775`

**(G) Inactive-NeverStarted · Sel=Y · Exp=Y** — 同 (E)，源 (F)

### Arrow icon swap 细节
- collapsed → expanded：把子节点 `icon/Arrow/Next` (`4138:981` 等) 替换为 `icon/Arrow/Dropdown`（M0 expanded 内的实例 `4139:1630` 是 dropdown 的参考）
- 实例 swap：找到现有 icon instance node → `node.swapComponent(dropdownMainComponent)` 或直接 clone + replace

## 3. Phase B Step 3 — combineAsVariants（Step 2 完成后做）

完成 12 个 FRAME 变体后：
1. **每个 FRAME 转 COMPONENT**：用 `figma.createComponentFromNode(frame)` 或手动 `figma.createComponent()` + appendChild children
2. 命名按 §2 convention
3. 收集 12 个 component nodes 数组 → `figma.combineAsVariants(componentArray, parentNode)`
4. 设置 component set 的 properties：
   - `state` (VARIANT) — Syncing / Inactive-Idle / Inactive-NeverStarted
   - `selected` (VARIANT) — Yes / No
   - `expanded` (VARIANT) — Yes / No
5. 可选：加文本 component properties（`#N`, slot-A, slot-B, delta value, last-ran timing）便于 instance 复用

## 4. Phase B Step 3+ — 下个 session 起点（component set 已就绪，直接消费）

按原始 handoff Step 4-10 执行，**现在所有 row 操作都用 instance of `4559:3148`**：

1. **Replace M0-M4 existing row clones with component instances**
   - 在 M0 (`4139:1376`)、M1 (`4119:199`)、M4 (`4210:1715`)、Cap-Full (`4528:2768`) 中，定位 row FRAME 节点，删除原 clone，插入 Session Row instance 并设 variant properties
   - Instance 创建：`figma.getNodeByIdAsync("4559:3148")` 后 pick 对应 variant component → `.createInstance()`
   - 注意：text 子节点（Sync session #N、Last ran…、slot-A/B 等）目前是硬编码在每个 variant 内，instance 上要 detach 文本或手改 — 或下次扩展 component 时把这些抽成 component properties (TEXT type)

2. **Build new frames M1-Empty / M5 / M8 / M10** from scratch using instances (per 原 handoff §4.1)

3. **Rework M4 / M6 / M7** 用 instance 替代 raw rows

4. **Delete legacy M2 frame** `4139:988`

5. **Update Design Rules card** `4166:1231` 加 §3.2 的新规则（accordion model / 手动 ▼ toggle / renumber on delete / scroll trigger）

6. **Resize Plan A section** 容纳新 frame 数量

7. **State-label fan-out** per new frame

8. **Screenshot verification + memory回流 + work log**

**建议下个 session 顺序**：先做 1（替换 M0-M4 验证 instance 工作正常）→ 再做 2（新 frame） → 3（rework） → 余下文档与 cleanup。

## 5. 关键 node ID 速查（本 session 验证最新）

| Purpose | Node ID |
|---|---|
| Plan A section | `4119:198` (page 1640,67, w=6373 h=5379) |
| Variant Lab 容器 | `4538:2714` |
| Anchor: Syncing/Sel/Coll | `4538:2719` |
| Anchor: Syncing/Unsel/Coll | `4538:2741` |
| Anchor: Idle/Unsel/Coll | `4538:2760` |
| Anchor: NS/Unsel/Coll | `4538:2775` |
| Anchor: Syncing/Sel/Exp | `4538:2785` |
| M0 expand-panel (clone 源 for D/F) | `4139:1632` |
| M1 row#1 remove-control（参考） | `4507:2662` |
| M0 row#1 dropdown arrow（参考 swap target） | `4139:1630` |
| M1 row#1 next arrow（参考） | `4138:981` |
| 旧 M2 待删 | `4139:988` |
| M4 待简化 | `4210:1715` |
| Cap-Full 待重构 | `4528:2768` |
| Remove dialog 待迁入 | `4528:3222` |

## 6. 估算与风险

- Step 2 剩余 7 variants + combineAsVariants：**5-7 use_figma calls**
- Step 3 整组件化交付（含验证）：**1-2 calls**
- 之后 Step 4-10（frame 重建 + 文档更新）：**15-20 calls + 3-5 doc writes**
- **建议下个 session 单独跑完 Step 2+3（共 6-9 calls），不要混 Step 4+**

主要风险：
1. swap arrow icon 时如果 main component ID 不对会失败 — 先 inspect dropdown 的 main component key
2. expand-panel clone 时其内部 select-box instance 的 main component 引用可能脱链 — clone 整子树通常没事但要验证
3. combineAsVariants 要求 12 个都是 COMPONENT (非 FRAME) — 顺序：先全 frame 完工 → 批转 component → 再 combine

## 7. Memory 已更新

[figma-video-sync-source.md](../../../../.claude/projects/-Users-nancy-Documents-AICoding-VS-Code-MicroApps/memory/figma-video-sync-source.md) 已加入 Row 架构发现段，下个 session 进来不用 rediscover。
