---
id: v4-1865-wifi-hotspot-mode
title: Hotspot Module 3-mode Architecture
doc_type: handoff
status: in-dev
last_updated: 2026-06-01
sources:
  - { type: jira, ref: "V4-1865", url: "https://tvunetworks.atlassian.net/browse/V4-1865" }
  - { type: jira, ref: "FB-7775", url: "https://tvunetworks.atlassian.net/browse/FB-7775" }
figma:
  file: rJJjWWs51n2iFOlCIC7aYG
  nodes:
    - { id: "6601:2", label: "Config-T page — V4-1865 (v2 architecture)", url: "https://www.figma.com/design/rJJjWWs51n2iFOlCIC7aYG/Config-T---Local-UI---v7.7---8.0--20240220?node-id=6601-2" }
---

# V4-1865 Mockup v2 — Hotspot Module 3-mode Architecture

**Date**: 2026-06-01
**Author**: Nancy Zeng
**Status**: Draft v2 — awaits walkthrough
**Supersedes**: [`2026-05-29-v4-1865-mockup-handoff.md`](./2026-05-29-v4-1865-mockup-handoff.md) (v1 — architectural misread)
**Jira**: [V4-1865](https://tvunetworks.atlassian.net/browse/V4-1865) · High · Joshua Tucker · parent [V4-1676](https://tvunetworks.atlassian.net/browse/V4-1676)
**Customer ticket**: [FB-7775](https://tvunetworks.atlassian.net/browse/FB-7775)

---

## 1. Why v2 (what was wrong with v1)

v1 误读了硬件架构。用户纠正后核心 fact：

| v1 假设 (❌) | v2 实际 (✅) |
|---|---|
| TM1100 有 2 个内置 WiFi 模块 | TM1100 只有 1 个 Onboard WiFi + 1 个 Hotspot 模块 |
| 需求 = "复用第二个内置模块" | 需求 = "让 Hotspot 模块可切到 WiFi 模式" |
| 多 WLAN 来自内置 dual-module | 多 WLAN = Hotspot-as-WiFi + USB 扩展 |
| Hotspot OFF = 字段 dim/disabled | Hotspot 非 active mode = 字段 **隐藏**（文件既有规则） |

v1 的 IA option A（复用 pagination）方向**仍然正确**（pagination 模型适合多 WLAN），但语义解释完全换了：分页器现在表达"WiFi 连接数"而不是"内置模块数"。

---

## 2. v2 Architecture

### 核心：Hotspot 模块 3-mode 操作

**Hotspot section 顶部 Mode 选择器**（替换原来的 ON/OFF toggle）：

| Mode | 行为 |
|---|---|
| `Disable` | 模块完全关闭，不广播、不当 WLAN 客户端 |
| `Hotspot` | 广播 SSID（当前默认行为） |
| `WiFi` | 作为第二 WLAN 客户端，配置走 WiFi section |

### 2-section coordination · 单一真源 + 被动反映

- **Hotspot section** 的 `Mode` 字段 = state 唯一真源
- **WiFi section** 被动反映：
  - Mode ≠ WiFi → 单卡，pagination `< 1 / 1 >` 或隐藏
  - Mode = WiFi → 分页器 `< 1 / 2 >` ⇄ `< 2 / 2 >`，Page 1 = Onboard WiFi，Page 2 = formerly-Hotspot 模块
- 未来 USB WiFi 扩展接入 → pagination 自然 scale 到 `< 1 / 3 >`... 不需要新组件

### Mode 选择器 = Dropdown（不是 segment tab）

- Legacy lib 无现成 segment-control 组件
- 同段 Router/Bandwidth 已用 dropdown，视觉一致
- 权衡：当前态可见，3 选项展开需 1 click — 可接受
- Fallback：若 walkthrough 要求"3 选项始终可见"，退到垂直 radio group

---

## 3. Deliverables（Figma file `rJJjWWs51n2iFOlCIC7aYG`）

[Page `< V4-1865 > Enable Reuse of TM1100 Pack Second Internal WiFi Module 20260529` (6601:2)](https://www.figma.com/design/rJJjWWs51n2iFOlCIC7aYG/Config-T---Local-UI---v7.7---8.0--20240220?node-id=6601-2) — page 名因建立日期保留 2026-05-29，未改名避免破坏既有引用。

### Frame 清单（v2 共 6 mockup + 1 spec card）

| Frame | Node | 描述 |
|---|---|---|
| **F0** | `6602:1949` | Baseline (current Network page reference) |
| **F1** | `6602:2230` | WiFi section · `< 1 / 2 >` viewing Module 1 (Mode = WiFi case) |
| **F2** | `6602:2792` | WiFi section · `< 2 / 2 >` viewing Module 2 (Mode = WiFi case) |
| **F3** | `6626:287` | Hotspot section · Mode = Disable (params hidden, per file rule) |
| **F4** | `6626:569` | Hotspot section · Mode = Hotspot (current default, all params shown) |
| **F5** | `6626:851` | Hotspot section · Mode = WiFi (Mode selector + redirect note) |
| **F6** | `6629:1125` | M23 UX Delivery Spec (bilingual, 9 sections) |

### v1 frames 已删除

`6602:2511` (F2 scanning, deferred to spec) · `6603:287` (F4 dim — wrong design) · `6614:1203` (M23 v1 — outdated) · 对应 captions 也已删除。

---

## 4. Mode 切换状态机（spec）

| From → To | 行为 |
|---|---|
| Hotspot → Disable | 优雅断开 connected clients |
| Hotspot → WiFi | 停止广播 → 显示 WiFi section redirect 提示 |
| WiFi → Hotspot | 断开 Module 2 的 WLAN 连接 → 恢复广播 |
| WiFi → Disable | 断开 WLAN，模块完全关闭 |
| Disable → Hotspot | 用最后一次 config 启动广播 |
| Disable → WiFi | 跳到 WiFi section page 2 配置入口 |

每次切换 toast 提示（"Hotspot disabled. N device(s) disconnected." 文案待 PM 拍板）。

---

## 5. Open Questions（待 PM/RD/customer 拍板）

⚠️ 见 F6 M23 卡 §7 完整列表：

| # | 问题 | 找谁 |
|---|---|---|
| 1 | Module 1 + Module 2 WLAN bonding 行为？聚合带宽 vs 主备 | Danner Dai (RPS V164 patch) |
| 2 | 同一 SSID 在 Module 1+2 → UI 允许还是 block | PM |
| 3 | USB WiFi 适配器兼容性 list | Product team |
| 4 | LCD 端是否要联动设计？ | Joshua Tucker / Trevor Yao |
| 5 | 升级后 Mode 默认值（应为 Hotspot，保留现有行为） | RD |
| 6 | Mode 切换 toast 文案 | PM |

---

## 6. Dev handoff checklist（M23 卡 §8）

- [ ] Backend `mode` enum API: `{ Disable, Hotspot, WiFi }`
- [ ] `wlanModuleCount` 上报：1 / 2 / N
- [ ] Per-WLAN-module API：getStatus / connect / disconnect / scan
- [ ] `Hotspot.broadcast(true/false)` 与 Mode 字段联动，验收 = 附近手机扫不到 SSID
- [ ] Mode 切换断开逻辑
- [ ] USB hot-plug 触发 pagination update event
- [ ] Mode 字段持久化（pack config，survives reboot）

---

## 7. Process Notes & Learnings（给下次 session / 自己）

### v1 → v2 反思

| # | 反思点 | 回流候选 |
|---|---|---|
| 1 | **硬件架构假设错** — 凭 Jira description 文字推断双模块，没问"当前实际硬件配置" | mockup 起手协议 Pre-Phase 0 Step A Intake Gate 加 "Hardware/system architecture clarification" step |
| 2 | **Hide vs dim 是 product-level convention** — 默认套用通用 UX 实践（disabled=dim）违反了 file 既有规则 | mockup-conventions.md 加 "Read file convention on disabled/hidden states first" |
| 3 | **2-section coordination** 是干净的 single source of truth + passive reflection 设计模式 | 候选收录为 design-process.md pattern |
| 4 | **v1 → v2 删除成本低** | mockup file 内容易迭代，v1 不必追求 perfect。下次起手少惧怕 sweep |

### MCP / API gotchas（沿用 v1）
- `get_metadata` 不带 nodeId 只返 1 page（实际 62 page）— 用 `figma.root.children` 直接 enum
- `get_metadata` hidden 节点 screenshot 1×1 — 用 `get_design_context` 强渲染
- `query()` selector 不接受名字含 `/` — 用 `findAll`
- 字体 Helvetica Neue / PingFang SC 本地未装 — Inter Regular 替代（视觉接近）

---

## 8. Next-session Pickup

| 项 | 状态 |
|---|---|
| Walkthrough with Nancy / PM / Joshua | ⏳ |
| 回 Jira V4-1865 评论：贴 Figma link + 6 个待 confirm 问题 | ⏳ |
| 找 Danner 同步 V164 patch 实际行为 | ⏳ |
| LCD 端是否联动 | ⏳ |
| Dev handoff to Bonnie Zhou | walkthrough 通过后 |
| 若 walkthrough feedback 要求"3 选项始终可见" | 切到 fallback 方案（垂直 radio group），需另做一版 |

---

## 9. Library Compliance Refactor (post-walkthrough, same-day)

Walkthrough 后用户报两处 M32 (Library-First) 违例 → 当天修：

| # | 违例 | 根因 | 修复 |
|---|---|---|---|
| 1 | F5 redirect note 用 Unicode `→` | path-of-least-resistance — 写字符 vs grep 库 + createInstance | 重建 horizontal auto-layout：`icon/Drive/Arrow Up` (`50:4752`) library instance + 去 `→` 的 text；icon path 染 brand green，bg rectangle opacity=0 避覆盖 |
| 2 | Mode 选择器用 dropdown 不是 radio | M22 设计意图失败 — 用户原心愿"识别当前 mode" 被翻译成"dropdown 1 行可见"，但 dropdown 隐藏其他 N-1 选项反而弱化识别 | F3/F4/F5 拆 dropdown 行 → 垂直 radio group：outer horizontal AL（`Mode:` label 左 + 右侧 vertical stack 3 row）；每 row = `WebUI/Form/Radio/Selected`(`3:305`)/`Unselect`(`3:308`) instance + mode name + description；当前 mode 单独 Selected 变体 |
| 3 | M23 §7 `⚠` / §8 `□` Unicode 符号 | 同 #1 | 统一改 `•` bullet；Tag=Warning library instance 判定为 over-design 不加 — section heading 已用 brand green + 标题文字传达优先级 |

### Library Primitive Discovery（v3 起手强制做）

Legacy `Config T (Local UI) V7.7&v8.0` 库现已确认存在以下 primitive（之前 v2 我 miss 了）：

| 用途 | 组件 | NodeID |
|---|---|---|
| Radio dot · selected | `WebUI/Form/Radio/Selected` | `3:305` |
| Radio dot · unselected | `WebUI/Form/Radio/Unselect` | `3:308` |
| Arrow up | `icon/Drive/Arrow Up` | `50:4752` |
| Arrow down | `icon/Drive/Arrow Down` | `50:4758` |
| Warning tag/badge | `Tag=Warning, Status=Enable` | `59:7769` |
| Checkbox · checked | `WebUI/Form/Checkbox/Checked` | `3:310` |
| Checkbox · empty/unchecked | **缺失** —— 库无现成 | — |

### 反思 + 回流候选

1. **任何 in-product 装饰/方向/状态符号 → grep 库优先于 Unicode 字符** — mockup-conventions.md 候选 M32 sub-clause
2. **互斥选择控件量化原则** — N ≤ 4 default = radio/segment；N ≥ 5 default = dropdown。design-process.md 候选新 heuristic
3. **Library primitive 探查为 Phase 0 标配** — v2 我只 grep `segment/tab` 没全面扫 Radio/Checkbox/Tag primitive，下次 Phase 0 起手扫一遍主流 form primitive 库存

---

## 10. v4 · M23 canonical + 8-step Jira flow 合规重构

Walkthrough 2 后用户抓 5 个严重违例 — 全部修复：

| # | 违例 | 修复 |
|---|---|---|
| 1 | F5 redirect 用 `icon/Drive/Arrow Up` (语义错 — accordion 箭头 ≠ "section above" 指针) | 删 icon，改 `*` 文字前缀，沿用 baseline TM3-1408 的 `*` hint 范式 |
| 2 | redirect 染品牌绿 (品牌色应留给 active state) | 改 tips 灰 `#A6ADB8` |
| 3 | radio name 列 FIXED 100 没生效，description 列错位 | 9 个 name text node 强制 `resize(100, h)` → desc 列起点 x 完全对齐 |
| 4 | PRD/UX 卡脱离 mockup + 没用 M23 canonical 规则 | 整体重构（见下） |
| 5 | **完全跳过 design-process.md 8 步流程 ② (Jira component / M23.8) + ⑧ (规则回流)** | 加 Jira instance + M23.8 hyperlink + 本次反思真源化（Phase 3） |

### 重构后 v4 layout

```
y=-1100  [Jira requirement widget (V4-1865 hyperlink)]    [PRD card (M23 canonical)]
y=0      [F0]   [F1]   [F2]    [WiFi UX delivery card]
y=1360   [F3]   [F4]   [F5]    [Hotspot UX delivery card]
```

| 元素 | NodeID | M23 canonical 合规点 |
|---|---|---|
| Jira widget | `6646:1324` | M23.8 hyperlink to V4-1865 verified by `getRangeHyperlink` |
| PRD card | `6648:1116` | navy + 4px cyan 左 stroke + Layout A-逐行双语 + Noto Sans SC ZH @0.45 + 字号-2px |
| WiFi UX 卡 | `6649:1116` | 同上 · 4 段 (Why / Changes / Interaction / Acceptance) |
| Hotspot UX 卡 | `6650:1116` | 同上 · 5 段 (Why / Changes / Mode transitions / Acceptance / Open questions 含 warning 色) |

### 关键反思（待回流真源）

| # | 反思 | 候选回流去处 |
|---|---|---|
| 1 | **跳 design-process.md 8 步流程是 process gap** — 我从 v1 起根本没读 §10 完整 8 步 | mockup 起手协议 Pre-Phase 0 加 "必读 design-process.md 8 步" + 8 步 self-check 表 |
| 2 | **M23 canonical 起手没读全文凭印象做** — 7 点规则只对了 2 点 | mockup-conventions.md M23 加 "起手 read-all 强制" 备注 |
| 3 | **"品牌色" 与 "M23 accent 色" 不能混用** — brand green ≠ M23 cyan | mockup-conventions.md M23 加 "色彩归属表"：品牌绿 = active state / cyan = M23 annotation accent |
| 4 | **deliverable 与 mockup 的空间关系是 layout 一等约束** — 卡的位置必须紧邻它描述的 mockup | mockup-conventions.md M23 加 "Spatial proximity rule" |

### v4 后续 · 3 条流程连线（按 M23 canonical §"箭头画法" + §"条件标签"）

| # | 路径 | NodeID | Label |
|---|---|---|---|
| 1 | F3 → F4 (Row 2 水平) | `6654:1116` / `6654:1119` | "Pick Hotspot · 选 Hotspot" |
| 2 | F4 → F5 (Row 2 水平) | `6654:1120` / `6654:1123` | "Pick WiFi · 选 WiFi" |
| 3 | **F5 → F2 (跨行 ↑)** | `6654:1124` / `6654:1127` | "Mode = WiFi unlocks Module 2 · Mode=WiFi 解锁 WiFi 段 Module 2" |

合规点：VECTOR 手动 vectorPaths 画箭头 · stroke `#33A4FD` 2px · 条件标签用 GROUP（Rect bottom / Text top）· 双语 EN + Noto Sans SC ZH @opacity 0.45。

**踩坑实证**：第一版箭头 Y 用了 frame 中线 (2360)，实际 Mode group `absoluteBoundingBox` 拿到的 y=1679（高很多）。修法：永远用 `absoluteBoundingBox` probe 真实位置而不是猜。

