jim800121chen c54f16fca0 Initial commit: visionA monorepo with local-tool subproject
local-tool/: visionA-local desktop app
- M1: Wails shell + Go server + Next.js UI + Mock mode (macOS dmg ready)
- M2: i18n (zh-TW/en) + Settings 4-tab refactor
- M3: Embedded Python 3.12 runtime (python-build-standalone) + KneronPLUS wheels
- M4: Windows Inno Setup script (build on Windows runner)
- M5: Linux AppImage script + udev rule (build on Linux runner)
- M6: ffmpeg (GPL, pending legal review) + yt-dlp bundled
- Lifecycle: watchServer health check, fatal native dialog,
            Wails IPC raise endpoint, stale process cleanup

.autoflow/: full PRD / Design Spec / Architecture / Testing docs
            (4 rounds tri-party discussion + cross review)
.github/workflows/: macOS / Windows / Linux build CI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 22:10:38 +08:00

13 KiB
Raw Blame History

08 — 錯誤狀態與空狀態設計方向

8.1 錯誤狀態分級

級別 範例情境 視覺語彙 UX 回應
Critical Server 啟動失敗、崩潰 全螢幕錯誤頁 + 紅色 提供重啟、查看日誌、回報問題
Error 模型載入失敗、USB 連線中斷 Toast (destructive) + 頁面 inline error 提供重試、排錯提示
Warning Mock 模式運行中、未簽章警告 Badge / Banner (warning) 持續顯示,資訊性質
Info 操作成功、模型已切換 Toast (success/info) 3 秒自動消失

8.2 全域錯誤頁Critical

當 Server 無法啟動或崩潰,整個前端顯示:

┌──────────────────────────────────────────────┐
│                                              │
│              ⚠                               │
│                                              │
│       Server 無法啟動                         │
│                                              │
│    錯誤原因Port 3721 已被其他程式佔用       │
│                                              │
│   [重新啟動 Server]  [更改 Port]              │
│   [查看日誌]  [回報問題]                      │
│                                              │
│   技術資訊 ▾ (點擊展開 stacktrace)            │
│                                              │
└──────────────────────────────────────────────┘

