L 級新功能、PRD/Design/TDD/ADR 三方協作 + 互審 + M9-6 SDK 雙驗證、總計 ~9000 行文件。
範圍:
- A 階段(MVP、5 人天):KL520 + KL720 自動升級 KDP1 → KDP2
- B 階段(10.5 人天):手動降版面向一般使用者 + KL630 / KL730 擴展
- 合計 15.5 人天、安裝包 +7MB(保守 bundle 策略)
關鍵決策:
- 翻案 R5-Q9(progress.md 第二輪使用者決策「韌體燒錄 flash → B 砍掉」)
- 跨平台用 KneronPLUS Python C API、不用 DFUT.exe
- 多版本目錄結構選 C metadata(firmware/<chip>/{version}/ + CURRENT_VERSION)
- Kneron firmware redistribution 授權與 R5-B4 預置模型同性質、發佈前評估
文件產出:
- PRD v2.2(PRD-v2.md 495 行 + features/feature-firmware-management.md 599 行)
- Design v2.2(firmware-management.md 948 行 + control-panel.md §6a graceful shutdown)
- TDD v2.2(v2/firmware-management.md 823 行 + ADR-001 218 行)
- 8 份 research(含 M9-6 弱驗證 + 強驗證、~3200 行)
- 3 份三方互審報告(PM/Design/Architect cross-review)
M9-6 強驗證重大發現(影響 B 階段):
- KL730 product_id 實際是 0x732(不是 0x0730)
- KL630/KL730 firmware 是 embedded Linux rootfs(不是 .bin、不同代設計)
- KneronPLUS Python 沒 update_kdp_firmware_from_files 公開 API、warrenchen 走 ctypes
- 不影響 A 階段、B 階段 M9-8 需 spike
下一步:派 backend M9-1 起跑(bridge.py handle_firmware_upgrade)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
62 KiB
v2.7 — Kneron Dongle FW 管理(升級 + 版本切換)
本章對應 R5-Q9 翻案(FW 管理面向一般使用者)+ Architect research 30-integration-plan §M9-2/3/4 + 42-manual-downgrade-for-end-users §5。 上層索引:
../design-spec-v2.md版本:v2.2 新增 · 建立日期:2026-05-24 相關:
- 架構參考:
.autoflow/04-architecture/research-kl520-fw-management/30-integration-plan.md、42-manual-downgrade-for-end-users.md- UI 銜接:
v2/settings-update.md(新增分頁進這個檔的結構)、spec/03-wireframes.md §3.3 Devices(FW badge)
1. 定位與職責
「韌體管理」是 Settings 內新增的第 5 個分頁,使用者在此:
- 看到每張 Kneron dongle 當前 FW 版本 + bundled 最新版的對照
- 升級到最新 FW(一鍵動作、KL520 KDP1→KDP2 為主要場景)
- 切換 FW 版本(含降版)——進階使用者操作,多層 safety guard 保護
- 看升級 / 切換進度、失敗復原指引
這個分頁不做的事:
- 不做 device-level inference / model 操作(那是 Workspace 的事)
- 不顯示非 Kneron 裝置
- 不做 OTA / 線上版本更新(所有 FW 從安裝包內 bundle,R5-Q9 決策)
為什麼是 Settings 分頁、不是 Devices 頁主流程:
- 降版屬於高風險操作(理論上可能 brick)、不應放在使用者點擊頻率最高的 Devices 頁
- 升級雖然低風險,但「升級」與「切換」介面共用同一套版本選單較自然
- 對應 architect 42 §1.2 的 trade-off 分析
2. IA 整合:Settings 分頁順序
依 v2/settings-update.md §1 的 4 分頁結構(一般 / 硬體 / 模型 / 進階)擴充為 5 分頁:
| Tab | 順序 | 變更 |
|---|---|---|
| 一般 | 1 | 不變 |
| 硬體 | 2 | 不變 |
| 韌體 | 3 | 本檔新增 |
| 模型 | 4 | 順序後移 |
| 進階 | 5 | 順序後移 |
為什麼放在「硬體」與「模型」之間:
- 概念分組:硬體(裝置本身)→ 韌體(裝置韌體)→ 模型(裝置上跑的軟體資產),由低階到高階
- 「硬體」分頁列出裝置掃描策略,使用者看完掃描設定後,自然會問「裝置韌體呢」
- 進階分頁仍放最後(不變)
2.1 分頁標籤文案
| Key | zh-TW | en |
|---|---|---|
settings.tabs.firmware |
韌體 | Firmware |
用詞警告:分頁名稱不能叫「降版」——對一般使用者過於負面。一律稱「韌體」或「韌體管理」。「版本切換」是進階操作的中性說法。
3. 頁面結構(Wireframe)
3.1 整體佈局(韌體分頁主畫面)
預設狀態(有偵測到 2 張 dongle、其中 KL520 有可升級的版本):
┌─ Settings > 韌體 ───────────────────────────────────────────────────┐
│ │
│ 韌體管理 │
│ 管理已連接 Kneron 裝置的韌體版本。 │
│ │
│ ───────────────────────────────────────────────────────────── │
│ │
│ ┌─ KL520 #1 ─────────────────────────────────────────────────┐ │
│ │ kn_number: │ │
│ │ ● KDP1 (legacy) 0x1A2B3C4D │ │
│ │ 建議升級到 KDP2 v2.2.0 │ │
│ │ │ │
│ │ 當前版本:KDP1 │ │
│ │ 最新版本:KDP2 v2.2.0 (建議) │ │
│ │ │ │
│ │ [ 升級到 KDP2 v2.2.0 ] [ 版本切換 ▾ ] │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ KL720 #1 ─────────────────────────────────────────────────┐ │
│ │ kn_number: │ │
│ │ ● KDP2 v2.2.0 (current) 0x5E6F7890 │ │
│ │ 裝置使用最新韌體 │ │
│ │ │ │
│ │ 當前版本:KDP2 v2.2.0 │ │
│ │ 最新版本:KDP2 v2.2.0 │ │
│ │ │ │
│ │ [ 版本切換 ▾ ] │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ⓘ 沒看到裝置?檢查 USB 連接後到「硬體」分頁重新掃描。 │
│ │
└───────────────────────────────────────────────────────────────────────┘
配色解讀(裝置卡片左側圓點):
| 圓點顏色 | 條件 | 文字標籤 |
|---|---|---|
🔴 color.destructive |
KDP1 / legacy FW | KDP1 (legacy) |
🟡 color.warning |
有可用較新版本 | KDP2 v2.1.0 (older) |
🟢 color.success |
已是最新 bundled 版本 | KDP2 v2.2.0 (current) |
3.2 空狀態(無 dongle 連接)
┌─ Settings > 韌體 ───────────────────────────────────────────────────┐
│ │
│ 韌體管理 │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ │ │
│ │ 🔌 │ │
│ │ │ │
│ │ 尚未偵測到 Kneron 裝置 │ │
│ │ │ │
│ │ 插上 USB 裝置後,到「硬體」分頁點擊 │ │
│ │ 「重新掃描」便會出現在這裡。 │ │
│ │ │ │
│ │ [ 前往硬體分頁 ] │ │
│ │ │ │
│ └──────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────┘
3.3 「版本切換」展開後(accordion 樣式、不是 dropdown)
點 版本切換 ▾ 後、卡片下半部展開:
┌─ KL520 #1 ─────────────────────────────────────────────────────────┐
│ ● KDP2 v2.2.0 (current) kn_number: 0x... │
│ 裝置使用最新韌體 │
│ │
│ 當前版本:KDP2 v2.2.0 │
│ 最新版本:KDP2 v2.2.0 │
│ │
│ [ 版本切換 ▴ ] │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ⚠ 切換到舊版可能導致: │ │
│ │ • 部分新版 .nef 模型無法載入 │ │
│ │ • 推論效能與穩定性下降 │ │
│ │ • KDP1 無法執行多模型載入 │ │
│ │ 切換過程約需 30 秒(KL520)/ 3 分鐘(KL720),切勿拔除裝置。│ │
│ │ │ │
│ │ 可用版本: │ │
│ │ ○ KDP2 v2.2.0 (current) 當前版本 │ │
│ │ ○ KDP2 v2.1.0 發布於 2024-08(與舊模型相容) │ │
│ │ ○ KDP1 (legacy) 僅供 third-party 工具相容測試 │ │
│ │ │ │
│ │ [ 取消 ] [ 切換到此版本 ] │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
展開區的元件規格:
| 元素 | 類型 | 細節 |
|---|---|---|
| Inline warning | <div role="note"> |
color.warning/10 背景、color.warning 邊框(1px)、padding 16、圓角 radius.md |
| ⚠ icon | inline SVG | 20×20、color.warning |
| 版本 radio list | <input type="radio"> + <label> |
name 共用「fw-version-{deviceId}」、預設選中 current |
| 版本說明 | <span> |
12px / color.muted-foreground、跟版本 label 同行右側 |
| 「切換到此版本」按鈕 | Button destructive md |
disabled 條件:選中的就是 current;enabled 條件:選了非 current 版本 |
| 「取消」按鈕 | Button ghost md |
點擊 → 收起 accordion、radio 重置為 current |
為什麼用 radio list 不用 dropdown:
- bundled 版本通常 ≤ 3 個(current + 1 舊版 + KDP1),不需要 dropdown 的省空間優勢
- radio list 讓所有選項與描述都可見、降低使用者「不知道有什麼選項」的疑慮
- 對 a11y 友善(screen reader 一次讀完所有選項)
4. Devices 頁面:FW Badge 整合
依 R5-Q9 第 5 點,Devices 頁 device card 需顯示 FW badge。
4.1 既有 Devices 卡片(spec/03-wireframes.md §3.3)的補強
┌──────────────────┐ ┌──────────────────┐
│ KL720-A │ │ KL520-B │
│ 🟢 Ready │ │ 🟢 Ready │
│ FW [● v2.2.0] │ ← 綠 │ FW [● KDP1] ⚙ │ ← 紅 + 連結
│ kn: 0x5E6F... │ │ kn: 0x1A2B... │
│ [ Workspace ] │ │ [ Workspace ] │
└──────────────────┘ └──────────────────┘
4.2 Badge 規格
| Badge 狀態 | 顏色 token | 文字 | 形狀 |
|---|---|---|---|
| Current(綠) | color.success bg + color.success-foreground fg |
● v2.2.0 或對應版本 |
Pill, padding 2 8, radius radius.full, 11px medium |
| Older(黃) | color.warning bg + color.warning-foreground fg |
● v2.1.0 |
同上 |
| Legacy(紅) | color.destructive bg + color.destructive-foreground fg |
● KDP1 |
同上 |
版本後綴規則:
- bundled current → 不加後綴(純版本號)
- 較舊的 bundled 版本 → 不加後綴(但用黃色提示)
- 非 KDP2 系列(KDP1)→ 顯示
KDP1(不顯示版本號,因為 KDP1 沒有 semver)
4.3 Tooltip(hover badge)
| Badge | Tooltip 文案 |
|---|---|
| 綠 | 韌體為最新版本({version})。 |
| 黃 | 韌體版本較舊,建議升級。點擊前往韌體管理 → |
| 紅 | 此韌體為舊版 KDP1,建議升級到 KDP2 以支援完整功能。點擊前往韌體管理 → |
4.4 點擊行為(⚙ icon)
- 黃 / 紅 badge → badge 右側顯示 ⚙ icon(grey、12×12)
- 綠 badge → 不顯示 ⚙ icon(避免吸引使用者進入降版)
- 點 ⚙ icon → deep-link 跳轉到
Settings > 韌體並 scroll 到該裝置卡片(並 flash 該卡片邊框 600ms 提示) - 點 badge 本體 → 同 ⚙ icon 行為(提高 hit area)
- 鍵盤:badge / icon 都可 Tab 聚焦,Enter 觸發 deep-link
4.5 為什麼 Devices 頁不放升級 / 切換按鈕
依 architect 42 §1.2 / R5-Q9 第 5 點:
- Devices 頁是使用者最常用的 Workspace 入口前置頁,避免誤觸高風險操作
- Badge + ⚙ deep-link 維持「資訊呈現」的職責
- 真正執行升級 / 切換的操作集中在 Settings → 韌體分頁、視覺更隆重
5. 升級流程 UX(一鍵升級到 bundled latest)
5.1 升級確認 Modal(低風險、單擊 + 一次確認)
點裝置卡片的 升級到 KDP2 v2.2.0 按鈕後彈出:
┌─ 升級韌體 ──────────────────────────────────────┐
│ │
│ 升級 KL520 #1 的韌體 │
│ │
│ 當前版本:KDP1 (legacy) │
│ 升級到: KDP2 v2.2.0 │
│ │
│ ⓘ 升級過程約需 30 秒,期間切勿拔除裝置或關閉 │
│ 應用程式。完成後裝置會自動重新識別。 │
│ │
│ [ 取消 ] [ 開始升級 ] │
│ │
└───────────────────────────────────────────────────┘
規格:
- 寬度 480px、
color.card背景、elevation.4陰影 - 標題 18px / SemiBold
- 「開始升級」按鈕
primary變體(不是 destructive,因為升級是建議行為) - 「取消」按鈕
ghost - 可點外部關閉(升級不可中斷的警告留給降版 modal、升級單純確認意願即可)
- ESC 可關閉
5.2 升級進度 Modal(不可中斷)
點「開始升級」後 modal 不關閉、切換為進度狀態:
┌─ 升級韌體 ──────────────────────────────────────┐
│ │
│ 升級 KL520 #1 的韌體 │
│ │
│ ████████████████░░░░░░░░░░░ 60% │
│ │
│ 階段 3 / 5:寫入韌體 │
│ │
│ ⚠ 請勿拔除裝置 │
│ │
│ │
│ (沒有取消按鈕) │
│ │
└───────────────────────────────────────────────────┘
規格:
- 進度條:高 8px、
color.primaryfill、color.mutedtrack、圓角radius.full - 階段文字:14px / regular /
color.muted-foreground - ⚠ banner:紅色 destructive(升級失敗風險仍存在)
- 不顯示 ✕ / 取消按鈕(不可中斷)
- 點擊外部 / ESC:不關閉(捕獲事件並抖動 modal 200ms 提示)
- 預估時間(如可從後端取得):「預估剩餘 X 秒」附在階段文字下方
5.3 升級階段對應
依 architect 30 §M9-1 stage 對應。stage 命名以本檔 §8 狀態機為準(v2.2 對齊修正、取代早期草稿的 scan/connect/loader/upgrade/verify):
後端 stage(FirmwareProgress.Stage) |
UI 顯示文字 | 預估佔比 |
|---|---|---|
preparing |
階段 1 / 4:準備(偵測 + 連接裝置) | 5% |
loading |
階段 2 / 4:載入引導程式(僅 KDP1→KDP2 路徑) | 20% |
flashing |
階段 3 / 4:寫入韌體 | 50% |
verifying |
階段 4 / 4:驗證完成 | 90% |
done |
階段完成 | 100% |
如果是 KDP2→KDP2 同代升級(loading 引導程式路徑跳過),階段壓縮為 3 階段(preparing → flashing → verifying),UI 顯示文字改為 階段 n / 3。
stage 名稱對齊備註:
- 早期 Architect TDD 曾用
connecting / loading_loader / loading_firmware / verifying、Design 早期草稿曾用scan / connect / loader / upgrade / verify - v2.2 三方對齊後、以本檔 §8 狀態機的
preparing / loading / flashing / verifying為唯一 source of truth - 後端
FirmwareProgress.Stage欄位、Frontend i18n key(§9.6)、失敗 stage 對應(§7.1)全部用此命名
5.4 升級成功 Toast
升級成功後 modal 關閉、右上角 toast:
┌──────────────────────────────────────────┐
│ ✓ KL520 #1 升級成功 │
│ 從 KDP1 升級到 KDP2 v2.2.0(耗時 28 秒)│
└──────────────────────────────────────────┘
- variant: success
- 停留 6 秒(比預設 3 秒長、讓使用者讀完細節)
- 同時:對應裝置卡片自動重新整理(rescan API),FW badge 變綠
5.5 升級失敗
詳見 §7「失敗復原 UX」。
6. 版本切換流程 UX(含降版、高風險)
依 R5-Q9 第 5 點「使用『輸入 DOWNGRADE 字串』二次確認」+ architect 42 §5.3。
6.1 進入二次確認 Modal
使用者在 §3.3 accordion 選了非 current 版本、點「切換到此版本」後:
┌─ 切換到 KDP2 v2.1.0 ─────────────────────────────────────────┐
│ │
│ ⚠ 確認切換到舊版韌體? │
│ │
│ 你正在將 KL520 #1 的韌體切換到較舊的版本。 │
│ │
│ ┌──────────────────────┬──────────────────────┐ │
│ │ 當前版本 │ 將切換到 │ │
│ ├──────────────────────┼──────────────────────┤ │
│ │ KDP2 v2.2.0 │ KDP2 v2.1.0 │ │
│ │ 2025-03 發行 │ 2024-08 發行 │ │
│ └──────────────────────┴──────────────────────┘ │
│ │
│ 切換到舊版可能導致: │
│ • 部分新版 .nef 模型無法載入 │
│ • 推論效能與穩定性下降 │
│ • 切換過程中拔除裝置可能導致裝置損毀 │
│ │
│ 預估時間:約 30 秒 │
│ │
│ 為確認你了解風險,請輸入「DOWNGRADE」: │
│ ┌──────────────────────────────────────────┐ │
│ │ │ │
│ └──────────────────────────────────────────┘ │
│ (大小寫需完全相同) │
│ │
│ [ 取消 ] [ 確認切換 ] │
│ │
└────────────────────────────────────────────────────────────────┘
規格:
- Modal 寬度:560px
- 標題:「⚠ 確認切換到舊版韌體?」(KDP1 目標時改為「⚠ 確認切換到 KDP1?」)
- 比較表格:兩欄、表頭
color.muted背景、表身 paddingspace.3 - 風險列表:用
<ul>、12px /color.foreground、無 icon(避免過載) - 「DOWNGRADE」輸入:
<input type="text" autocomplete="off" spellcheck="false">- placeholder:
輸入 DOWNGRADE 確認(i18n key 換對應 EN:Type DOWNGRADE to confirm) - 輸入框邊框:未輸入 / 輸入錯誤時
color.warning;輸入正確時color.success - 輸入正確時:邊框變綠 + 右側顯示
✓icon(16×16、color.success)
- 「確認切換」按鈕:
- variant:
destructive - disabled 條件:input 值不等於字面
DOWNGRADE - 大小:
md
- variant:
- 「取消」按鈕:
ghostmd - 可點外部關閉嗎? → 不能(捕獲事件、抖動 200ms 提示「請使用『取消』按鈕關閉」),ESC 可關閉(與外部點擊行為不同——ESC 是鍵盤使用者的明確意圖、外部點擊較可能誤觸)
6.2 切換到 KDP1 的額外警告
當目標版本是 KDP1(legacy)時,風險列表多加一條:
• KDP1 不支援多模型載入,部分 visionA-local 功能將無法使用
並在表格上方加紅色 banner:
┌──────────────────────────────────────────────────┐
│ 🚫 KDP1 是已棄用的舊版韌體,建議僅用於 │
│ 與第三方工具相容性測試。 │
└──────────────────────────────────────────────────┘
- banner 背景
color.destructive/10、邊框color.destructive/30、iconcolor.destructive - 文字 13px / SemiBold /
color.destructive
6.3 切換進行中 Modal
確認後 modal 不關閉、直接切換為進度狀態(與 §5.2 升級進度同設計、但有差異):
┌─ 切換韌體 ──────────────────────────────────────┐
│ │
│ 切換 KL520 #1 至 KDP2 v2.1.0 │
│ │
│ ████████████░░░░░░░░░░░░░░░ 45% │
│ │
│ 階段 3 / 5:寫入韌體 │
│ │
│ ⚠ 請勿拔除裝置、請勿關閉應用程式 │
│ 切換韌體不可中斷,中斷可能導致裝置永久損毀。 │
│ │
│ │
└───────────────────────────────────────────────────┘
與升級進度的差異:
- 警告語更強(「永久損毀」用詞)
- 警告區域更大、紅色背景(不只是紅字)
- 仍然沒有取消按鈕
全螢幕 hover 攔截:當切換進行中時、視窗任何地方(除了 modal 內)的點擊都被攔截、滑鼠 cursor 變 not-allowed、確保使用者不會誤關視窗。
6.4 切換成功
切換成功後與升級成功類似,toast:
┌──────────────────────────────────────────┐
│ ✓ KL520 #1 韌體已切換到 KDP2 v2.1.0 │
│ (耗時 32 秒、可能需要重新插拔裝置) │
└──────────────────────────────────────────┘
variant: success、停留 8 秒(含「可能需要重新插拔」提示,使用者要讀完)。
7. 失敗復原 UX(升級 / 切換共用)
依 architect 42 §5.5 失敗類型分類。
7.1 失敗類型對應
UI 失敗分類以 stage(§5.3 / §8 狀態機)為主軸、加上 timeout / disconnect 兩個橫切性失敗,共 7 種使用者面友善訊息。對應 backend FirmwareProgress.Reason 的細分原因見 TDD §3.4(Architect TDD v2.2 新增的 stage→errorCode→reason 對應表)。
| 失敗 stage / 類型 | 對應 backend stage | 使用者面友善訊息 | 建議動作 |
|---|---|---|---|
preparing 找不到裝置(scan_not_found) |
preparing |
找不到裝置,可能已斷開。 | [ 重新插拔後重試 ] |
preparing 連接失敗(connect_failed) |
preparing |
無法連接裝置,請確認 USB 連接後重試。 | [ 重試 ] |
loading 引導程式寫入失敗(loader_write_failed) |
loading |
引導程式載入失敗。請拔除裝置後重新插入並重試。 | [ 拔插後重試 ] |
flashing 寫入失敗(upgrade_mid_failed) |
flashing |
韌體寫入失敗。可能是裝置硬體異常,請聯絡技術支援。 | [ 複製錯誤訊息 ] [ 取得協助 ] |
verifying 失敗(verify_mismatch) |
verifying |
韌體已寫入但驗證未通過。請拔除裝置後重新插入並到「硬體」分頁重新掃描。 | [ 拔插後重新掃描 ] |
| Timeout(>60s) | 任一 stage、reason=timeout |
操作超時。可能成功也可能未完成,請拔除裝置後重新掃描以確認狀態。 | [ 拔插後重新掃描 ] |
| Disconnect during op | 任一 stage、reason=disconnect_during_op |
裝置在操作過程中斷開,狀態未知。請拔除後重新插入裝置。 | [ 重新插拔後重試 ] |
Reason 欄位對應:Frontend 收到 WebSocket error event 時、依 FirmwareProgress.Stage + FirmwareProgress.Reason 兩欄位查表決定顯示哪條 friendly message。完整 backend reason 列舉與 errorCode 對應見 TDD §3.4。
7.2 失敗 Modal Wireframe
┌─ 升級失敗 / 切換失敗 ────────────────────────────────┐
│ │
│ ⚠ 韌體寫入失敗 │
│ │
│ {對應友善訊息} │
│ │
│ 錯誤代碼:fw_loading_loader_write_failed_E102 │
│ │
│ ─ 技術資訊 ▾ ─ │
│ ┌──────────────────────────────────────────┐ │
│ │ stage: loading │ │
│ │ reason: loader_write_failed │ │
│ │ device: KL520 port 1 │ │
│ │ before: KDP1 │ │
│ │ raw_error: kp_update_kdp_firmware... │ │
│ │ duration_ms: 18203 │ │
│ └──────────────────────────────────────────┘ │
│ │
│ [ 複製錯誤訊息 ] │
│ │
│ [ 關閉 ] [ 對應的建議動作按鈕 ] │
│ │
└───────────────────────────────────────────────────────┘
規格:
- 寬度 560px
- icon 24×24、
color.destructive - 標題 18px / SemiBold /
color.destructive - 友善訊息 14px / regular /
color.foreground、最多 3 行 - 錯誤代碼:12px / mono /
color.muted-foreground(給技術支援辨識用) - 「技術資訊」collapsible、預設收合
- 展開內容用
font.family.mono12px、color.surface-1背景、padding 12、圓角radius.md、可選取複製 - 「複製錯誤訊息」:複製整段技術資訊到剪貼簿、複製後按鈕短暫變「已複製 ✓」2 秒
- modal 不自動關閉(讓使用者讀完訊息)
7.3 部分成功(罕見但需處理)
若後端回傳 verify 階段失敗、但韌體已寫入(before/after 版本不同),UX 邏輯:
- 視為「升級可能成功、但驗證未確認」
- toast 提示為 warning(非 destructive):「韌體可能已升級但無法驗證,請重新插拔裝置後到『硬體』分頁重新掃描。」
- 裝置卡片暫時隱藏 FW badge(顯示「狀態未知」灰色 badge)、直到使用者重新掃描
8. 狀態機(裝置卡片 + Modal 整合)
[idle]
│ 使用者點「升級」/「版本切換 → 切換到此版本」
▼
[confirming] ── 使用者點「取消」 / ESC ──▶ [idle]
│
│ 使用者點「開始升級」 / 輸入 DOWNGRADE + 確認
▼
[preparing] ← 後端 FirmwareProgress.Stage = "preparing"(涵蓋偵測 + 連接)
│
▼
[loading] ← 後端 FirmwareProgress.Stage = "loading"(僅 KDP1→KDP2 路徑)
│
▼
[flashing] ← 後端 FirmwareProgress.Stage = "flashing"
│
▼
[verifying] ← 後端 FirmwareProgress.Stage = "verifying"
│
├── 成功 ──▶ [success_toast] ──▶ rescan ──▶ [idle](badge 更新)
│
└── 失敗 ──▶ [error_modal] ── 使用者點「重試」──▶ [preparing]
│
└── 使用者點「關閉」──▶ [idle](裝置卡 badge 維持原狀或變灰)
狀態 → UI 對應:
| 狀態 | 卡片視覺 | Modal 視覺 |
|---|---|---|
| idle | 正常 | 不顯示 |
| confirming | 卡片變灰、按鈕 disabled | 確認 modal 顯示 |
| preparing / loading / flashing / verifying | 卡片變灰、按鈕 disabled、有 inline progress bar(修可選、與 modal 重複) | 進度 modal 顯示,進度條對應 stage |
| success_toast | 短暫高亮(綠 ring 1s)後復原 | 關閉、toast 出現 |
| error_modal | 卡片變灰 | 失敗 modal 顯示 |
9. i18n keys 清單(namespace: settings.firmware)
依 architect 42 §5.6 估算 46 keys、實際定版 51 keys(v2.2 對齊修正後、含 settings 分頁 label 與 Devices 頁 badge tooltip;對齊細節見本節末尾備註)。
9.1 頁面結構
| Key | zh-TW | en |
|---|---|---|
settings.tabs.firmware |
韌體 | Firmware |
settings.firmware.title |
韌體管理 | Firmware Management |
settings.firmware.description |
管理已連接 Kneron 裝置的韌體版本。 | Manage firmware versions for connected Kneron devices. |
settings.firmware.empty.title |
尚未偵測到 Kneron 裝置 | No Kneron device detected |
settings.firmware.empty.description |
插上 USB 裝置後,到「硬體」分頁點擊「重新掃描」便會出現在這裡。 | Plug in a USB device and click "Rescan" on the Hardware tab to see it here. |
settings.firmware.empty.cta |
前往硬體分頁 | Go to Hardware |
settings.firmware.helpText |
沒看到裝置?檢查 USB 連接後到「硬體」分頁重新掃描。 | Don't see your device? Check USB connection then rescan on the Hardware tab. |
9.2 裝置卡片
| Key | zh-TW | en |
|---|---|---|
settings.firmware.card.label.knNumber |
kn_number | kn_number |
settings.firmware.card.label.currentVersion |
當前版本 | Current version |
settings.firmware.card.label.latestVersion |
最新版本 | Latest version |
settings.firmware.card.tag.current |
(current) | (current) |
settings.firmware.card.tag.latest |
(建議) | (recommended) |
settings.firmware.card.tag.legacy |
(legacy) | (legacy) |
settings.firmware.card.status.legacy |
KDP1 (legacy) — 建議升級到 KDP2 | KDP1 (legacy) — Upgrade to KDP2 recommended |
settings.firmware.card.status.older |
有可用較新版本 | Newer version available |
settings.firmware.card.status.current |
裝置使用最新韌體 | Device is up to date |
settings.firmware.card.action.upgrade |
升級到 {version} | Upgrade to {version} |
settings.firmware.card.action.switchVersion |
版本切換 | Switch version |
9.3 版本切換 accordion
| Key | zh-TW | en |
|---|---|---|
settings.firmware.switch.warningTitle |
切換到舊版可能導致: | Switching to an older version may cause: |
settings.firmware.switch.risk.modelIncompat |
部分新版 .nef 模型無法載入 | Some newer .nef models may not load |
settings.firmware.switch.risk.perfDegrade |
推論效能與穩定性下降 | Reduced inference performance and stability |
settings.firmware.switch.risk.kdp1Limit |
KDP1 無法執行多模型載入 | KDP1 does not support multi-model loading |
settings.firmware.switch.duration |
切換過程約需 {duration},切勿拔除裝置。 | Switching takes ~{duration}. Do not unplug the device. |
settings.firmware.switch.availableVersions |
可用版本: | Available versions: |
settings.firmware.switch.versionNote.releasedOn |
發布於 {date} | Released {date} |
settings.firmware.switch.versionNote.compatibleWithOld |
與舊模型相容 | Compatible with older models |
settings.firmware.switch.versionNote.thirdPartyOnly |
僅供 third-party 工具相容測試 | For third-party tool compatibility testing only |
settings.firmware.switch.action.cancel |
取消 | Cancel |
settings.firmware.switch.action.switchToThis |
切換到此版本 | Switch to this version |
9.4 升級確認 modal
| Key | zh-TW | en |
|---|---|---|
settings.firmware.upgradeModal.title |
升級韌體 | Upgrade firmware |
settings.firmware.upgradeModal.heading |
升級 {deviceName} 的韌體 | Upgrade firmware on {deviceName} |
settings.firmware.upgradeModal.from |
當前版本 | Current version |
settings.firmware.upgradeModal.to |
升級到 | Upgrading to |
settings.firmware.upgradeModal.warning |
升級過程約需 {duration},期間切勿拔除裝置或關閉應用程式。完成後裝置會自動重新識別。 | Upgrade takes ~{duration}. Do not unplug the device or close the application. The device will be re-detected automatically when done. |
settings.firmware.upgradeModal.action.start |
開始升級 | Start upgrade |
9.5 降版二次確認 modal
| Key | zh-TW | en |
|---|---|---|
settings.firmware.downgradeModal.title |
切換到 {version} | Switch to {version} |
settings.firmware.downgradeModal.heading |
⚠ 確認切換到舊版韌體? | ⚠ Confirm switching to an older firmware? |
settings.firmware.downgradeModal.headingKdp1 |
⚠ 確認切換到 KDP1? | ⚠ Confirm switching to KDP1? |
settings.firmware.downgradeModal.intro |
你正在將 {deviceName} 的韌體切換到較舊的版本。 | You are about to switch the firmware on {deviceName} to an older version. |
settings.firmware.downgradeModal.kdp1Banner |
KDP1 是已棄用的舊版韌體,建議僅用於與第三方工具相容性測試。 | KDP1 is a deprecated legacy firmware. Recommended only for third-party tool compatibility testing. |
settings.firmware.downgradeModal.compareHeader.from |
當前版本 | Current version |
settings.firmware.downgradeModal.compareHeader.to |
將切換到 | Switching to |
settings.firmware.downgradeModal.risk.brick |
切換過程中拔除裝置可能導致裝置損毀 | Unplugging the device during the process may damage it |
settings.firmware.downgradeModal.duration |
預估時間:約 {duration} | Estimated time: ~{duration} |
settings.firmware.downgradeModal.confirmPrompt |
為確認你了解風險,請輸入「DOWNGRADE」: | To confirm you understand the risks, type "DOWNGRADE": |
settings.firmware.downgradeModal.confirmPlaceholder |
輸入 DOWNGRADE 確認 | Type DOWNGRADE to confirm |
settings.firmware.downgradeModal.confirmHelpText |
(大小寫需完全相同) | (case-sensitive) |
settings.firmware.downgradeModal.action.confirm |
確認切換 | Confirm switch |
settings.firmware.downgradeModal.escAttemptHint |
請使用「取消」按鈕關閉 | Use the "Cancel" button to close |
9.6 進度 modal
| Key | zh-TW | en |
|---|---|---|
settings.firmware.progress.upgradeTitle |
升級韌體 | Upgrading firmware |
settings.firmware.progress.switchTitle |
切換韌體 | Switching firmware |
settings.firmware.progress.deviceHeading.upgrade |
升級 {deviceName} 的韌體 | Upgrading firmware on {deviceName} |
settings.firmware.progress.deviceHeading.switch |
切換 {deviceName} 至 {version} | Switching {deviceName} to {version} |
settings.firmware.progress.stage.preparing |
階段 {n} / {total}:準備(偵測 + 連接裝置) | Stage {n} / {total}: Preparing |
settings.firmware.progress.stage.loading |
階段 {n} / {total}:載入引導程式 | Stage {n} / {total}: Loading bootloader |
settings.firmware.progress.stage.flashing |
階段 {n} / {total}:寫入韌體 | Stage {n} / {total}: Flashing firmware |
settings.firmware.progress.stage.verifying |
階段 {n} / {total}:驗證完成 | Stage {n} / {total}: Verifying |
settings.firmware.progress.warning.upgrade |
⚠ 請勿拔除裝置 | ⚠ Do not unplug the device |
settings.firmware.progress.warning.switch |
⚠ 請勿拔除裝置、請勿關閉應用程式。切換韌體不可中斷,中斷可能導致裝置永久損毀。 | ⚠ Do not unplug the device or close the application. Firmware switching cannot be interrupted; interruption may permanently damage the device. |
settings.firmware.progress.estimatedRemaining |
預估剩餘 {seconds} 秒 | ~{seconds}s remaining |
9.7 成功 toast
| Key | zh-TW | en |
|---|---|---|
settings.firmware.success.upgrade |
{deviceName} 升級成功 | {deviceName} upgrade succeeded |
settings.firmware.success.upgradeDetail |
從 {from} 升級到 {to}(耗時 {duration}) | From {from} to {to} (took {duration}) |
settings.firmware.success.switch |
{deviceName} 韌體已切換到 {version} | {deviceName} firmware switched to {version} |
settings.firmware.success.switchDetail |
(耗時 {duration}、可能需要重新插拔裝置) | (took {duration}, may need to unplug and reconnect) |
settings.firmware.partialSuccess.toast |
韌體可能已升級但無法驗證,請重新插拔裝置後到「硬體」分頁重新掃描。 | Firmware may have been written but verification failed. Please unplug, reconnect, and rescan on the Hardware tab. |
9.8 失敗訊息
| Key | zh-TW | en |
|---|---|---|
settings.firmware.error.modalTitle |
韌體操作失敗 | Firmware operation failed |
settings.firmware.error.message.preparing.scanNotFound |
找不到裝置,可能已斷開。 | Device not found, possibly disconnected. |
settings.firmware.error.message.preparing.connectFailed |
無法連接裝置,請確認 USB 連接後重試。 | Cannot connect to device. Please check the USB connection and retry. |
settings.firmware.error.message.loading |
引導程式載入失敗。請拔除裝置後重新插入並重試。 | Bootloader load failed. Please unplug, reconnect, and retry. |
settings.firmware.error.message.flashing |
韌體寫入失敗。可能是裝置硬體異常,請聯絡技術支援。 | Firmware write failed. Possible hardware issue, please contact technical support. |
settings.firmware.error.message.verifying |
韌體已寫入但驗證未通過。請拔除裝置後重新插入並到「硬體」分頁重新掃描。 | Firmware was written but verification failed. Please unplug, reconnect, and rescan on the Hardware tab. |
settings.firmware.error.message.timeout |
操作超時。可能成功也可能未完成,請拔除裝置後重新掃描以確認狀態。 | Operation timed out. The operation may or may not have completed. Please unplug, reconnect, and rescan to confirm. |
settings.firmware.error.message.disconnect |
裝置在操作過程中斷開,狀態未知。請拔除後重新插入裝置。 | Device disconnected during operation. State unknown. Please unplug and reconnect. |
settings.firmware.error.errorCode |
錯誤代碼:{code} | Error code: {code} |
settings.firmware.error.technicalInfo |
技術資訊 | Technical info |
settings.firmware.error.copyError |
複製錯誤訊息 | Copy error info |
settings.firmware.error.copied |
已複製 ✓ | Copied ✓ |
settings.firmware.error.action.close |
關閉 | Close |
settings.firmware.error.action.retry |
重試 | Retry |
settings.firmware.error.action.replugRetry |
重新插拔後重試 | Unplug and retry |
settings.firmware.error.action.rescan |
拔插後重新掃描 | Unplug and rescan |
settings.firmware.error.action.getHelp |
取得協助 | Get help |
9.9 Devices 頁 FW badge tooltip
| Key | zh-TW | en |
|---|---|---|
devices.card.fwBadge.tooltipCurrent |
韌體為最新版本({version})。 | Firmware is up to date ({version}). |
devices.card.fwBadge.tooltipOlder |
韌體版本較舊,建議升級。點擊前往韌體管理 → | Firmware is older. Click to manage firmware → |
devices.card.fwBadge.tooltipLegacy |
此韌體為舊版 KDP1,建議升級到 KDP2 以支援完整功能。點擊前往韌體管理 → | Legacy KDP1 firmware. Upgrade to KDP2 for full feature support. Click to manage firmware → |
devices.card.fwBadge.deepLinkA11y |
前往韌體管理 — {deviceName} | Go to firmware management — {deviceName} |
合計新增 i18n keys:51 個(zh-TW 與 en 各一套)。
v2.2 對齊修正:原稿標 52、本輪因 stage 命名統一(§5.3 / §8)將 §9.6 從 5 個 stage key 減為 4 個(
preparing/loading/flashing/verifying),同時 §9.8 將preparing拆為scanNotFound/connectFailed兩個 sub-key(總數不變)。
10. 無障礙考量(A11y)
| 項目 | 設計 |
|---|---|
| Settings 分頁切換 | 沿用既有 Tabs 元件,role="tablist" / role="tab" / role="tabpanel"、← → 切換 tab |
| 裝置卡片結構 | <section aria-labelledby="fw-device-{id}">、標題用 <h3 id="fw-device-{id}">{deviceName}</h3> |
| FW 狀態圓點 | 不單靠顏色傳達——圓點旁同時有文字標籤(KDP1 (legacy) 等);圓點本身加 aria-hidden="true" 避免重複朗讀 |
| 版本 radio | 每個 radio 用 <label> 包住 visible 文字、fieldset + legend 包整組(<legend> 用 sr-only class 隱藏視覺、保留 screen reader) |
| 「切換到此版本」disabled 狀態 | 加 aria-describedby="hint-fw-{id}"、hint 用 sr-only 寫「請先選擇與當前不同的版本」 |
| Upgrade modal | role="dialog" aria-modal="true" aria-labelledby="..." aria-describedby="..."、focus trap、ESC 可關 |
| Downgrade modal | 同上 + 額外 aria-describedby 指向風險列表 <ul> 的 id(screen reader 先讀標題、再讀風險) |
| DOWNGRADE 輸入 | <input aria-label="輸入 DOWNGRADE 確認" aria-describedby="downgrade-hint">、hint 「大小寫需完全相同」、輸入正確時 aria-invalid="false"、錯誤時 aria-invalid="true" |
| 進度 modal | <div role="status" aria-live="polite"> 包進度文字(不用 aria-live="assertive"、避免每秒朗讀) |
| 進度條 | <div role="progressbar" aria-valuenow={percent} aria-valuemin="0" aria-valuemax="100" aria-valuetext="階段 {n}/{total}"> |
| 失敗 modal | role="alertdialog"(比 dialog 更強的語意、screen reader 會主動朗讀) |
| 鍵盤導航 | Tab 順序:分頁切換 → 第 1 張卡片的升級按鈕 → 第 1 張卡片的版本切換按鈕 → 第 2 張卡片… |
| Focus ring | 沿用 ring.2 · color.ring、2px outline-offset |
| 色彩對比 | FW badge 紅 / 黃 / 綠對白色文字皆 ≥ 4.5:1(critical 信號不妥協) |
| 觸控目標 | 升級按鈕、版本切換按鈕、⚙ icon 全部 ≥ 44×44px tap area(即使視覺更小、padding 補足) |
| Reduced motion | prefers-reduced-motion: reduce → 進度條改靜態顯示百分比文字、modal 動畫 0ms、卡片高亮 1s 改為直接最終色(不淡入淡出) |
11. Design Tokens 沿用 / 新增
11.1 沿用既有 tokens(spec/07-design-tokens.md)
所有色彩、字級、間距、圓角、elevation、focus ring 全部沿用 v1 既有 token。
具體沿用的 semantic token:
color.destructive/color.warning/color.success/color.infocolor.muted/color.muted-foreground/color.foregroundcolor.card/color.background/color.bordercolor.primary/color.primary-foregroundcolor.ringradius.md/radius.lg/radius.fullspace.2~space.8font.size.xs/sm/base/lgfont.weight.medium/semiboldfont.family.mono(技術資訊區用)
11.2 新增 component-level tokens(本檔提案)
只在「需要」時新增、避免過度膨脹 token 系統:
| 新 token | 推導自 | 用途 |
|---|---|---|
color.fw-badge.current.bg |
color.success |
FW badge 綠底 |
color.fw-badge.current.fg |
color.background(白) |
FW badge 綠底文字 |
color.fw-badge.older.bg |
color.warning |
FW badge 黃底 |
color.fw-badge.older.fg |
color.foreground |
FW badge 黃底文字(黑、確保對比) |
color.fw-badge.legacy.bg |
color.destructive |
FW badge 紅底 |
color.fw-badge.legacy.fg |
color.background(白) |
FW badge 紅底文字 |
為什麼要拆 component token:
- Light / Dark 模式下,warning 黃底配什麼前景需獨立調(直接用
color.warning-foreground可能對比不足) - 未來如果調 badge 視覺、不需要動 semantic token 影響其他元件
推導表(Light / Dark):
| Token | Light value | Dark value | 對比比率(對 fg) |
|---|---|---|---|
color.fw-badge.current.bg |
oklch(0.65 0.17 145) |
oklch(0.75 0.17 145) |
4.7 : 1 |
color.fw-badge.current.fg |
oklch(1 0 0) |
oklch(0.145 0 0) |
— |
color.fw-badge.older.bg |
oklch(0.75 0.18 85) |
oklch(0.80 0.18 85) |
5.2 : 1 |
color.fw-badge.older.fg |
oklch(0.145 0 0) |
oklch(0.145 0 0) |
— |
color.fw-badge.legacy.bg |
oklch(0.577 0.245 27.325) |
oklch(0.704 0.191 22.216) |
4.8 : 1 |
color.fw-badge.legacy.fg |
oklch(1 0 0) |
oklch(0.145 0 0) |
— |
對比比率實測責任(v2.2 三方互審後對齊):
表中 4.7:1 / 5.2:1 / 4.8:1 為 Design 在 oklch 色彩空間的推算值、提供給後續實作 / 驗收作參考。
實測為 M9-12 Frontend Agent 責任:M9-12 Frontend 在落地 6 個 component token 到
frontend/src/app/globals.css後、於 Design QA 階段用 axe-core / Lighthouse / Chrome DevTools contrast checker 工具驗證真實對比 ≥ 4.5:1(WCAG AA)、特別需注意 Dark 模式下legacy.fg黑底紅oklch(0.704 0.191 22.216)配oklch(0.145 0 0)文字。實測未達標時、Frontend 與 Design 對齊調整 token 數值。此項不需 Architect 互審回應——是 implement-time verification、不是 design-time decision。
12. R-FW 風險的 Design 對策
12.1 R-FW-11:一般使用者誤觸降版 brick 風險
依 architect 42 §6 風險評估、Design 提供多層 mitigation:
| 風險路徑 | Design 緩解措施 | 對應 §章節 |
|---|---|---|
| 點到「版本切換」就直接降 | 二次確認 modal、需輸入「DOWNGRADE」字串 | §6.1 |
| 沒讀風險警告就降 | 風險列表大字、color.warning 背景區塊、KDP1 額外紅色 banner |
§6.1 §6.2 |
| 降到 KDP1 後發現功能不可用 | KDP1 額外警告語、明示「不支援多模型」 | §6.2 |
| 降版進行中拔 USB | 全螢幕 hover 攔截 + 紅色 banner「永久損毀」 + 不可關閉 modal | §6.3 |
| 降版進行中關 app | 同上、加上 beforeunload event handler 攔截(Frontend 實作) |
§6.3 + 給 Frontend 提示 |
| 升錯版本當降版 | Architect 42 §2.4 driver 層 guard:拒絕目標版本 >= current(這是後端職責) | UI 不需處理、driver 拒絕後顯示「無效操作」error modal |
| 找不到救援路徑 | 失敗 modal 提供「取得協助」連結 + 「複製錯誤訊息」給技術支援 | §7.2 |
12.2 R-FW-11.5(Design 自提):使用者輸入「downgrade」小寫繞過
依 §6.1,input 嚴格 case-sensitive 比對字面 DOWNGRADE、小寫 / 全形 / 半形 / 空白都不接受。
Frontend 實作提示:用 event.target.value === 'DOWNGRADE'(嚴格相等)、不要用 .toUpperCase() 後比對。
12.3 R-FW-11.6(Design 自提):意外觸發 deep-link 跳轉
從 Devices 頁的 ⚙ icon deep-link 到 Settings → 韌體後、不自動展開「版本切換」accordion——只 highlight 卡片邊框 600ms。 理由:deep-link 對應的是「使用者想關注此裝置的韌體狀態」、不是「使用者已決定要降版」。展開 accordion 等於暗示「快來降版」、與整體謹慎策略矛盾。
13. 與既有 design v2.1 的銜接點
13.1 與 v2/settings-update.md 的銜接
- Settings 分頁結構從 4 變 5、第 3 個分頁新增「韌體」
- v2.1 既有的「一般」「硬體」「模型」「進階」內容不變、只是順序調整
- 本檔不重寫 settings-update.md、但 settings-update.md 應在下次補丁加註「分頁順序見 firmware-management.md §2」
- Settings 整體分頁切換高度:保持與既有分頁一致(依
spec/03-wireframes.md §3.X Settings既定高度),新分頁內容若超出視窗高度則加 scroll(不擴張 Settings 整體高度)
13.2 與 spec/03-wireframes.md §3.3 Devices 的銜接
- Devices 卡片現有結構 5 行(標題 / status / FW / kn / button)保持
- FW 那行從純文字
FW 2.1.0改為FW [● v2.2.0]pill badge(+ 黃/紅時加 ⚙ icon) - pill badge 寬度依版本字串自適應、但 FW 行整體高度不變(仍 18px)
- 點 badge / ⚙ 行為見 §4.4
13.3 與 v2/control-panel.md 的銜接
控制台不涉及 FW 管理——FW 管理在瀏覽器端 Settings、不在控制台。
- 升級 / 切換 modal 的 dialog 不會在控制台顯示
- 但升級 / 切換的 log(後端 stage 訊息)會出現在控制台 log panel(INFO / WARN / ERROR)
- log 等級依 architect 30 §M9-3 設計、Design 不重複定
13.4 與 spec/08-states.md(全域錯誤分級)的銜接
韌體升級 / 切換失敗的 嚴重級別屬於 Error(08 表的 Error 級別、非 Critical):
- 不擋整個 app
- 顯示 modal、可關閉
- 提供重試 + 排錯提示
- 對應 08 §8.3 inline error 規格、但因為涉及裝置操作、用 modal 而非 inline
14. v2.1 → v2.2 索引變更摘要(給 design-spec-v2.md)
14.1 索引變更
在 design-spec-v2.md §文件結構 表格、加一行:
| **v2.7** | **韌體管理**(v2.2 新增) | **`v2/firmware-management.md`** | **Kneron dongle FW 升級 + 版本切換完整規格:Settings 新分頁、Devices 頁 FW badge、二次確認流程、51 個 i18n keys、R-FW-11 設計緩解** |
14.2 版本標頭變更
- 版本:
v2.1→v2.2 - 子標題增補:「v2.2(2026-05-24 補丁)」吸收 R5-Q9 翻案 + B2 階段降版面向一般使用者的設計規格
14.3 「v2 → v2.2 差異總覽」表格新增段
## v2.1 → v2.2 差異總覽(2026-05-24 補丁)
v2.2 是吸收 R5-Q9 翻案後的補丁,新增 FW 管理面向一般使用者的設計規格。
| 面向 | v2.1 | v2.2(2026-05-24 補丁)| 來源 |
|------|------|---------------------|------|
| Settings 分頁數 | 4(一般 / 硬體 / 模型 / 進階)| **5**(一般 / 硬體 / **韌體** / 模型 / 進階)| R5-Q9 + architect 42 |
| Devices 頁 FW 顯示 | 純文字 `FW 2.1.0` | **pill badge**(紅 / 黃 / 綠三色)+ deep-link ⚙ icon | R5-Q9 第 5 點 |
| FW 升級 UX | 未設計 | **單步驟確認 + 進度 modal**(不可中斷)| architect 30 §M9-4 |
| FW 降版 UX | 未設計 | **二次確認**(輸入「DOWNGRADE」字串)+ 進度 modal + 全螢幕 hover 攔截 | architect 42 §5.3 |
| 失敗復原 UX | 未設計 | **8 種失敗類型對應 friendly message + 復原行動** | architect 42 §5.5 |
| 新增 i18n keys | — | **51 keys**(zh-TW + en)| 本檔 §9 |
| 新增 component tokens | — | **6 個 fw-badge token** | 本檔 §11.2 |
14.4 「給 Orchestrator 的問題(懸而未決)」新增
**v2.2 新增懸而未決**:
4. **降版 modal 字串「DOWNGRADE」未來 i18n 化的處理**:當前 Design 規格要求使用者輸入字面字串 `DOWNGRADE`(en),中文使用者也輸入這個字。若未來改為 i18n(如中文使用者輸入「降版」),需與 Architect / Frontend 確認後端 confirmToken 是否同步調整(目前 architect 42 §2.3 後端固定接受 `confirmToken: "DOWNGRADE"`)。建議**短期維持英文字串**(跨語言一致、避免後端多語對應)、長期看是否分語系 token。
5. **bundled 版本清單的「發布日期」資料源**:§3.3 範例顯示「2024-08 發行」等文案,但目前 architect 42 §2.1 `FirmwareVersion.ReleaseDate` 為 optional。需 Backend 在 bundle firmware 時準備 metadata(每個 version 目錄附 `META.json` 含 releaseDate / notes)。如果 metadata 缺、UI fallback 為不顯示日期、只顯示版本號。
6. ~~**降版進行中關閉 Wails 控制台視窗會發生什麼**~~ **(已解決、v2.2 三方對齊)**:依 R5-2 控制台關閉 = 結束 server。降版進行中關控制台 = 中斷降版 = 可能 brick。**解法(三方對齊)**:(a) Architect 在 TDD §8.6 補 server graceful shutdown 拒絕邏輯(偵測 active firmware task → 拒絕 shutdown → 通知 Wails 控制台);(b) Design 在 `v2/control-panel.md` 補對應 UI——降版進行中按關閉視窗 → 跳警告 modal「韌體更新進行中、強制關閉會造成裝置損毀」+ 預估剩餘時間 + 兩個按鈕(「繼續等待」主、「強制關閉」destructive)。
15. 給 Orchestrator 派 PM / Architect 互審的注意點
15.1 派 PM 互審重點
- 驗 PRD 是否需要新增章節:R5-Q9 翻案後、PRD-v2 應有對應 §「Firmware 管理 v2.2 補丁」、Design 已對齊 architect 30 §M9-0 PM 任務、但 PM 是否實際補了 PRD 是 Orchestrator 應追蹤的點
- 驗 user story 覆蓋:本檔 §1 列了 4 種使用者降版情境(architect 42 §1.1 取出)、PM 應檢查是否與 PRD 既有 user story 相容、是否需要新增 user story「進階使用者切換 FW 版本」
- 驗 UI 用語:Design 採用「韌體 / 版本切換」中性詞、不用「降版」對 user-facing UI。PM 確認此用語策略與 PRD 整體 product voice 一致
- 驗成功指標:本功能的 PRD 是否定了 success metric(如「90% 升級成功率」「Brick 事件 < 0.1%」),Design 文件未涵蓋指標、但實作前 PM 應補
15.2 派 Architect 互審重點
- §2.1 分頁順序:Architect 是否覺得「韌體」放在「硬體」與「模型」之間是合理的(vs 放最後 / 進階)
- §4.1 FW badge 配色:Architect review §11.2 推導表的對比比率、特別是 Dark 模式下
legacy.fg用黑底紅、需用 contrast checker 工具驗證真實對比 - §6.1 「DOWNGRADE」嚴格比對:Architect 42 §2.3 後端要求
confirmToken: "DOWNGRADE"、Design 對應 UI 強制使用者輸入字面字串、確認前後端規格一致 - §7.1 失敗類型對應:Design 列了 7 種失敗類型、對應 backend stage(
preparing / loading / flashing / verifying)+ 兩個橫切性失敗(timeout / disconnect)+FirmwareProgress.Reason細分原因(見 TDD §3.4)。確認所有後端可能 throw 的 error 都有對應 friendly message、沒有遺漏 - §8 狀態機:v2.2 三方對齊後、stage 命名以 Design
preparing / loading / flashing / verifying為 source of truth、Architect TDD §4.3 對齊此命名 - §11.2 token 命名:
color.fw-badge.*命名是否符合 Architect / Frontend 對 token 階層的期望(vs 用color.status.firmware-*等替代方案) §14.4 懸而未決第 6 點(已解):降版進行中關 Wails 控制台 = brick 風險。v2.2 三方對齊後解法:Architect TDD §8.6 補 server graceful shutdown 拒絕;Design 在v2/control-panel.md補 graceful shutdown 警告 modal UI。
15.3 互審回合預期(v2.2 補丁)
| 互審項 | 狀態 |
|---|---|
| Architect §11.2 顏色對比實測值 | 已解:實測責任歸 M9-12 Frontend、Design 提供推算值參考(§11.2 備註) |
| Architect §8 狀態機名稱對齊 | 已解:三方統一採 preparing / loading / flashing / verifying(Design §5.3 §8 / TDD §4.3 / i18n keys 全對齊) |
| Architect graceful shutdown 拒絕邏輯 | 已解:TDD §8.6 + Design v2/control-panel.md graceful shutdown modal |
| PM user story 覆蓋 / 用語策略 / 成功指標 | PM 在下次 PRD 補丁處理(見 PM review) |
| §7.1 失敗復原 4 vs 8 對齊 | 已解:對齊 backend FirmwareProgress.Reason、TDD §3.4 補完整 mapping |
三方互審 v2.2 已收斂、本檔可進 M9-12 Frontend 實作。
16. 文件大小與拆檔策略
本檔 v2.2 修訂後 ~930 行(含表格、wireframe、i18n table、v2.2 互審吸收紀錄),已逼近原訂的 800 行拆檔門檻、但尚未跨越 1000 行強制拆檔門檻。
判斷:v2.2 暫不拆。理由:
- v2.2 主要新增是吸收三方互審後的「對齊紀錄」(stage 命名、reason mapping、graceful shutdown 解法),不是新增獨立章節
- 拆檔反而會讓「升級流程」「切換流程」「失敗復原」三個密切相關的章節分散
- §9 i18n keys 佔 ~140 行、本身是一張長表、拆出去也不會增加可讀性
- Frontend 實作時需要對照所有章節(從 IA 到 i18n),單檔較便利
下次拆檔觸發:若本檔超過 1000 行(如 KL630/KL730 特殊降版流程落地、或補新的失敗情境),則拆為:
firmware-management.md(索引 + IA + 主流程,目標 ≤ 600 行)firmware-i18n.md(i18n keys 細表)firmware-error-recovery.md(失敗類型細表 + reason mapping)
下一步:
- 更新
design-spec-v2.md索引(§14.1 / §14.2 / §14.3 / §14.4) - Orchestrator 派 PM Agent 互審(依 §15.1)+ Architect Agent 互審(依 §15.2)
- 互審通過後、本檔 + design-spec-v2.md 一起進 git
- 進 M9-12 Frontend 實作前、Design 同步 confirm Architect 補 TDD
v2/device-firmware.md章節與本檔一致
變更紀錄
| 日期 | 版本 | 變更 | 作者 |
|---|---|---|---|
| 2026-05-24 | v2.2-draft | 初版(R5-Q9 翻案 + B2 階段降版設計) | Design Agent |
| 2026-05-25 | v2.2 | 吸收三方互審:(1) §5.3 / §8 / §9.6 / §9.8 stage 命名統一為 preparing/loading/flashing/verifying(A-MISMATCH-1);(2) §7.1 失敗類型對齊 backend FirmwareProgress.Reason 細分原因(A-MID-3);(3) §11.2 token 對比實測責任明示歸 M9-12 Frontend(A-MID-2);(4) §14.4 第 6 點 graceful shutdown 標已解、UI 落地至 v2/control-panel.md(A-MID-1);(5) §9 i18n keys 51 個(原 52 減 1);(6) §15.3 互審回合預期改為 v2.2 收斂狀態表 |
Design Agent |