visionA/local-tool/.autoflow/03-design/v2/settings-update.md
jim800121chen 8cd5751ce3 feat(local-tool): M8 重構 — Wails 控制台 + 瀏覽器 Web UI(R5 決策)
依 R5 五輪決策把 visionA-local 從「Wails 內嵌 Next.js」重構為「Wails
本機伺服器控制台 + 瀏覽器 Web UI」模式(類比 Docker Desktop / Ollama)。

程式碼變動
  - M8-1 砍 yt-dlp 全套(後端 resolver / URL handler / 前端 URL tab /
    Makefile vendor / installer / bootstrap / CI workflow,-555 行)
  - M8-2 砍 Mock 模式全套(driver/mock、mock_camera、Settings runtimeMode、
    VISIONA_MOCK 環境變數,-528 行)
  - M8-3 ffmpeg 從 GPL 切換到 LGPL 混合方案:Windows/Linux 用 BtbN 現成
    LGPL binary,macOS 自 build minimal decoder-only 進 git
    (vendor/ffmpeg/macos/ffmpeg 5.7MB + ffprobe 5.6MB,比 GPL 版省 85% 空間)
  - M8-4 Wails Server Controller:state machine、log ring buffer 2000 行、
    preferences.json atomic write、boot-id、Gin SkipPaths、shutdown 7+1 秒、
    notify_*.go 三平台 OS 通知、watchServer 改 Error state 不 os.Exit
  - M8-4b 啟動階段管線 R5-E:6 階段進度 event、20s soft / 60s hard timeout、
    stage 5/6 skip 規則、sentinel file、RestartStartupSequence 5 步驟
  - M8-5 Wails 控制台 vanilla HTML/JS/CSS(9 檔 ~2012 行)取代 M7-B splash:
    state 視覺、log panel、startup progress panel、Stage 6 manual CTA
    pulse、shutdown modal、Settings、Dark Mode、i18n 中英雙語
  - M8-6 上傳影片副檔名擴充(mp4/avi/mov/mpeg/mpg)
  - M8-7 Web UI Server Offline Overlay(role=alertdialog + focus trap +
    wsEverConnected 容錯 + Page Visibility)
  - M8-8 CORS middleware(127.0.0.1/localhost only + suffix attack 防護)+
    ws/origin.go 獨立 WebSocket CheckOrigin 避 package cycle
  - MAJ-4 server:shutdown-imminent WebSocket broadcast 機制
    (/ws/system endpoint + notifyShutdownImminent helper)
  - M8-9 Boot-ID + 瀏覽器 tab 自動重連(sessionStorage loop guard)

品質
  - ~105+ 新 unit test + race detector (-count=2) 全綠
  - 10 個 milestone 全部通過 Reviewer 審查
  - 三方 v2 + v2.1 文件(PRD / Design Spec / TDD)+ 交叉互審紀錄
    收錄在 .autoflow/

交付前待處理(M8-10)
  - 重跑 make payload-macos 把舊 GPL 77MB binary 換成新 LGPL
  - 三平台 end-to-end build 驗證

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 17:57:54 +08:00