規格

  • 置中佈局,最大寬度 560px
  • 主標題 font.size.3xl / semibold
  • 錯誤原因用純文字(友善語氣,非 raw error
  • 技術資訊預設收合,展開後顯示 monospace 的 raw error log
  • CTA 按鈕橫排,主要動作用 primary

常見錯誤 + 對應文案

錯誤類型 友善描述 建議動作
Port 衝突 Port {port} 已被其他程式佔用 更改 Port / 關閉佔用程式
Python venv 建立失敗 無法建立 Python 環境,可能缺少系統套件 查看日誌 / 重新安裝
KneronPLUS 載入失敗 無法載入 Kneron SDK 檢查裝置 / 切到 Mock 模式
磁碟空間不足 磁碟空間不足,無法啟動 清理磁碟
未知錯誤 Server 發生未預期錯誤 重啟 / 查看日誌 / 回報

8.3 Inline ErrorError level

當頁面部分區塊載入失敗:

┌─ Models ─────────────────────────┐
│                                  │
│           ⚠                      │
│     無法載入模型清單              │
│     可能是 server 暫時無法回應    │
│                                  │
│         [重試]                   │
│                                  │
└──────────────────────────────────┘

規格置中於所在區塊icon 48px提供單一主要動作重試

8.4 Toast 規格

位置:右上角(距邊 16px
寬度:固定 360px行動裝置尺寸下改全寬 - 32px
高度:自適應(最低 56px
堆疊:多個 toast 垂直堆疊,間隔 8px
最多同時顯示4 個(超過的排隊)

Variants

Variant 背景 前景 icon 預設停留
success color.success (8% opacity) color.success 3s
info color.info (8% opacity) color.info 3s
warning color.warning (10% opacity) color.warning 5s
destructive color.destructive (10% opacity) color.destructive 持續(需手動關)

結構

┌─────────────────────────────────────┐
│ [icon] 主要訊息                  ✕ │
│        次要說明(可選)             │
│        [動作](可選)               │
└─────────────────────────────────────┘

動畫

  • 進入slide-in-right + fade-inmotion.duration.normal + motion.easing.decelerate
  • 離開fade-outmotion.duration.fast

8.5 空狀態Empty States

所有空狀態遵循統一結構:Icon + 標題 + 次要描述 + 主要 CTA (+ 次要 CTA)

通用樣板

┌──────────────────────────────────────┐
│                                      │
│              [icon 64px]             │
│                                      │
│         [標題 — 說明沒有什麼]         │
│     [次要描述 — 為什麼沒有、該做什麼]  │
│                                      │
│        [主要 CTA] [次要 CTA]          │
│                                      │
└──────────────────────────────────────┘

空狀態清單

頁面 情境 Icon 標題 描述 主 CTA 次 CTA
Models 無任何模型 📦 還沒有模型 上傳一個 .nef 檔案開始使用,或啟用預置模型 上傳模型 啟用預置模型
Devices (Real) 無裝置 🔌 沒有偵測到 Kneron 裝置 接上 USB 裝置後會自動顯示 重新掃描 切到 Mock 模式
Devices (Mock) 永遠有 3 顆假裝置
Workspace 無裝置 還沒有可用的裝置 先到 Devices 接上裝置,或切到 Mock 模式 前往 Devices 切到 Mock
Workspace > Batch 無上傳圖片 🖼 還沒有圖片 拖放圖片檔到此處,或點選上傳 上傳圖片
Workspace > Video 無上傳影片 🎞 還沒有影片 拖放影片檔開始推論 上傳影片
Dashboard > Activity 無活動紀錄 🕒 還沒有任何活動 開始使用後,這裡會顯示最近的事件
Settings > 模型 > 自訂路徑 無自訂路徑 📁 沒有自訂模型路徑 新增資料夾讓 App 自動載入其中的 .nef 新增路徑

空狀態的「正向語氣」原則

  • 不用「沒有」「無」開頭 — 用「還沒有」(暗示「之後會有」)
  • 主 CTA 用動詞 —「上傳」「前往」「切換」
  • 描述要告訴使用者為什麼會空 +怎麼讓它不空

8.6 載入狀態

場景 方案
頁面初次載入 Skeleton沿用 shadcn skeleton 元件)
清單刷新 Top bar 細線 progress bar2px無確定進度
按鈕執行中 Button 內 spinner + 文字改為「...中」
長時間操作(> 3 秒) Modal + 進度條 + 可取消按鈕
Mock 模式假進度 遵循真實時間,不要故意 fake 慢

8.7 破壞性操作確認

操作 確認強度
切換 Mock/Real 模式 Dialog 確認(說明會中斷正在進行的推論)
刪除單一模型 Dialog 確認(顯示模型名稱)
清除所有快取 Dialog 確認
重置所有設定 雙重確認(需輸入 RESET 字樣)
結束 App主視窗關閉 無確認Q7 決策:傳統式,直接結束)

所有確認對話框使用 destructive 按鈕樣式在主動作上,次要動作(取消)用 ghost

8.8 Python sidecar 專用狀態(第四輪新增)

背景Architect tray-and-lifecycle.md §4.3(或等效章節)規劃 Python sidecar「空閒 N 分鐘自動 kill、崩潰時 Go server 自動重啟最多 3 次」。這些生命週期事件對使用者來說都是**「按下 Start 後 1-3 秒無回應」**的體驗斷裂,必須有明確的前端呈現。

8.8.1 自動重啟 loading崩潰後

觸發Go server 偵測到 Python sidecar exit code ≠ 0前端透過 WebSocket 事件 sidecar.state 收到 restarting

呈現Workspace 的 Camera Feed 區塊覆蓋一層半透明 overlay,顯示:

┌──── Camera Feed ────────────────┐
│                                 │
│  ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░  │
│  ░                           ░  │
│  ░        ⟳                  ░  │
│  ░   推論引擎正在重新啟動      ░  │
│  ░   (第 1/3 次)             ░  │
│  ░                           ░  │
│  ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░  │
│                                 │
└─────────────────────────────────┘
  • 背景:color.background + 60% opacity 黑色覆蓋
  • Spinnermotion.duration.normal,不用 progress bar不知道要多久
  • 文案:「推論引擎正在重新啟動(第 X/3 次)」
  • 不阻擋使用者切換到其他頁面overlay 只蓋在 Camera FeedSidebar/Header 仍可互動)
  • 重啟成功 → overlay 淡出300ms→ 恢復影像串流
  • 3 次重啟都失敗 → 進入 8.8.3 的 Critical 錯誤頁

