# 5. 使用者流程 本章節描述三個關鍵使用者流程:**首次安裝 → First-Run → 日常使用**。每個流程都標注對應的 User Story 與驗收標準。 ## 5.1 首次安裝流程(對應 US-1) ``` ┌─────────────────────────────────────────┐ │ 1. 使用者從內部 Gitea Releases 下載安裝檔 │ │ macOS → .dmg │ │ Windows → .exe(Inno Setup) │ │ Ubuntu → .AppImage │ └────────────┬────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 2. 雙擊安裝檔 │ │ macOS: .dmg mount → 拖拉到 Applications │ │ 首次開啟跳 Gatekeeper 警告 │ │ → 右鍵 → 開啟(安裝頁文件說明) │ │ Win: .exe 安裝精靈(Inno Setup 預設流程) │ │ SmartScreen 警告 → 仍要執行 │ │ Linux: 給 .AppImage chmod +x → 雙擊 │ └────────────┬────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 3. Wails 殼啟動 → 偵測是否首次執行 │ │ 首次:開啟安裝精靈(沿用原 installer UI) │ │ 後續:跳過,直接進主畫面 │ └────────────┬────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 4. 安裝精靈執行以下步驟(有進度條): │ │ a. 解壓 payload 到應用資料目錄 │ │ (mac: ~/Library/Application Support/ │ │ visiona-local/;Win/Linux 依慣例) │ │ b. 建立 Python venv(優先用內嵌 Python) │ │ c. pip install --no-index wheels/ │ │ (numpy / opencv / pyusb / KneronPLUS)│ │ d. 解壓 ffmpeg binary │ │ e. 解壓預置 .nef 模型 │ │ f. 平台特定步驟: │ │ - Win: 裝 WinUSB driver(跳 UAC) │ │ - Linux: 寫入 udev rules(跳 sudo) │ │ - Mac: ad-hoc codesign dylib │ │ g. 啟動 edge-ai-server 子行程 │ │ h. 等待 server ready(http://127.0.0.1:3721)│ └────────────┬────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 5. 進入 First-Run 歡迎流程(見 5.2) │ └─────────────────────────────────────────┘ ``` **關鍵驗收點:** - 第 4 步 a-h 全部自動化,使用者無需任何 CLI 操作 - 若 Python fallback 失敗(系統無 Python),明確告知使用者到哪下載 - 若 Windows 拒絕 UAC,提供「手動安裝 driver」的指引 - 整個流程目標 ≤ 3 分鐘 / **上限 ≤ 5 分鐘**(一般硬體 + SSD)— 第四輪 R4-4 決策 ## 5.2 First-Run 歡迎流程(對應 US-2) ``` ┌─────────────────────────────────────────┐ │ Step 1 — 歡迎畫面 │ │ ┌─────────────────────────────────────┐ │ │ │ [visionA-local Logo(沿用 EAP)] │ │ │ │ │ │ │ │ 「邊緣 AI 推論,裝起來就能跑」 │ │ │ │ │ │ │ │ [開始使用] [稍後再說] │ │ │ └─────────────────────────────────────┘ │ └────────────┬────────────────────────────┘ │(點擊「開始使用」) ▼ ┌─────────────────────────────────────────┐ │ Step 2 — 執行模式選擇 │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ 🟢 真實硬體 │ │ 🟡 Mock 模式 │ │ │ │ │ │(先看看) │ │ │ │ 需要 Kneron │ │ 無需硬體 │ │ │ │ KL720/KL730 │ │ 假裝置 + 假推論│ │ │ │ USB 裝置 │ │ │ │ │ │ │ │ │ │ │ │ [選擇] │ │ [選擇] │ │ │ └──────────────┘ └──────────────┘ │ │ │ │ 預設:真實硬體(依據 Q8) │ └────────────┬────────────────────────────┘ │ ├─── 選真實硬體 ───▶ Step 3a(硬體偵測) │ └─── 選 Mock ──────▶ Step 3b(Mock 準備) ┌─────────────────────────────────────────┐ │ Step 3a — 硬體偵測(真實模式) │ │ 「請插上 Kneron 裝置…」 │ │ [自動掃描 USB,持續 10 秒] │ │ ├ 成功:顯示偵測到的裝置卡片 │ │ │ [前往 Workspace] │ │ └ 失敗:顯示排錯清單 │ │ 1. 確認 USB 已插好 │ │ 2. 試試 USB 3.0 埠 │ │ 3. 重插一次 │ │ 4. 檢查驅動是否安裝 │ │ [重試] [切換 Mock 模式] │ └─────────────────────────────────────────┘ ┌─────────────────────────────────────────┐ │ Step 3b — Mock 模式準備 │ │ 「已進入 Mock 模式,您將看到 3 個假裝置」 │ │ [進入 Dashboard] │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 完成 → 進入 Dashboard │ │ 右上角顯示目前模式(真實 / Mock) │ │ 主視窗標題列 + sidebar 底部狀態列同步顯示模式│ └─────────────────────────────────────────┘ ``` **關鍵驗收點:** - AC-2.1:Mock 模式是一鍵選項(非預設) - AC-2.2:進入 Mock 後 ≤ 30 秒看到假推論(首次)/ ≤ 15 秒(回訪)— 第四輪 R4-7 拆兩級 - AC-2.3:Mock 模式有明確視覺標記(主視窗標題列 + 首頁徽章 + sidebar 底部狀態列) - AC-2.4:Mock 模式**不 spawn Python sidecar**,純前端 state + Go server(對應 nonfunctional §6.1 的 Mock idle RAM ≤ 600MB 預算) - 三步都可「稍後再說」跳過,直接進 Dashboard ## 5.3 日常使用流程 ### 5.3.1 典型使用會話(FAE 在客戶現場) ``` 1. 使用者從 app 目錄或桌面捷徑開啟 visionA-local ↓ 2. Wails 殼啟動 → 跳過 First-Run(已完成) ↓ 3. 啟動 edge-ai-server 子行程(< 2 秒) ↓ 4. 主視窗開啟 → Dashboard ↓ 5. 使用者插上 Kneron USB ↓ 6. Devices 頁自動顯示新裝置(WebSocket push) ↓ 7. 使用者點「Connect」→ 進入 Workspace ↓ 8. 選 webcam + 選模型 → 點 Start ↓ 9. 看到 MJPEG 串流 + 推論 overlay ↓ 10. demo 完畢 → 點 Stop → 關閉主視窗 → 程式退出(Q7 傳統式) ``` **關鍵驗收點:** - 步驟 3 到步驟 9 整段 ≤ 30 秒 - 步驟 5 到步驟 6(USB 偵測)≤ 3 秒 - 步驟 7 到步驟 9(connect 到第一幀)≤ 10 秒 ### 5.3.2 跨會話狀態持久化 > **路徑說明(第三輪 Q-E1 + 第四輪 R4-5)**:資料目錄名統一全小寫為 `visiona-local`(對齊 Bundle ID `com.innovedus.visiona-local` 與 Linux 檔案系統慣例)。macOS 採 OS 慣例路徑 `~/Library/Application Support/visiona-local/`;Windows 預期為 `%APPDATA%\visiona-local\`;Linux 預期為 `~/.local/share/visiona-local/` 或 `$XDG_DATA_HOME/visiona-local/`。以下以 `` 表示該目錄。 | 狀態 | 儲存位置 | 是否跨會話保留 | |------|---------|----------------| | 上次選的模型 | `/config.json` | ✅ 是 | | 上次選的 webcam | `/config.json` | ✅ 是 | | 上次的模式(真實 / Mock) | `/config.json` | ✅ 是 | | 上傳的自訂模型 | `/models/` | ✅ 是 | | 推論歷史 / 日誌 | `/logs/` | ✅ 是(滾動保留) | | 語言偏好 | `/config.json` | ✅ 是 | | 深色模式 | N/A | 跟隨系統,無覆寫 | ### 5.3.3 快速操作流程(原 Tray 流程 — 已砍) > **第三輪 Q-A 決策**:系統列 tray 已砍掉。原本透過 tray 提供的快速操作(顯示主視窗、Server 狀態、快速動作、模式切換、結束)改由以下方式提供: > > | 原 Tray 項目 | 新的入口 | > |-------------|---------| > | 顯示主視窗 | 桌面 / Dock / 工作列 icon(OS 原生行為) | > | Server 狀態 | Dashboard 的 Server 狀態卡片 + sidebar 底部狀態列 | > | 快速動作(新增裝置 / 上傳模型 / 開啟工作區)| 原生 menu bar:`File → New Device / Upload Model / Open Workspace` | > | 模式切換(真實 / Mock)| Settings 一般分頁;或 Dashboard 右上角的模式指示器 | > | 關於 | 原生 menu bar:`Help → About` | > | 結束(⌘Q)| 原生 menu bar + OS 標準快捷鍵 | > > Q7 已決定「關閉視窗 = 結束程式」,搭配 tray 被砍,整體生命週期就是「打開 = 跑、關閉 = 結束」的傳統桌面 app 模式。 ## 5.4 錯誤處理流程 ### 5.4.1 Port 3721 被占用 ``` Server 啟動失敗 → 顯示錯誤對話框: 「連接埠 3721 已被其他程式占用。 請關閉占用該埠的程式,或在 Settings 中變更 server 埠號。」 [設定埠號] [結束] ``` ### 5.4.2 Python venv 建立失敗 ``` First-Run 步驟 4b 失敗 → 顯示錯誤對話框: 「無法建立 Python 執行環境。 可能原因: - 系統 Python 版本不符(需要 3.10+) - 磁碟空間不足(需要 ~500MB) - 權限不足 [檢視日誌] [重試] [結束]」 ``` ### 5.4.3 KneronPLUS wheel 安裝失敗 ``` First-Run 步驟 4c 失敗 → 顯示錯誤對話框: 「KneronPLUS SDK 安裝失敗。 請確認: - 已允許 UAC 提權(Windows) - 已安裝 libusb(Linux:sudo apt install libusb-1.0-0) [檢視日誌] [重試] [結束]」 ``` ### 5.4.4 Mac Gatekeeper 警告 ``` 首次開啟 → Mac 跳出「無法開啟,因為來自未識別的開發者」 → First-Run 歡迎頁提供說明: 「第一次開啟時,請在 Finder 中對 visionA-local 按右鍵, 選擇『開啟』,然後確認警告。 這是因為 visionA-local 未購買 Apple notarization。」 ``` ### 5.4.5 Windows SmartScreen 警告 ``` 首次下載執行 → SmartScreen 跳出警告 → 發佈說明頁提供引導: 「點選『更多資訊』→『仍要執行』。 這是因為 visionA-local 未購買 Windows 程式碼簽章。」 ``` ## 5.5 Single-instance 行為與第二次雙擊 UX(第四輪補) **背景**:visionA-local 以 single-instance 模式運作(單一程式實例),避免多個 Wails 殼搶 Go server 的 127.0.0.1:3721 port。實作細節請參考 Architect 的 `04-architecture/lifecycle.md`(single-instance lock 章節)。 **PRD 層級要求的 UX 行為**: | 情境 | 預期行為 | 驗收點 | |------|---------|--------| | 第一次雙擊 icon(無已開啟的 visionA-local) | 正常啟動,走 First-Run 或直達 Dashboard | 與 5.1 / 5.2 流程一致 | | **第二次雙擊 icon(已有 visionA-local 在前景)** | **把既有視窗帶到前景 + focus**,**不開第二個視窗**、**不顯示錯誤** | 無閃爍、無崩潰 | | **第二次雙擊 icon(已有 visionA-local 但視窗最小化 / 隱藏)** | **把既有視窗還原並帶到前景 + focus** | 使用者不會以為「怎麼沒反應」 | | **第二次雙擊 icon(已有 visionA-local 但 Go server 已 crash)** | 第二個實例偵測到 lock 殘留但 server 不在,清理 lock 後正常啟動 | 不該無限卡住 | **快捷鍵相關**: - 原生 menu bar `File → 重啟 Server` 的快捷鍵為 **⌘Shift+R**(macOS)/ **Ctrl+Shift+R**(Win/Linux)— 第四輪 R4-6 決策:從原 `⌘R` 改為 `⌘Shift+R`,避免與瀏覽器 reload 的肌肉記憶衝突 - 原本規畫的 `⌘Shift+W` 取消(⌘W / ⌘4 已可關閉視窗)— 第四輪 R4-6 決策 - 其餘快捷鍵維持:`⌘Q` 結束、`⌘W` 關閉視窗(= 結束,Q7 傳統式)、`⌘,` 開 Settings、`⌘1-⌘4` 切換主導航 **第二次雙擊的具體 Design 規格待 Design Agent 在 `03-design/spec/` 補齊**(動畫時間、focus ring 表現、Dock icon bounce 等細節)。PRD 層級只定義行為意圖,不定義視覺細節。 ## 5.6 OS 通知策略(第四輪 R4-8 決策) visionA-local 採**分層通知策略**,不是所有事件都走原生 OS 通知,避免噪音與跨平台一致性問題。 | 事件 | 通知管道 | 理由 | |------|---------|------| | 裝置連線成功(USB 插入 + connect OK) | **App 內 toast**(右下角,auto-dismiss 3 秒)| 使用者 app 已在前景,OS 通知是多餘噪音;跨平台一致 | | 裝置斷線(USB 拔出 / connect 失敗) | **App 內 toast**(右下角,auto-dismiss 5 秒 + 錯誤色)| 同上 | | 模型上傳完成 | **App 內 toast** | 同上 | | 推論開始 / 停止 | **狀態列更新 + 無 toast**(過於頻繁,改用持續性狀態指示) | 避免噪音 | | First-Run 步驟完成 | **步驟條更新 + 無 toast** | First-Run 本身就是向導,不需額外通知 | | **Server 崩潰**(Go server 意外退出)| **shell out 原生 OS 通知**(`osascript` on macOS / PowerShell `New-BurntToastNotification` 或 WinRT on Windows / `notify-send` on Linux)+ app 內錯誤對話框 | 重大錯誤,使用者可能切到其他視窗,必須確保看到 | | **Python sidecar 崩潰**(推論 backend 掛了)| **shell out 原生 OS 通知** + app 內錯誤對話框 | 同上 | | **致命錯誤**(無法啟動、依賴缺失)| **app 啟動對話框**(blocking) | 無 app 可用,必須擋住使用者 | ### 實作要求 - **App 內 toast** 沿用原 edge-ai-platform 的 `sonner` 或 shadcn toast 元件 - **原生 OS 通知** 採 shell out 策略(不引入 go-toast 等第三方 binding,避免增加依賴與打包複雜度) - macOS:`osascript -e 'display notification "..." with title "visionA-local"'` - Windows:PowerShell `New-BurntToastNotification` 或直接用 Wails 提供的 `runtime.MessageDialog`(如支援) - Linux:`notify-send "visionA-local" "..."` - 若 shell out 失敗(工具不存在),靜默 fallback 到 app 內錯誤對話框 - **權限要求**:macOS 首次顯示原生通知時會觸發系統通知權限請求,First-Run 需在文案裡提及「本 app 在出現嚴重錯誤時會通知你」 ### 驗收點 - AC-7.1(新增):裝置連/斷僅以 app 內 toast 呈現,不觸發系統通知 - AC-7.2(新增):Go server 或 Python sidecar 崩潰時,使用者就算切到其他視窗也能透過系統通知得知 - AC-7.3(新增):macOS 的通知權限請求時機在 First-Run 或首次崩潰時才觸發,不在 app 啟動時無緣無故詢問