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>
949 lines
62 KiB
Markdown
949 lines
62 KiB
Markdown
# 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 個分頁**,使用者在此:
|
||
|
||
1. 看到每張 Kneron dongle 當前 FW 版本 + bundled 最新版的對照
|
||
2. 升級到最新 FW(一鍵動作、KL520 KDP1→KDP2 為主要場景)
|
||
3. **切換 FW 版本(含降版)**——進階使用者操作,多層 safety guard 保護
|
||
4. 看升級 / 切換進度、失敗復原指引
|
||
|
||
**這個分頁不做的事**:
|
||
- 不做 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.primary` fill、`color.muted` track、圓角 `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` 背景、表身 padding `space.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`
|
||
- 「取消」按鈕:`ghost` `md`
|
||
- **可點外部關閉嗎?** → **不能**(捕獲事件、抖動 200ms 提示「請使用『取消』按鈕關閉」),ESC 可關閉(與外部點擊行為不同——ESC 是鍵盤使用者的明確意圖、外部點擊較可能誤觸)
|
||
|
||
### 6.2 切換到 KDP1 的額外警告
|
||
|
||
當目標版本是 KDP1(legacy)時,風險列表多加一條:
|
||
|
||
```
|
||
• KDP1 不支援多模型載入,部分 visionA-local 功能將無法使用
|
||
```
|
||
|
||
並在表格上方加紅色 banner:
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────┐
|
||
│ 🚫 KDP1 是已棄用的舊版韌體,建議僅用於 │
|
||
│ 與第三方工具相容性測試。 │
|
||
└──────────────────────────────────────────────────┘
|
||
```
|
||
|
||
- banner 背景 `color.destructive/10`、邊框 `color.destructive/30`、icon `color.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.mono` 12px、`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.info`
|
||
- `color.muted` / `color.muted-foreground` / `color.foreground`
|
||
- `color.card` / `color.background` / `color.border`
|
||
- `color.primary` / `color.primary-foreground`
|
||
- `color.ring`
|
||
- `radius.md` / `radius.lg` / `radius.full`
|
||
- `space.2` ~ `space.8`
|
||
- `font.size.xs` / `sm` / `base` / `lg`
|
||
- `font.weight.medium` / `semibold`
|
||
- `font.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` §文件結構 表格、加一行:
|
||
|
||
```markdown
|
||
| **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 差異總覽」表格新增段
|
||
|
||
```markdown
|
||
## 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 的問題(懸而未決)」新增
|
||
|
||
```markdown
|
||
**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)
|
||
|
||
---
|
||
|
||
**下一步**:
|
||
1. 更新 `design-spec-v2.md` 索引(§14.1 / §14.2 / §14.3 / §14.4)
|
||
2. Orchestrator 派 PM Agent 互審(依 §15.1)+ Architect Agent 互審(依 §15.2)
|
||
3. 互審通過後、本檔 + design-spec-v2.md 一起進 git
|
||
4. 進 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 |
|