8.8.2 空閒被 kill 後的冷啟動(使用者再次按 Start

觸發:使用者閒置超過 Architect 設定的 N 分鐘sidecar 被 Go server 主動 kill節省記憶體。使用者下次按 [▶ Start]sidecar 需要 2-3 秒冷啟動。

呈現Start 按鈕進入 loading 狀態 + Camera Feed 顯示 skeleton

[▶ Start] → [⟳ 正在啟動推論引擎...]  (按鈕 disabledaria-busy=true

Camera Feed 區域:
┌──── Camera Feed ────────────────┐
│                                 │
│     [Skeleton + 「首次啟動       │
│      需要 2-3 秒,請稍候」]     │
│                                 │
└─────────────────────────────────┘
  • Start 按鈕文字即時改為「正在啟動推論引擎...」+ spinner不要只靠 disabled 讓使用者以為當掉
  • 預期時間上限 5 秒,超過 → 改顯示「啟動時間較長請稍候」10 秒 → 進入錯誤狀態
  • 這是使用者首次按 Start冷啟動閒置重啟時的標準體驗,文案相同

8.8.3 Sidecar 3 次重啟失敗Critical

觸發Go server 連續重啟 sidecar 3 次都失敗,發送 sidecar.state = failed

呈現Workspace 整區切換為 Critical 錯誤頁(沿用 §8.2 通用全域錯誤頁樣板,但文案與動作為 sidecar-specific

┌──────────────────────────────────────────────┐
│                                              │
│              ⚠                               │
│                                              │
│       推論引擎無法啟動                        │
│                                              │
│    已嘗試重新啟動 3 次Python 推論引擎       │
│    仍無法正常運作。                           │
│                                              │
│    可能的原因:                                │
│    • KneronPLUS SDK 與 OS 不相容              │
│    • 內嵌 Python 環境損毀                     │
│    • 系統資源不足                             │
│                                              │
│   [手動重試]  [切換到系統 Python]             │
│   [切到 Mock 模式]  [查看 Python 日誌]        │
│                                              │
│   技術資訊 ▾ (點擊展開 stacktrace)            │
│                                              │
└──────────────────────────────────────────────┘
  • 「切換到系統 Python」連結到 Settings > 進階的 Python 執行模式(見 03-wireframes §3.5
  • 「查看 Python 日誌」開啟 server-log-viewer 並 filter 到 sidecar 類別
  • OS 原生通知:同時 shell out 原生通知(因為 Server 崩潰屬 R4-8 定義的嚴重事件文案「visionA-local推論引擎無法啟動」

8.8.4 與 Architect 的 API 依賴

本節所有呈現都依賴 Architect 提供:

  • WebSocket 事件 sidecar.statepayload: { state: 'starting' | 'running' | 'idle' | 'restarting' | 'failed', attempt?: number }
  • POST /api/sidecar/restart(手動重試按鈕)
  • GET /api/sidecar/logs?since=<timestamp>(日誌 viewer

若 Architect 未提供以上 API/事件,本節狀態無法落地,需在 M2 前與 Architect 對齊。