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

255 lines
13 KiB
Markdown
Raw Permalink 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.

# 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-in`motion.duration.normal` + `motion.easing.decelerate`
- 離開fade-out`motion.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 黑色覆蓋
- Spinner`motion.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.state`payload: `{ state: 'starting' | 'running' | 'idle' | 'restarting' | 'failed', attempt?: number }`
- `POST /api/sidecar/restart`(手動重試按鈕)
- `GET /api/sidecar/logs?since=<timestamp>`(日誌 viewer
**若 Architect 未提供**以上 API/事件,本節狀態無法落地,需在 M2 前與 Architect 對齊。