依 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>
28 KiB
v2.1 — Wails Server Control Panel 設計規格
本章對應 R5-1 / R5-2 / R5-3 / R5-4 / R5-5 / R5-5a / R5-D1 / R5-D3 / R5-E(v2.1 補丁) 上層索引:
../design-spec-v2.md版本:v2.1 · 更新日期:2026-04-14 相關:v2/startup-progress.md(R5-E 階段化啟動進度面板,Starting state 時顯示)
1. 定位與職責
Wails Server Control Panel(以下稱「控制台」)是 visionA-local 雙擊開啟後看到的第一個畫面,也是使用者唯一可以關 server 的地方。它的職責:
- Server lifecycle 管理:Start / Stop / Restart
- 即時 log 顯示、過濾、複製、匯出
- 一鍵開啟瀏覽器 Web UI
- 顯示 server 狀態(port、PID、uptime、version)
- 錯誤狀態的視覺呈現與自助排除入口
控制台不做的事(與 v1 清單一致、R5 複核):
- 不管 device、model、inference(那是 Web UI 的事)
- 不顯示 Mock 模式切換(R5-5:拿掉 Mock 切換)
- 不提供語言切換(跟隨系統 locale,見 §9)
- 不提供 Dark Mode 切換(跟隨系統)
2. 視窗規格
| 項目 | 值 | 說明 |
|---|---|---|
| 預設寬度 | 720 px |
log 一行約 90-100 字元可顯示 |
| 預設高度 | 560 px |
log 區塊可顯示 18-20 行 |
| 最小寬度 | 560 px |
防止 primary controls 擠壞 |
| 最小高度 | 420 px |
log 區最少 6 行 |
| 可調整大小 | 是 | 拖角落縮放 |
| 最大化 | 保留 | |
| 最小化 | 保留 | |
| 置頂 | 否 | |
| Title bar | 原生 | 不做自訂 title bar |
| 視窗標題 | visionA-local · Server Control |
|
| 視窗 icon | 沿用 frontend/icon.png |
|
| 初始位置 | 螢幕中央(首次)/上次位置(後續) | 記憶寫到 ~/Library/Application Support/visiona-local/control-panel.json |
3. 佈局 Wireframe(最終版)
┌───────────────────────────────────────────────────────────────────┐
│ ● visionA-local · Server Control [ − ][ □ ][ × ]│ ← Title bar(原生)
├───────────────────────────────────────────────────────────────────┤
│ │
│ ┌────┐ visionA-local v0.1.0 │
│ │LOGO│ ● Running · Browser opened │ ← Header
│ └────┘ Port: 3721 Uptime: 00:12:43 PID: 45821 │
│ │
├───────────────────────────────────────────────────────────────────┤
│ │
│ [ 🌐 Open in Browser ] [ Start ] [ ⋯ Manage ▾ ] │ ← Primary controls
│ │
│ ☑ Follow tail ☑ Show timestamps 🔍 [ Filter ... ] │ ← Log controls
│ │
├───────────────────────────────────────────────────────────────────┤
│ 10:23:41 INFO HTTP server listening on 127.0.0.1:3721 │
│ 10:23:41 INFO wails ipc ready │
│ 10:23:42 INFO device scan: found 1 Kneron KL520 │
│ 10:23:43 INFO GET /api/devices 200 (4ms) │ ← Log panel
│ 10:23:45 INFO GET /api/models 200 (2ms) │ (等寬字體
│ 10:23:58 WARN python sidecar restart (attempt 1) │ 可捲動
│ 10:23:59 INFO python sidecar ready │ 等級著色)
│ 10:24:12 INFO inference session start: classification │
│ ... │
│ │
│ │
├───────────────────────────────────────────────────────────────────┤
│ [ Clear ] [ Copy ] [ Export log ] [ Open log folder ] │ ← Log actions
│ │
│ Lines: 142 / 2000 ⚠ Closing this window will stop │ ← Footer
│ the server. │
└───────────────────────────────────────────────────────────────────┘
和 v1 分析稿(design-analysis-round2-refactor.md §B2)的差異:
- 拿掉 log 控制列上方「Mock 模式切換」區(v1 分析稿其實沒有這個,只是
controlPanelSection清單有——R5-5a 確認砍) - Primary controls 從 4 顆精簡為 3 顆(
Start/Stop/Restart三顆合併為Start+ overflow menu) - Header 狀態列文字擴充,加入 "Browser opened"(首次 auto-open 後的視覺回饋,見 §5)
- Footer 新增「關閉視窗會停止 server」持久提示(R5-2 明確決策,用持久文字取代每次彈對話框)
4. 元件清單
4.1 Header(高度 80 px)
| 元素 | 類型 | 尺寸 / 位置 | 狀態 | 備註 |
|---|---|---|---|---|
| Brand logo | <img> |
40×40 px,左側 padding 16 | static | 沿用 frontend/icon.png |
| Product name | <h1> |
16 px / SemiBold | static | 文字:visionA-local |
| Version tag | <span> |
12 px / muted-foreground | static | 文字:v{major}.{minor}.{patch},右上角 |
| Status indicator | <span> + <svg> |
圓點 8 px | 見 §5 狀態機 | 顏色綁 semantic tokens |
| Status text | <span> |
14 px / Medium | 見 §5 | 例:Running · Browser opened |
| Server meta | <dl> |
12 px / muted | 6 個欄位 | Port / Uptime / PID(Uptime 每秒刷新) |
4.2 Primary controls(高度 48 px)
| 按鈕 | 變體 | 大小 | 啟用條件 | 備註 |
|---|---|---|---|---|
| Open in Browser | primary filled |
md |
僅 Running |
最左、最顯眼,附 🌐 icon |
| Start | outline |
md |
Stopped / Error |
附 ▶ icon |
| Manage ▾ | outline + dropdown |
md |
Running |
展開後包含 Stop server、Restart server |
Manage overflow menu 內容:
┌──────────────────────────┐
│ Stop server │ ← destructive 色彩提示
│ Restart server │ ← 普通
├──────────────────────────┤
│ Open log folder │ ← 重複項(方便直接存取)
└──────────────────────────┘
為什麼 Primary CTA 是 "Open in Browser"(Design Rationale):
- R5-4 決定首次啟動會自動開瀏覽器一次
- 使用者後續可能關 browser tab(環境整理、記憶體、誤關)
- 「關了想重開」是日常第二高頻操作(第一高頻是雙擊 app 本身,已被 auto-open 覆蓋)
- Start/Stop/Restart 只在出事時才點
- 結論:Open in Browser 保留為 primary(沿用第一輪 B3 提案)
Stop 放進 overflow 的原因:
- 避免誤按導致 server 中斷 + Web UI 爆掉
- Stop 放在 dropdown 多一個「點擊 > 選擇」保護,等效輕度確認
- 不做「你確定要 Stop?」modal,減少 UX 摩擦
4.3 Log controls(高度 40 px)
| 元素 | 類型 | 預設 | 行為 |
|---|---|---|---|
Follow tail |
<checkbox> |
✅ ON | 使用者往上捲動時自動關閉,捲到最底自動重啟。附提示 Jump to latest pill |
Show timestamps |
<checkbox> |
✅ ON | 關閉後 log 行去掉時間戳 |
Filter |
<input type="search"> |
空 | 即時字串過濾,無 regex;⌘F / Ctrl+F 聚焦 |
4.4 Log panel(高度剩餘 flex-grow)
| 屬性 | 值 |
|---|---|
| 字體 | font.mono(SF Mono / Consolas / Menlo) |
| 字級 | 12 px |
| 行高 | 1.5 |
| 背景 | color.surface-1(Light:oklch(0.99 0 0);Dark:oklch(0.18 0 0)) |
| 選取背景 | color.primary/20 |
| 最大行數 | 2000(ring buffer,超過舊的 drop;對齊 TDD v2 Go server 常數,~400KB 記憶體可忽略) |
| 寫檔 | 無(TDD v2 採 in-memory ring buffer,log 不落地;使用者若需保存用 Export log 手動匯出) |
| 滑入動畫 | 60 ms fade-in(prefers-reduced-motion 時關閉) |
| 選取冰結 | 使用者拖選文字時自動暫停 auto-scroll |
為什麼取消落地寫檔與 rotate:
- TDD v2 決定 Go server 採 in-memory ring buffer(容量 2000 行)統一管理 log,不落地滾動檔案
- rotate 7 天 / 10MB 需要
lumberjack.v2或自刻定時掃描 + size 比較,非 M8 scope 且會增加技術債 - 使用者如需保存 log →
Export log按鈕(§4.5)原生 save dialog 匯出當下 buffer 內容 - 使用者如需檢視/清理 →
Open log folder保留,指向<dataDir>/logs/(若未來重新啟用落地再用;目前該資料夾可能為空) - 未來若有落地需求 → 放 M9+ 迭代,不影響 v2.1 交付
等級著色(和 Web UI semantic token 一致):
| Level | Token | Light 範例 | Dark 範例 |
|---|---|---|---|
| DEBUG | color.muted-foreground |
#6b7280 |
#9ca3af |
| INFO | color.foreground |
#111827 |
#e5e7eb |
| WARN | color.warning |
#b45309 |
#fbbf24 |
| ERROR | color.destructive |
#b91c1c |
#f87171 |
4.5 Log actions(高度 40 px)
| 按鈕 | 類型 | 功能 |
|---|---|---|
Clear |
ghost small |
清空畫面 log(不動檔案),二次確認(toast「Log cleared」,5 秒內可 undo) |
Copy |
ghost small |
複製全部可見 log 到剪貼簿,首次點擊時提示「Log 可能包含檔名與裝置資訊」一次 |
Export log |
ghost small |
原生 save dialog,預設檔名 visiona-local-{yyyyMMdd-HHmmss}.log |
Open log folder |
ghost small |
呼叫 OS 開 ~/Library/Application Support/visiona-local/logs/ |
4.6 Footer(高度 32 px)
| 元素 | 位置 | 文字 / 樣式 |
|---|---|---|
| 行數統計 | 左 | Lines: {current} / 2000,12px muted |
| 關閉提示 | 右 | ⚠ Closing this window will stop the server.,12px muted |
持久提示為什麼不彈 modal(R5-2 解釋):
- 使用者已明確決策「關閉 = 結束 server」
- 每次關都彈 modal 只會煩,且使用者按過幾次就會盲目點「確定」失去意義
- 持久 footer 文字是「被動告知」而非「主動打斷」,符合 Jakob Nielsen 錯誤預防原則
- 使用者如果真的不想關,看到 footer 提示就會改按最小化
- 若使用者仍誤關,下次開啟 (R5-4 自動起 server + 自動開瀏覽器) 只需 3-5 秒就回到原狀,損失可控
5. Server 狀態機(五態視覺化)
5.1 狀態定義(v2.1 修訂)
| State | 觸發條件 | 持續時間 |
|---|---|---|
Starting |
控制台剛開啟 / 使用者按 Start / Restart 過程中 | 通常 4-15 秒,上限 60 秒(R5-E1) |
Running |
6 階段全部完成(含 WebSocket 連上,R5-E6) | 主要 state |
Stopping |
使用者按 Stop / 視窗關閉中 | 通常 <2 秒 |
Stopped |
Stop 完成、尚未 Restart | 空 state |
Error |
啟動階段:任一階段超時 20 秒進 Retry 提示;總計超過 60 秒(R5-E4)仍未就緒 → Error 運行階段: /api/health 連續失敗達閾值 / sidecar crash 超過 auto-restart 上限 |
停留直到使用者介入 |
5.2 視覺對照表(v2.1 修訂)
| State | 圓點顏色 | Icon | Status text 範例 | 附加元素 |
|---|---|---|---|---|
| Starting | color.warning 琥珀 |
旋轉 spinner | Starting · Stage {n}/6 |
log panel 上方浮出「啟動進度面板」(見 v2/startup-progress.md)Primary controls 全部 disabled |
| Running | color.success 綠 |
— | Running 或 Running · Browser opened(Toggle ON 首次顯示 10 秒) |
啟動進度面板 fade-out;Open in Browser enabled |
| Running(自動開瀏覽器瞬間) | color.success 綠 |
→ 淡入 ✓ icon 2 秒 → fade out | Running · Browser opened 持續 10 秒後自動變回 Running |
— |
| Stopping | color.warning 琥珀 |
旋轉 spinner | Stopping... |
所有 primary controls disabled |
| Stopped | color.muted-foreground 灰 |
— | Stopped |
只有 Start 按鈕可按 |
| Error | color.destructive 紅 |
⚠ | Error: {簡短原因} |
見 §6 錯誤面板;若從啟動階段進入 Error,啟動進度面板切換為 Error 狀態(見 startup-progress.md §5) |
5.3 狀態轉場動畫
- 圓點顏色過渡:300 ms ease-out
- Spinner 旋轉:1 s linear infinite
Running · Browser opened出現:fade + slide-in-left 200 ms,停留 10 秒,fade-out 200 msprefers-reduced-motion: reduce→ 全部動畫降為 0 ms 跳變
6. Error State 面板(R5 共識)
當 Server 進入 Error 時,控制台 log panel 上方(介於 log controls 和 log panel 中間)浮出一個 error banner,log 面板不消失。
6.1 Wireframe
├───────────────────────────────────────────────────────────────────┤
│ ☑ Follow tail ☑ Show timestamps 🔍 [ Filter ... ] │
├───────────────────────────────────────────────────────────────────┤
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ ⚠ Server failed to start │ │
│ │ │ │
│ │ Python sidecar exited with code 1 after 3 retries. │ │
│ │ Last error: ModuleNotFoundError: kneron_plus │ │
│ │ │ │
│ │ [ Restart Server ] [ View log details ↓ ] [ Report... ] │ │
│ └───────────────────────────────────────────────────────────────┘ │
├───────────────────────────────────────────────────────────────────┤
│ 10:24:12 ERROR python sidecar exited code=1 │ ← Log panel 照常顯示
│ 10:24:13 ERROR ... │
│ ... │
├───────────────────────────────────────────────────────────────────┤
6.2 元件
| 元素 | 類型 | 細節 |
|---|---|---|
| Banner 容器 | <div role="alert"> |
背景 color.destructive/10,邊框 1px color.destructive/30,圓角 radius.md,padding 16 |
| 警告 icon | <svg> |
20×20,color.destructive |
| 標題 | <strong> |
14 px SemiBold,color.destructive |
| 說明 | <p> |
13 px,color.foreground,最多 2 行,溢出 … |
Restart Server |
Button primary sm |
點擊 → 呼叫內部 restart,banner 轉為 Starting state |
View log details ↓ |
Button ghost sm |
點擊 → 自動捲動 log panel 到最後一條 ERROR 行並 flash 2 次 |
Report... |
Button ghost sm |
【hold】 現階段先不實作。待 PM 提供 GitHub Issue repo URL 後再恢復。Design 原意:開預設瀏覽器到 GitHub Issue 新增頁面,預填錯誤摘要 + 環境資訊(version、OS、最後 20 行 log,不含檔名 / 裝置 serial) |
R5-D1 落地(OS 原生通知並存):
Error state 進入時,除了控制台 log panel 上方的 Error banner(role="alert" 由 Wails WebView 內部顯示)另外發送一次 OS 原生 non-blocking 通知。這是 R5-D1 使用者決策:控制台可能被最小化、或在另一個桌面 / 虛擬桌面,使用者不一定會看到 banner,OS 通知作為次要冗餘提醒仍有價值。
| 平台 | 通知機制 | Fallback |
|---|---|---|
| macOS | osascript -e 'display notification "..." with title "visionA-local"'(toast 非 dialog) |
— |
| Windows | wailsRuntime.SendNotification(優先) |
msg * 命令列 |
| Linux | notify-send |
zenity --notification |
行為細節:
- 通知內容:
標題 = visionA-local Server Error/內文 = {error.title}: {error.description 前 60 字} - non-blocking:不阻塞 UI,不彈 modal;與先前 v1 的
showNativeError()(給 startup 致命錯誤用的 modal dialog)區分 - 不重複發:同一次 Error state 只發一次通知,使用者按 Restart Server 或 State 變回 Starting 後才重置「本次已發」flag
- 技術實作:新增 Go 檔案
visiona-local/notify.go,函式sendCrashNotification(title, body string) error;對應 TDD v2control-panel.md §4.7
6.3 Dismiss 條件
Banner 不可手動關閉(避免使用者忽略問題)。只有下列條件自動消失:
- 使用者按
Restart Server且 server 成功進入Running - 使用者手動修復環境後按 Start 成功
7. 啟動行為(對應 R5-4 / R5-D3 / R5-E)
7.1 預設流程(v2.1 修訂)
v2.1 重要變更:Starting 狀態下控制台顯示階段化啟動進度面板(見 v2/startup-progress.md),不是只有一顆 spinner。下述「step」對應進度面板的 6 個階段。
1. 使用者雙擊 visionA-local.app
↓
2. 控制台視窗開(螢幕中央 / 上次位置)
↓
3. 控制台進入 Starting 狀態,log panel 上方顯示「啟動進度面板」
→ 階段 1:初始化控制台
→ 階段 2:檢查 Python runtime 與驅動
→ 階段 3:啟動本機伺服器(等 /api/health 200)
→ 階段 4:偵測 Kneron 裝置
↓
4. Server ready(階段 3 完成訊號 = /api/health 200)
↓
5. 【每次 / Settings 為 ON(macOS/Windows 預設;Linux 預設 OFF)】
階段 5「開啟瀏覽器」觸發 OS open browser
- macOS: `open http://127.0.0.1:3721/`
- Windows: `start http://127.0.0.1:3721/`
- Linux: `xdg-open http://127.0.0.1:3721/`
【Toggle OFF 時】階段 5 標記為「跳過(依偏好設定)」,不執行 OS open,但仍推進
↓
6. 階段 6:等待 Web UI 連線
(等 WebSocket hub 收到第一個 client 連線,R5-E6 決策)
【Toggle OFF 時】此階段改為「等待使用者手動點擊『在瀏覽器開啟』」
↓
7. 所有 6 階段完成
→ 啟動進度面板淡出(fade-out 200 ms)
→ Status: Running
→ Status text 顯示 `Running · Browser opened` 10 秒(Toggle ON 時)
或純 `Running`(Toggle OFF 時,使用者尚未手動 Open)
控制台留在背景(不最小化、不關閉)
↓
8. 瀏覽器 tab 進入 Next.js First-Run wizard(見 v2.4)
R5-D3 重點:每次啟動(每次 Wails App process 新啟動)都會跑完整 6 階段流程並觸發 OS open,不是只有首次。Restart Server(同一個 Wails process 內重啟 server)不會重開瀏覽器 tab — 由 Offline Overlay 的自動重連處理(見 v2/server-offline-overlay.md)。
7.2 視覺回饋
第 6 步的 Running · Browser opened 是使用者看到控制台第一個確認 server 就緒的訊號。具體視覺:
- Status dot 綠色
- Status text 後方 fade-in 一個 ✓ icon(
color.success) - Text 改為
Running · Browser opened - 10 秒後 ✓ icon 淡出,text 縮為
Running
7.3 例外情境
| 情境 | 控制台行為 |
|---|---|
Server Starting 超過 5 秒 |
進入 Error state(見 §6),不開瀏覽器 |
| Port 3721 被佔 | Server fallback 到 3722 / 3723,Header 顯示 Port: 3722 (default 3721 in use),瀏覽器開的 URL 同步換 |
| Settings「自動開瀏覽器」= OFF | Server Running 後不做 auto-open,使用者需手動點 Open in Browser |
8. 深色模式處理
控制台深色模式與 Web UI 同步,機制:
- 讀取 OS 偏好:控制台是 Wails WebView,直接用 CSS
prefers-color-scheme - CSS 變數切換:和
frontend/src/app/globals.css用一樣的:root/[data-theme='dark']block - 不提供手動切換(v1 決策延續)
Dark 下額外考量:
- Log panel 背景
oklch(0.18 0 0)(比 surface 再暗 5%,模仿 terminal) - ERROR 紅色在 dark 下用
oklch(0.72 0.19 25)(避免過亮刺眼) - 圓點狀態色全部用 dark variant,確保 4.5:1 對比(R4-3 降為盡力而為,但狀態色這種 critical 信號仍維持嚴格)
9. i18n key 清單(新元件)
控制台文字走 desktop-control namespace,獨立於 Next.js Web UI 的 i18n 檔(但抽自同一份辭典,避免兩處維護)。
9.1 新增 key(zh-TW / en)
| Key | zh-TW | en |
|---|---|---|
control.title |
visionA-local · 伺服器控制台 | visionA-local · Server Control |
control.status.starting |
啟動中... | Starting... |
control.status.running |
執行中 | Running |
control.status.runningBrowserOpened |
執行中 · 已開啟瀏覽器 | Running · Browser opened |
control.status.stopping |
停止中... | Stopping... |
control.status.stopped |
已停止 | Stopped |
control.status.error |
錯誤:{reason} | Error: {reason} |
control.meta.port |
連接埠 | Port |
control.meta.portFallback |
連接埠:{port}(預設 {default} 被佔用) | Port: {port} (default {default} in use) |
control.meta.uptime |
執行時間 | Uptime |
control.meta.pid |
程序 ID | PID |
control.meta.version |
版本 | Version |
control.action.openBrowser |
在瀏覽器開啟 | Open in Browser |
control.action.start |
啟動 | Start |
control.action.manage |
管理 | Manage |
control.action.stopServer |
停止伺服器 | Stop server |
control.action.restartServer |
重新啟動伺服器 | Restart server |
control.log.followTail |
自動跟隨最新 | Follow tail |
control.log.showTimestamps |
顯示時間戳 | Show timestamps |
control.log.filterPlaceholder |
過濾 log... | Filter... |
control.log.jumpToLatest |
跳到最新 | Jump to latest |
control.log.clear |
清空 | Clear |
control.log.clearToast |
已清空 log(可復原) | Log cleared (undo) |
control.log.copy |
複製 | Copy |
control.log.copyPrivacyHint |
Log 可能包含檔名與裝置資訊,請注意分享對象 | Log may contain filenames and device info. Share with care. |
control.log.export |
匯出 log | Export log |
control.log.openFolder |
開啟 log 資料夾 | Open log folder |
control.log.lines |
行數:{current} / {max} | Lines: {current} / {max} |
control.footer.closeWarning |
⚠ 關閉此視窗會停止伺服器 | ⚠ Closing this window will stop the server |
control.error.title |
伺服器無法啟動 | Server failed to start |
control.error.description |
{具體原因} | {reason} |
control.error.restartButton |
重新啟動伺服器 | Restart Server |
control.error.viewLogDetails |
檢視 log 詳情 | View log details |
control.error.reportButton |
回報問題... | Report... |
9.2 刪除 key(從現有 Next.js i18n 砍)
見 v2/source-selector-update.md §3.2。
10. 無障礙考量
| 項目 | 設計 |
|---|---|
| Keyboard navigation | 所有 interactive 元素 tabindex 合理序列:Open in Browser → Start/Manage → Follow tail → Show timestamps → Filter → (log panel 可選取) → Clear → Copy → Export → Open folder |
| Focus ring | 沿用 Web UI token ring.2 · color.primary,2 px outline-offset |
| Keyboard shortcut | ⌘F / Ctrl+F 聚焦 filter;⌘C / Ctrl+C 複製選取 log;⌘W / Ctrl+W 關視窗(R5-2:結束 server) |
⌘Q |
macOS 原生結束 app:停 server + quit |
| Screen reader | Status indicator <span role="status" aria-live="polite">;Error banner <div role="alert">;log panel <output aria-live="polite" aria-atomic="false">(新行 append) |
| ARIA label | 所有 icon button 有 aria-label(例如 Follow tail checkbox 的 trailing spinner 有 aria-label="Auto-scrolling enabled") |
| 色彩對比 | Status dot / ERROR level log / Error banner 強制 ≥ 4.5:1(即使 A11y 整體降級為「盡力而為」,critical 信號不妥協) |
| Reduced motion | prefers-reduced-motion: reduce → 關閉 spinner 旋轉(改為靜態點)、關閉 log 滑入動畫、關閉 Browser opened fade |
| 字級可縮放 | 使用 rem 而非 px 定義字級,支援 OS 字級偏好 |
11. 與 v1(design-analysis-round2-refactor.md)的差異
| 面向 | v1 分析稿 | v2 正式規格 |
|---|---|---|
| 視窗職責 | 三方尚在討論 | 確定為雙 UI(R5-1) |
| 關閉行為 | 待 D1 決策 | 關閉 = 結束 server,footer 持久提示(R5-2) |
| Tray | 建議復活 tray | 不做(R5-3) |
| 首次啟動 | 建議自動開瀏覽器 | 採納自動開瀏覽器(R5-4) |
| Primary controls | 4 顆 | 3 顆(Stop/Restart 併入 Manage 下拉) |
| Header 狀態列 | 固定 Running |
首次啟動後動態 Running · Browser opened 10 秒 |
| Error 狀態視覺 | 未設計 | 新增 Error banner(§6) |
| Mock 切換 | 未納入 scope | 明確砍除(R5-5a) |
| Log 上限 | 1000 行 | 1000 行(維持) |
| Log 寫檔 | 7 天 / 10MB rotate | 維持 |
| 語系 | 跟隨系統 | 跟隨系統(維持) |
12. v2 → v2.1 Diff(2026-04-14)
| # | 位置 | v2 | v2.1 | 來源 |
|---|---|---|---|---|
| 1 | §4.4 Log panel 最大行數 | 1000 行 | 2000 行(對齊 TDD v2 ring buffer) | Architect Review Minor m-1 |
| 2 | §4.4 Log 寫檔 | rotate 7 天 / 10 MB | 無落地寫檔(in-memory ring buffer,使用者透過 Export log 手動匯出) | Architect Review Minor m-12 |
| 3 | §4.6 Footer 行數顯示 | Lines: {current} / 1000 |
Lines: {current} / 2000 |
Architect Review Minor m-1 |
| 4 | §5 狀態機 | Starting 只有 spinner,1-5 秒 | Starting 顯示階段化啟動進度面板,4-15 秒(上限 60 秒,R5-E1) | R5-E |
| 5 | §6.2 Error banner | Report... 正常按鈕 |
【hold】 待 PM 提供 GitHub Issue repo URL 後再恢復 | Architect Review Minor m-11 / G-3 |
| 6 | §6.2 新增 | — | R5-D1 OS 原生通知並存(Error state 發 non-blocking toast notification,不是 modal) | R5-D1 / Architect Review Minor m-4 |
| 7 | §7.1 第 5 步 | 「首次 / Settings 為 ON」 | 「每次 / Settings 為 ON」,新增 Linux 預設 OFF 說明,流程改為 6 階段化 | R5-D3 + R5-E |
| 8 | §7.1 新增 | — | 引用新檔 v2/startup-progress.md(R5-E 階段化啟動進度面板) |
R5-E |
下一步:交 M8-5 Frontend Agent 實作(Wails 控制台 + 啟動進度面板),交 Reviewer 審查 control-panel.md + startup-progress.md 整體一致性。