240 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# v2.5 — Settings 頁更新
> 本章對應 R5-4Settings 新增自動開瀏覽器 toggle+ R5-5a砍 Mock 模式相關設定)+ R5-D2Linux 預設 OFF+ R5-D3每次啟動都自動開+ R5-E階段化啟動進度
> 上層索引:`../design-spec-v2.md`
> 版本:**v2.1** · 更新日期2026-04-14
---
## 1. 變更摘要
Settings 維持 v1 的 **4 個分頁** 結構R4 第四輪決策確認):
| Tab | v1 內容 | v2 變更 |
|-----|---------|---------|
| 一般 | 語言、深色模式狀態(唯讀) | **新增**「首次啟動時自動開啟瀏覽器」toggle |
| 硬體 | 真實 / Mock 切換、裝置掃描策略 | **刪除** Mock 相關,只保留裝置掃描策略 |
| 模型 | 預置模型清單、上傳、自訂 | 不變 |
| 進階 | 資料目錄、Python 雙策略、清 log 等 | 不變 |
---
## 2. 「一般」分頁新增項目
### 2.1 結構
```
┌─ Settings > 一般 ───────────────────────────────────────────┐
│ │
│ 語言 │
│ ┌───────────────────────────┐ │
│ │ 繁體中文 ▾ │ │
│ └───────────────────────────┘ │
│ │
│ 深色模式 │
│ 跟隨系統(目前:深色) │
│ │
│ ── 啟動行為 ──────────────────────────────────────── │
│ │
│ 首次啟動時自動開啟瀏覽器 [■□] ON │
│ 啟動 visionA-local 時自動在預設瀏覽器開啟 Web UI。 │
│ 關閉此選項後,你需要手動點控制台的「在瀏覽器開啟」按鈕。 │
│ │
└──────────────────────────────────────────────────────────────┘
```
### 2.2 Toggle 規格
| 屬性 | 值 |
|------|----|
| 元件類型 | `<Switch>`shadcn |
| **預設值(依平台)** | **macOS / Windows = `ON`****Linux = `OFF`**R5-D2 |
| i18n key | `settings.general.autoOpenBrowser.label` / `settings.general.autoOpenBrowser.description` |
| 落地檔案 | **`preferences.json` 位於 `<dataDir>/`**(對齊 TDD v2<br>路徑:<br>• macOS `~/Library/Application Support/visiona-local/preferences.json`<br>• Windows `%APPDATA%\visiona-local\preferences.json`<br>• Linux `~/.local/share/visiona-local/preferences.json` |
| 儲存機制 | **Go server 端負責讀寫**`visiona-local/preferences.go`,非 Wails 內建機制 — Wails v2 沒有 settings store<br>JSON 格式:`{"openBrowserOnStart": true}`<br>原子寫入:**write-rename pattern**`os.WriteFile(path+".tmp", ...)``os.Rename(path+".tmp", path)`POSIX / Windows 均原子 |
| 讀取失敗 fallback | 呼叫 `DefaultPreferences()`,依 `runtime.GOOS` 回傳平台預設:<br>`Preferences{OpenBrowserOnStart: runtime.GOOS != "linux"}` |
| 生效時機 | 立即生效,不需 restart app |
| 影響對象 | 控制台的「每次啟動自動開瀏覽器」邏輯(見 `v2/control-panel.md §7` |
**Linux 首次使用說明R5-D2 落地)**
Linux 使用者第一次開 Wails 控制台時Settings > 一般 的「啟動時自動開啟瀏覽器」toggle **預設是關的**。原因是 Linux 桌面環境差異大,`xdg-open` 在無頭環境 / i3 / xmonad / 極簡 WM 下行為可能異常(例如無預設瀏覽器、或開到錯的 DE session。Linux 使用者若確認自己的環境可以正常 `xdg-open`,可手動打開此 toggle下次啟動就會自動開瀏覽器。
關閉此 toggle 時,**啟動進度面板仍然會顯示**(見 `v2/startup-progress.md`),只是第 5 階段「開啟瀏覽器」會被標記為 `跳過(依偏好設定)/ Skipped (per preference)`,第 6 階段「等待 Web UI 連線」改為「等待使用者手動點擊『在瀏覽器開啟』」— 視覺上仍會收尾至 Running state只是由使用者主動觸發第 5 階段(對齊 R5-E 階段化體驗)。
### 2.3 重要細節R5-D3 已定案)
**這個 toggle 同時影響「首次」和「每次」啟動嗎?**
**R5-D3 決策****每次**啟動(含每次 `StartServer` 成功後都會自動開瀏覽器不是只有首次。R5-4 原字面「首次啟動」已於 R5-D3 修正。精確行為:
- **Toggle ONmacOS / Windows 預設)**:每次雙擊 app 或每次 Start Server 成功後,控制台自動呼叫 OS open browser
- **Toggle OFFLinux 預設)**:啟動時只開控制台,不開瀏覽器;使用者自行點控制台的「在瀏覽器開啟」按鈕
- **Restart Server 情境**:因為 Restart 是同一個 Wails App process 內重啟R5 雙 UI 架構下 Wails 不關),且啟動進度面板只在 Wails 剛啟動時顯示一次Restart 時瀏覽器 tab 應由 Offline Overlay 自動重連,不會再開新 tab避免開一堆重複 tab
**Label 定版**:「啟動時自動開啟瀏覽器」/ "Auto-open browser on startup"(去掉「首次」二字)
| 版本 | label | description |
|------|-------|-------------|
| 原 R5-4 字面 | 首次啟動時自動開啟瀏覽器 | — |
| **最終採用R5-D3 定案)** | 啟動時自動開啟瀏覽器 | 啟動 visionA-local 時自動在預設瀏覽器開啟 Web UI。關閉此選項後你需要手動點控制台的「在瀏覽器開啟」按鈕。 |
---
## 3. 「硬體」分頁變更
### 3.1 刪除項目R5-5a
**所有 Mock 模式相關設定全砍**
| v1 項目 | 說明 | v2 處理 |
|---------|------|---------|
| `模式:真實硬體 / Mock` radio | 切換整個 app 推論來源 | **刪除** |
| `Mock 模式描述` 說明文字 | 介紹 Mock 用途 | **刪除** |
| `Mock 模式假資料速率` slider | 調 Mock 輸出速率 | **刪除** |
| `Mock 模式裝置名稱` input | 自訂假裝置名 | **刪除** |
| `切換模式會重啟推論 session` 警告 | 切換時的提醒 | **刪除** |
### 3.2 保留項目
| 項目 | 說明 |
|------|------|
| 裝置自動掃描頻率 | 每次啟動時 / 手動 / 關閉 |
| USB 熱插拔偵測 toggle | 自動偵測新裝置插入 |
| Kneron KL520 / KL720 韌體版本顯示 | 唯讀 |
### 3.3 新增項目
無。R5-5a 砍 Mock 後這個 tab 變得更簡潔。
### 3.4 Tab 是否保留?
因為「硬體」tab 被清空後仍有內容(掃描頻率、熱插拔、韌體版本),**tab 結構保留**,不合併也不刪除。
---
## 4. 「模型」與「進階」分頁
**完全不動**。R5 沒有觸及這兩個 tab 的內容。
---
## 5. i18n key 異動
### 5.1 新增 key
| Key | zh-TW | en |
|-----|-------|----|
| `settings.general.sectionStartup` | 啟動行為 | Startup behavior |
| `settings.general.autoOpenBrowser.label` | 啟動時自動開啟瀏覽器 | Auto-open browser on startup |
| `settings.general.autoOpenBrowser.description` | 啟動 visionA-local 時自動在預設瀏覽器開啟 Web UI。關閉此選項後你需要手動點控制台的「在瀏覽器開啟」按鈕。 | Automatically open the Web UI in your default browser when visionA-local starts. When off, you need to manually click "Open in Browser" in the Control Panel. |
### 5.2 刪除 key
執行 grep 找出所有 mock 相關 key
```
grep -rE "mock|Mock" frontend/src/lib/i18n/
```
預期刪除(實際清單以 grep 為準):
| Key | 原值zh-TW |
|-----|-------------|
| `settings.hardware.mode.label` | 模式 |
| `settings.hardware.mode.real` | 真實硬體 |
| `settings.hardware.mode.mock` | Mock |
| `settings.hardware.mode.description` | 選擇 visionA-local 的執行模式 |
| `settings.hardware.mock.rate.label` | Mock 速率 |
| `settings.hardware.mock.deviceName.label` | Mock 裝置名稱 |
| `settings.hardware.modeSwitchWarning` | 切換模式會重啟推論 session |
| `firstRun.mode.*` | 全部(見 `v2/first-run-update.md §5.2` |
| `devices.mockBadge` | Mock | (若存在於 Devices 頁面的 Mock 標籤)
| `dashboard.tryMockHint` | 或切換到 Mock 模式試用 | (若 Dashboard 空狀態有這句)
### 5.3 type 檔同步
`frontend/src/lib/i18n/types.ts` 對應 interface 欄位全砍,確保 TypeScript 編譯不留 dangling reference。
---
## 6. 清理殘留的 Mock 程式碼
Settings 改完後,前後端其他地方可能仍有 Mock 殘留:
### 6.1 前端
- `frontend/src/stores/` 任何 `mock` state / action
- `frontend/src/components/` 任何 `Mock` badge / pill
- `frontend/src/app/devices/` 頁面右上的「真實 / Mock」切換v1 `design-spec.md §第三輪修訂後仍待確認 4.` 提到)
### 6.2 後端
- Go server 任何 mock handler、假資料 generator
- Python sidecar 的 mock inference path
**這部分非 Design 的 scope**,但 Design 提醒 Orchestrator**R5-5a 的「完全砍除」不只是 UI 層面,後端也要同步砍**。交 Architect Agent 評估實作影響。
---
## 7. 遷移策略Migration
**使用者情境**v1 版本沒有 `preferences.json`Mock 模式相關設定(若存在)是記在前端 localStorage 或 v1 舊式 config不在 Go 端的 preferences 檔裡。v2 改用 Go 端 `preferences.json` 管理「是否啟動時自動開瀏覽器」,是**全新的檔案**。
**v2 首次讀取 preferences.json 流程**
1. 檢查 `<dataDir>/preferences.json` 是否存在
2. **不存在** → 呼叫 `DefaultPreferences()`(依 `runtime.GOOS` 回傳平台預設macOS/Windows `OpenBrowserOnStart=true`Linux `OpenBrowserOnStart=false`
3. **存在但解析失敗**JSON 毀損等)→ 同樣 fallback 到 `DefaultPreferences()`,並發 log WARN不擋啟動
4. **存在且解析成功** → 使用使用者設定
5. 使用者在 Settings 點擊 toggle → write-rename atomic pattern 寫回 `preferences.json`
**v1 → v2 無 migration 成本**:因為 v1 沒有對應檔案v2 啟動時若 `preferences.json` 不存在就建立新檔(或延遲到使用者第一次改 toggle 才寫),行為是**靜默**的,使用者不需要知道。
**Mock 模式殘留**v1 localStorage 可能有 mock 相關 keyFrontend 清理見 §6.1;與 `preferences.json` 無關。
---
## 8. 無障礙考量
| 項目 | 設計 |
|------|------|
| Toggle 元件 | 沿用 shadcn `<Switch>`,已內建 `role="switch"` / `aria-checked` |
| Tab 切換 | 沿用現有 Tabs 元件,`role="tablist"` / `role="tab"` / `role="tabpanel"` |
| Keyboard | `←` / `→` 切換 tab、`Tab` 進入 tab panel 內部 |
| 分區標題 | 「啟動行為」區用 `<h3>` 讓 screen reader 感知結構 |
---
## 9. 與 v1 差異對照
| 面向 | v1 | v2 |
|------|----|----|
| 一般 tab 項目數 | 2語言、深色模式 | **3**(新增 auto-open browser |
| 硬體 tab 項目數 | 5+(含 Mock 相關) | 3只剩真實硬體相關 |
| Mock 模式 UI | Settings + Devices pill + First-Run Step | **全砍** |
| 模型 / 進階 tab | — | 不變 |
---
## 10. v2 → v2.1 Diff2026-04-14
| # | 位置 | v2 | v2.1 | 來源 |
|---|------|----|----|------|
| 1 | §2.2 落地檔案 | `settings.json` + 「走 Wails 既有 settings store」 | **`preferences.json` @ `<dataDir>/`** + Go server 讀寫 + write-rename atomic | Architect Review Major 1 |
| 2 | §2.2 預設值 | 三平台一致 `ON` | **macOS/Windows = `ON`Linux = `OFF`**(依 `DefaultPreferences()``runtime.GOOS` | R5-D2 / Architect Review Major 2 |
| 3 | §2.2 新增 | — | Linux 首次使用說明toggle 預設關、關閉時啟動進度面板仍顯示、第 5/6 階段跳過)| R5-D2 + R5-E |
| 4 | §2.3 標題 | 「重要細節」待 confirm | **「R5-D3 已定案」每次啟動都開瀏覽器**label 定版 | R5-D3 |
| 5 | §7 遷移策略 | 假設 v1 有 `settings.json` + mock key 遷移 | 澄清 `preferences.json` 是**全新檔**,無 v1 migration 成本 | Architect Review Major 1 衍生 |
---
## 11. 給 Orchestrator 的提醒
1. ~~**R5-4 的 label 字面歧義**~~**已於 R5-D3 定案**每次啟動都開label 去掉「首次」)
2. **後端 Mock 清理**不在 Design scope需轉給 Architect / Backend Agent
3. **Devices 頁面的 Mock pill**v1 `design-spec.md` 第三輪修訂 §4要一併砍此變更未在本文件細寫但屬 R5-5a 範圍
4. **Linux 預設 OFF 對 E2E 測試的影響**testing Agent 在 Linux CI 上跑自動化時要注意,啟動後不會 auto-open需手動模擬點擊「在瀏覽器開啟」或在測試前將 `preferences.json` 改為 `{"openBrowserOnStart": true}`
---
**下一步**:交 Frontend Agent 更新 Settings 頁面。