依 autoflow-agent workspace v2 設計把 PRD / 設計 / 架構 / 交付類 共享文件從個人層 .autoflow/(ignored)搬到 docs/autoflow/(進 git), 讓團隊可共享產品與架構文件,個人層只留 progress / review / testing 等 per-branch 筆記。 - 02-prd/ 21 個檔(PRD、features、market-analysis 等) - 03-design/ 18 個檔(design-spec、wireframes、flows 等) - 04-architecture/ 31 個檔(TDD、design-doc、ADR×14、API 規格等) - 07-delivery/ 3 個檔(project-summary、phase-0.6-handover、stage-deployment-setup) 合計 73 檔。原檔已從 .autoflow/ 移除(migration 工具執行 git mv, 但因 .autoflow/ 在 .gitignore 中、git 將此操作視為新增、無 rename history)。
15 KiB
離線 / 掉線 UI 行為 — visionA Cloud
雲端版的根本差異:使用者的 Kneron 裝置不在瀏覽器那端,而是在某台電腦上透過 local agent 中繼。這條 tunnel 會因為網路抖動、電腦休眠、agent 被關等原因中斷。UI 必須誠實呈現狀態,不讓使用者以為「點了沒反應 = 壞了」。
1. 連線拓樸與失效點
Browser (User) ←HTTPS/WSS→ Cloud API Server ←yamux/WS→ Local Agent ←USB→ Kneron Device
A B C D
│ │ │ │
▼ ▼ ▼ ▼
斷網 (A) 雲端服務掛 (B) Agent 掉線 (C) USB 拔掉 (D)
四種失效層級,UI 給使用者的回饋應該不同:
| 失效點 | 影響範圍 | UI 呈現 |
|---|---|---|
| A. 使用者斷網 | 全部操作失效 | 瀏覽器層級(navigator.onLine)+ 全域 NetworkErrorBanner |
| B. 雲端 API 掛 | 全部操作失效 | 全域 NetworkErrorBanner |
| C. Local agent 掉線 | 該使用者的所有遠端裝置 | 每個裝置卡片顯示離線;Activity log 寫入 |
| D. USB 裝置拔除 | 單一裝置 | 該裝置狀態 error / disconnected(既有行為) |
核心設計原則:
- 「我這端沒事」vs「那台電腦沒事」要可區分 — 使用者需要判斷要怎麼處理
- 狀態要即時 — 透過 WebSocket 推送 + 合理的 heartbeat interval
- 優雅降級 — 裝置掉線時,操作按鈕要變成 disabled,而不是點了丟 error
2. 裝置連線狀態模型
雲端版的 Device 新增 remoteStatus 欄位(遠端 tunnel 層級),與既有 status 欄位(USB / 硬體層級)並存:
interface Device {
// 既有
id: string;
name: string;
type: string;
status: 'detected' | 'connecting' | 'connected' | 'flashing' | 'inferencing' | 'error' | 'disconnected';
firmwareVersion?: string;
flashedModel?: string;
// 雲端版新增
remoteStatus: 'online' | 'offline' | 'reconnecting' | 'error';
lastSeenAt: string; // ISO 8601,最後心跳時間
hostName?: string; // local agent 回報的主機名稱
pairedAt: string; // 配對時間
errorMessage?: string; // 錯誤訊息(remoteStatus=error 時)
}
2.1 狀態組合矩陣
remoteStatus |
status(USB) |
使用者看到 | 可執行操作 |
|---|---|---|---|
| online | connected | 🟢 已連線 | 全部 |
| online | inferencing | 🔵 推論中 | 停止推論、切換來源 |
| online | flashing | 🟡 燒錄中 | 等待中(唯讀) |
| online | error | 🔴 裝置錯誤 | 重試、查看日誌 |
| online | disconnected | ⚪ 裝置未連接 | 重新連接(agent 端) |
| reconnecting | * | 🟡 重新連線中(pulse) | 唯讀等待 |
| offline | * | ⚪ 離線・最後心跳 X 前 | 解除配對、查看歷史 |
| error | * | 🔴 連線錯誤:{message} | 查看 troubleshooting |
顯示優先級:remoteStatus != online 時,UI 優先顯示 remoteStatus(因為連遠端都沒連上,USB 狀態已不可信)。
3. 全域:NetworkErrorBanner
觸發條件:雲端 API 不可達(A 或 B 失效)
3.1 偵測邏輯(給 Frontend 參考)
// 簡化版邏輯
let consecutiveFailures = 0;
setInterval(async () => {
try {
await fetch('/api/system/health');
consecutiveFailures = 0;
hideBanner();
} catch {
consecutiveFailures++;
if (consecutiveFailures >= 3) showBanner();
}
}, 10_000);
// 搭配 navigator.onLine 事件
window.addEventListener('offline', () => showBanner({ cause: 'local' }));
window.addEventListener('online', () => checkHealth());
3.2 狀態展示
| 狀態 | 文案 | 樣式 | 按鈕 |
|---|---|---|---|
| 本機斷網 | 「你的網路似乎離線了」 | bg-amber-50 |
[重試] |
| API 失敗重試中 | 「連線中斷 — 無法連上雲端服務。正在重試...」 | bg-amber-50 |
[立即重試] |
| 恢復 | 「✓ 已恢復連線」 | bg-green-50(短暫 3 秒消失) |
- |
3.3 行為
- 顯示於 Header 下方(sticky top-14 z-40)
- 顯示期間不阻擋 UI 操作(使用者仍可點擊按鈕;按下會顯示 toast 錯誤)
- 恢復後 3 秒自動消失
詳細元件規格見 components.md 10.4。
4. 裝置列表:離線裝置顯示
4.1 在 /devices 頁
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Kneron KL520 🟢 │ │ Kneron KL720 ⚪ │ │ Kneron KL520 🟡 │
│ │ │ 離線・2 分鐘前 │ │ 重連中... │
│ 類型:KL520 │ │ 類型:KL720 │ │ 類型:KL520 │
│ 韌體:2.3.1 │ │ 韌體:—(cache) │ │ 韌體:2.3.1 │
│ │ │ │ │ │
│ [管理][工作區] │ │ [管理][解除配對] │ │ [管理](disabled)│
└──────────────────┘ └──────────────────┘ └──────────────────┘
離線裝置卡片:
opacity-75(淡化但仍可讀)RemoteDeviceBadge顯示「離線・最後心跳 2 分鐘前」- 「工作區」按鈕 hidden 或 disabled
- 「管理」按鈕仍可用(進詳情頁看歷史)
- 新增「解除配對」選項(Phase 0 可放進詳情頁而非卡片)
4.2 排序建議
- 預設:在線優先(online → reconnecting → offline → error),其次按配對時間
- Phase 1 加篩選:
[全部] [在線] [離線]
4.3 Dashboard ConnectedDevicesList
同樣策略:離線裝置顯示最後心跳時間;若全部裝置離線,顯示 EmptyState「所有裝置都離線了,[查看 troubleshooting]」。
5. 裝置詳情頁(/devices/[id]):離線降級
5.1 頁面頂部狀態 Banner
當 remoteStatus === 'offline':
┌──────────────────────────────────────────────────────────────┐
│ ⚠ 此裝置目前離線 │
│ 最後心跳時間:2026-04-21 14:28(2 分鐘前) │
│ 所在電腦:office-mac │
│ 部分操作無法使用,待 local agent 重新連線後自動恢復 │
│ [重新整理] │
└──────────────────────────────────────────────────────────────┘
樣式:bg-amber-50 dark:bg-amber-950/30 + border-amber-300;圖示 AlertTriangle。
5.2 按鈕降級
| 按鈕 | 在線 | 離線 |
|---|---|---|
| 燒錄模型(FlashDialog) | 可用 | disabled + tooltip「裝置離線中」 |
| 開啟工作區 | 可用 | disabled + tooltip「裝置離線中」 |
| 中斷連線 | 可用 | 隱藏(已無連線可中斷) |
| 解除配對 | 可用 | 可用(使用者仍可清除此紀錄) |
| 編輯別名 / 備註 | 可用 | 可用(本地資料修改,不需要 agent) |
5.3 Cached Data
離線時顯示最後已知資訊(從 server cache 讀):
- 韌體版本
- 已燒錄模型
- 裝置健康狀態(標註「資料截至 X 時間」)
5.4 自動恢復
- 頁面持續透過 WebSocket 監聽狀態變化
remoteStatus: online推送到 → 自動隱藏 banner、恢復按鈕 → toast「✓ 裝置已重新連線」
6. Workspace(/workspace/[deviceId]):執行中掉線處理
Workspace 是最不能忍受掉線的頁面(正在跑推論)。
6.1 頂部狀態列
既有 Workspace 頁頂部有 ← 返回 + 裝置名稱 + FlashDialog + 開始/停止推論,新增:
← 返回 工作區:office-mac / Kneron KL520 🟢 在線 (150ms latency)
│
└─ 點擊展開詳情
連線品質提示(Phase 1+):
- Latency < 200ms → 🟢 流暢
- 200-500ms → 🟡 稍慢
-
500ms → 🔴 不穩
6.2 裝置掉線時的全頁遮罩
┌──────────────────────────────────────────────────────────────┐
│ │
│ 🔌 │
│ │
│ 裝置已離線 │
│ │
│ 與 Kneron KL520 的連線中斷,推論已自動停止 │
│ │
│ [等待重連 (0:23)] [返回裝置列表] │
│ │
│ 📄 最後 5 張推論結果已儲存在 /activity │
│ │
└──────────────────────────────────────────────────────────────┘
行為:
- 遮罩層
bg-background/80 backdrop-blur-sm,覆蓋 CameraInferenceView + InferencePanel - Camera stream 顯示最後一幀 + overlay「連線中斷」
- 持續 polling / WebSocket 重連嘗試
- 若 60 秒內重連成功 → 遮罩消失,toast「✓ 裝置已重新連線,請手動重啟推論」(不要自動 resume)
- 若 60 秒仍未連線 → 提示「建議先返回,稍後再試」
6.3 推論 buffer / 資料保全
- Workspace 裡的推論結果(分類結果、效能指標)暫存在 Zustand store
- 掉線時保留最近資料,可供使用者查看(唯讀)
- 重連後使用者需要手動重啟推論(避免意外累積費用或資源浪費)
7. Cluster Workspace:部分離線的降級
叢集工作區(/workspace/cluster/[clusterId])是多裝置場景,允許部分失效。
7.1 Degraded 狀態
從 POC 搬來的叢集已經有 degraded 狀態(某裝置離線,叢集自動降級)。UI 呈現:
叢集:Production Cluster A 🟡 降級執行中 (2/3 裝置在線)
成員裝置:
● office-mac KL520 🟢 在線 w=3 處理 85 fps
● home-pi KL720 🟢 在線 w=1 處理 25 fps
● backup KL520 ⚪ 離線 w=3 (已降級)
7.2 全叢集掉線
所有成員都離線 → 叢集狀態 offline → 顯示「叢集目前無法使用,等待裝置重新連線」
8. Toast / 通知策略
| 事件 | Toast 類型 | 持續時間 | 範例文字 |
|---|---|---|---|
| 裝置上線 | success | 3s | 「Kneron KL520 已連線」 |
| 裝置掉線(首次) | error | 5s | 「Kneron KL520 已離線」 |
| 推論被迫停止(掉線) | warning | 5s | 「裝置離線,推論已停止」 |
| 重新連線成功 | success | 3s | 「✓ Kneron KL520 已重新連線」 |
| API 失敗(單次) | error | 4s | 「操作失敗,請稍後再試」 |
| Pairing 完成 | success | 4s | 「✓ Kneron KL520 已成功配對」 |
| Pairing 失敗 | error | 5s | 「配對失敗:{原因}」 |
重要:不要對同一個裝置的反覆掉線 spam toast。建議 debounce / throttle:同裝置同狀態 60 秒內只發一次通知。
9. Activity Timeline 擴充
Dashboard 的活動時間軸新增雲端版事件類型:
| Event | Icon | 文案範例 |
|---|---|---|
device_paired |
Link2 |
已配對裝置 Kneron KL520 |
device_unpaired |
Unlink |
已解除配對 Kneron KL520 |
device_online |
CheckCircle |
Kneron KL520 已上線 |
device_offline |
XCircle |
Kneron KL520 已離線 |
tunnel_reconnected |
RefreshCw |
與 office-mac 的連線已恢復 |
cluster_degraded |
AlertTriangle |
Production Cluster A 降級執行中 |
10. i18n key(整合)
remote.status.online → 在線 / Online
remote.status.offline → 離線 / Offline
remote.status.reconnecting → 重新連線中 / Reconnecting
remote.status.error → 連線錯誤 / Connection error
remote.lastSeen → 最後心跳 {time} / Last seen {time}
remote.lastSeenNever → 從未連線 / Never connected
remote.banner.offline.title → 此裝置目前離線
remote.banner.offline.description → 部分操作無法使用,待 local agent 重新連線後自動恢復
remote.banner.offline.refresh → 重新整理
remote.workspace.disconnected.title → 裝置已離線
remote.workspace.disconnected.description → 與 {deviceName} 的連線中斷,推論已自動停止
remote.workspace.disconnected.waiting → 等待重連 ({time})
remote.workspace.disconnected.backToList → 返回裝置列表
remote.toast.online → {deviceName} 已連線
remote.toast.offline → {deviceName} 已離線
remote.toast.reconnected → ✓ {deviceName} 已重新連線
remote.toast.inferenceStopped → 裝置離線,推論已停止
network.disconnected.title → 連線中斷
network.disconnected.description → 無法連上雲端服務。正在重試...
network.disconnected.retryButton → 立即重試
network.restored → ✓ 已恢復連線
11. 無障礙
- 所有狀態變更透過
aria-live="polite"宣告 RemoteDeviceBadge有role="status"- 錯誤遮罩層用
role="alertdialog"+ 焦點陷阱 - 不只靠顏色(綠 / 紅 / 黃),都搭配文字與 icon
- 掉線遮罩的按鈕可 Tab 聚焦、Enter 觸發
- 重連倒數計時不閃爍(避免 seizure-inducing motion)
12. 效能考量
- Heartbeat interval:local agent ↔ 雲端 10 秒 / 次(全棧統一)
- 掉線判定閾值:3 次未收到心跳(30 秒) 才標記為 offline,避免短暫抖動誤判
- UI 呈現對應時機:
- 第 1 次心跳 miss(10s 起):不做任何變化,避免閃爍
- 第 2 次心跳 miss(20s 起):裝置狀態切為 🟡
reconnecting(重連中),卡片opacity-90;不發 toast - 第 3 次心跳 miss(30s 達成):標記為 ⚪
offline,發 toast「{裝置名稱} 已離線」,Workspace 遮罩觸發 - 任一期間心跳恢復:立即切回 🟢
online,若曾通知則發「✓ {裝置名稱} 已重新連線」
- 前端 polling 避免過於密集;優先使用 WebSocket push
- 離線裝置不主動 polling 詳情 API,等使用者進詳情頁才拉
13. TODO
| 項目 | 時機 |
|---|---|
| Latency 顯示 | Phase 1 |
| 連線品質圖表(過去 24 小時 uptime) | Phase 1 |
| 使用者可訂閱特定裝置的掉線通知(Email / Slack / Webhook) | Phase 2 |
| 自動重啟推論(使用者可選擇) | Phase 2 |
| SLA / 可用性儀表板 | Phase 2+ |