# 元件庫清單與規格 — visionA Cloud > **本元件庫 100% 沿用 `local-tool/frontend/src/components/`**,Frontend Agent 實作時直接搬(或整併到 monorepo shared package)。本文件整理既有元件清單與用途,並定義雲端版**新增的 4 個元件**。 --- ## 1. 元件分類總覽 | 類別 | 數量 | 來源 | 狀態 | |------|------|------|------| | 基礎 UI(Shadcn 風)| 22 | `src/components/ui/` | 沿用 | | Layout | 4 | `src/components/layout/` | 沿用(Sidebar 需擴充) | | Dashboard | 3 | `src/components/dashboard/` | 沿用 | | Devices | 8 | `src/components/devices/` | 沿用(需擴充遠端狀態) | | Models | 6 | `src/components/models/` | 沿用 | | Inference | 5 | `src/components/inference/` | 沿用 | | Camera | 多個 | `src/components/camera/` | 沿用 | | 特殊 | 7 | 根目錄 | 沿用(部分可能棄用) | | **雲端版新增** | 4 | `src/components/cloud/`(建議)| 本次新增 | --- ## 2. 基礎 UI 元件(沿用 Shadcn 風) 來源:`local-tool/frontend/src/components/ui/` | 元件 | 檔案 | 用途 | |------|------|------| | `Button` | `button.tsx` | 6 variants(default / destructive / outline / secondary / ghost / link),8 sizes(xs / sm / default / lg / icon*3)| | `Card` | `card.tsx` | 資訊容器(含 Header / Title / Description / Content / Footer / Action 子元件)| | `Dialog` | `dialog.tsx` | Modal 對話框(Radix Dialog 封裝)| | `AlertDialog` | `alert-dialog.tsx` | 確認危險操作的專用 Dialog(無法 ESC 關閉)| | `Tabs` | `tabs.tsx` | 分頁切換(Radix Tabs 封裝)| | `Input` | `input.tsx` | 單行文字輸入框,h-9 | | `Select` | `select.tsx` | 下拉選單(Radix Select 封裝)| | `Badge` | `badge.tsx` | 標籤徽章,6 variants | | `Slider` | `slider.tsx` | 滑桿(Radix Slider 封裝,用於 confidence threshold)| | `Progress` | `progress.tsx` | 進度條(Radix Progress) | | `Checkbox` | `checkbox.tsx` | 多選框(Radix Checkbox) | | `Label` | `label.tsx` | 表單標籤(Radix Label) | | `Separator` | `separator.tsx` | 分隔線 | | `ScrollArea` | `scroll-area.tsx` | 自訂滾動條(Radix ScrollArea) | | `Sonner` | `sonner.tsx` | Toast 通知(Sonner 封裝) | | `EmptyState` | `empty-state.tsx` | 空狀態頁面(Icon + Title + Description + Action) | **狀態覆蓋(所有互動元件都需具備):** - Default / Hover / Active / Focus-visible / Disabled / Loading - Focus-visible 使用 shadcn 預設 `ring-[3px]`(符合 WCAG AA) - Disabled 統一 `disabled:pointer-events-none disabled:opacity-50` --- ## 3. Layout 元件(沿用 + 擴充) 來源:`local-tool/frontend/src/components/layout/` | 元件 | 檔案 | 用途 | 雲端版變更 | |------|------|------|-----------| | `Header` | `header.tsx` | 頂部列:平台標題 + HelpButton + ConnectionStatus | 平台標題改「visionA Cloud」;ConnectionStatus 語義變更(見下) | | `Sidebar` | `sidebar.tsx` | 左側導航:Dashboard / Models / Devices / Workspace / Settings | **新增**:底部 UserMenu;**新增**:`/clusters` 導航項 | | `ConnectionStatus` | `connection-status.tsx` | 伺服器連線狀態徽章(綠 / 紅點)| 語義變更:local-tool 指「本機 server」,Cloud 指「雲端 API Server」| | `HelpButton` | `help-button.tsx` | 開啟引導(driver.js tour)| Phase 0 保留,Phase 1 重寫內容為雲端版 | ### 3.1 Sidebar 變更規格(雲端版) ``` ┌────────────────────────┐ │ [Logo] visionA Cloud │ ← h-14,border-b ├────────────────────────┤ │ ▸ Dashboard │ │ ▸ Models │ │ ▸ Devices │ │ ▸ Clusters (new) │ ← 新增 │ ▸ Workspace │ │ ▸ Settings │ │ │ │ (flex-1) │ │ │ ├────────────────────────┤ │ [avatar] username ▾ │ ← 新增 UserMenu(點開展示 Account / Sign out) ├────────────────────────┤ │ v0.1.0 │ └────────────────────────┘ ``` **導航項目(繁中 / English):** | i18n key | 繁中 | English | icon | |---------|------|---------|------| | `nav.dashboard` | 儀表板 | Dashboard | `LayoutDashboard` | | `nav.modelLibrary` | 模型庫 | Models | `Boxes` | | `nav.devices` | 裝置 | Devices | `Cable` | | `nav.clusters` | 叢集 | Clusters | `Network` | | `nav.workspace` | 工作區 | Workspace | `Play` | | `nav.settings` | 設定 | Settings | `Settings` | > **改動說明**:local-tool 目前用 `{ icon: 'H' }` 這種單字母佔位(見 `sidebar.tsx:13-18`),設計上不理想。Design Review 列為 **Minor**。**雲端版建議改用 Lucide Icon**,與產品視覺語言一致。 ### 3.2 ConnectionStatus 語義變更 原 local-tool 的 `ConnectionStatus` 檢查 `fetch('/system/health')`,顯示「伺服器已連線」。 **雲端版需要區分兩層連線:** | 層級 | 顯示位置 | 含義 | 綠 / 紅 / 黃 | |------|---------|------|-------------| | 雲端 API | Header 右側(現有位置)| 前端能不能打雲端 API Server | 綠:OK / 紅:API 不可達 | | 遠端 Agent | 個別裝置卡片 / 詳情頁 | 該使用者的 local agent 有沒有連上雲端 | 綠:在線 / 灰:離線 / 黃:連線中 | **Cloud 版 `ConnectionStatus` 行為:** - 綠點 + 「已連線」→ 可以正常使用 - 紅點 + 「連線失敗,重試中」→ 整個服務不可用,顯示全域 `NetworkErrorBanner` - 不再顯示 local-tool 的「伺服器未連線」— 雲端 API 應該永遠可達,失敗應觸發明顯警告 --- ## 4. Dashboard 元件(沿用) 來源:`local-tool/frontend/src/components/dashboard/` | 元件 | 用途 | 雲端版變更 | |------|------|-----------| | `StatCard` | 統計卡片(標題 + 數字 + icon) | 無 | | `ActivityTimeline` | 近期活動時間軸 | 無(但資料來源從 local store 改為雲端事件)| | `ConnectedDevicesList` | 已連線裝置列表(Dashboard 上的)| **小改動**:裝置項目要顯示「最後心跳 X 秒前」| --- ## 5. Devices 元件(沿用 + 擴充) 來源:`local-tool/frontend/src/components/devices/` | 元件 | 用途 | 雲端版變更 | |------|------|-----------| | `DeviceList` | 裝置卡片網格 | 無 | | `DeviceCard` | 單一裝置卡片(名稱、狀態、類型、韌體、已燒錄模型、操作按鈕)| **擴充**:右上角新增 `RemoteDeviceBadge`(見第 10 節)| | `DeviceStatus` / `DeviceStatusBadge` | 狀態點 + 文字徽章 | **新增狀態**:`offline`(遠端掉線)、`reconnecting`(重連中) | | `DeviceHealthCard` | 健康狀態(運行時間、韌體版本、最後活動)| **擴充**:新增「Pairing Token」、「最後心跳」欄位 | | `DeviceConnectionLog` | 連線歷史紀錄 | 無(但雲端版 log 應包含 tunnel 事件) | | `DeviceSettingsCard` | 裝置別名、備註 | 無 | | `FlashDialog` | 燒錄模型對話框 | 無(行為透過 tunnel 傳到 local agent) | | `FlashProgress` | 燒錄進度 | 無(WebSocket 透過雲端中繼) | ### 5.1 DeviceStatusBadge 擴充 **新增狀態(雲端版):** | 狀態 | 顏色 | i18n key | 文字(繁中)| |------|------|---------|-----------| | `offline` | `bg-gray-400` | `devices.status.offline` | 離線 | | `reconnecting` | `bg-yellow-400 animate-pulse` | `devices.status.reconnecting` | 重新連線中 | > 既有的 `detected` / `disconnected` 狀態在雲端版可能用不到(使用者不會直接「scan」遠端裝置)。雛形不刪除,但 UI 上會以 Pairing 流程取代 scan。 --- ## 6. Models 元件(沿用,無變更) 來源:`local-tool/frontend/src/components/models/` - `ModelCard` / `ModelGrid` / `ModelFilters` / `ModelDetail` / `ModelUploadDialog` / `ModelComparisonDialog` 雲端版行為差異:上傳 `.nef` 檔案走雲端儲存(S3-compat),但 UI 不變。 --- ## 7. Inference 元件(沿用,無變更) 來源:`local-tool/frontend/src/components/inference/` - `InferencePanel` / `PerformanceMetrics` / `ClassificationResult` / `VideoProgress` / `ConfidenceSlider` 雲端版行為差異:推論結果透過 WebSocket 從 local agent → 雲端 → 瀏覽器。UI 不變。 --- ## 8. Camera 元件(沿用,無變更) 來源:`local-tool/frontend/src/components/camera/` - `CameraInferenceView` / `CameraControls` / `SourceSelector` / `BatchImageThumbnails` 等 **雲端版注意:** - Camera stream 仍然走 local agent(硬體存取) - MJPEG stream 透過 tunnel 從 local agent → 雲端 → 瀏覽器顯示 - Image / Video 上傳:Phase 0 仍走 local agent 處理;Phase 1+ 考慮直上雲端 --- ## 9. 特殊元件(部分調整) 來源:`local-tool/frontend/src/components/` | 元件 | 用途 | 雲端版變更 | |------|------|-----------| | `OnboardingDialog` | 首次訪問引導 Dialog | **Phase 0 暫時保留**;Phase 1 重寫(現版本假設「插 USB Dongle」,雲端不適用)| | `GuidedTour` | driver.js 步驟引導 | **Phase 0 暫時保留**;Phase 1 重寫為 Cloud Tour | | `ServerStatusDashboard` | 後端 Go runtime 狀態 | **Phase 0 可移除 or 隱藏**(使用者看不到雲端 runtime);留供 Phase 1 重新定位 | | `ServerLogViewer` | 伺服器即時日誌 | 同上 | | `LangSync` | 語言狀態同步 | 無變更 | | `ThemeSync` | 主題跟隨系統 | 無變更 | | `StoreHydration` | Zustand store hydration | 無變更 | --- ## 10. 雲端版新增元件 **建議路徑**:`visionA-frontend/src/components/cloud/` ### 10.1 `UserMenu` **用途**:Sidebar 底部的使用者選單,雲端版必備。 **視覺**: ``` ┌──────────────────────────┐ │ [avatar] jim@example.com ▾ │ ← trigger └──────────────────────────┘ ↓ click ┌──────────────────────────┐ │ 帳號設定 (Account) │ │ 語言切換 (Language) │ │ ─────────────── │ │ 登出 (Sign out) │ └──────────────────────────┘ ``` **規格:** - 基底元件:Radix DropdownMenu(shadcn 封裝) - Trigger:Button variant=ghost,size=default - Avatar:40px 圓形(`rounded-full`),顯示使用者 Email 首字母 or GravatarURL - 下拉位置:`side="top" align="start"`(展開往上) - 項目圖示:Lucide `UserCog` / `Globe` / `LogOut` **i18n key:** - `account.menu.accountSettings` - `account.menu.language` - `account.menu.signOut` **雛形簡化**:Phase 0 `signOut` 直接跳回 `/login`(不接 API);`account.menu.accountSettings` 跳 `/account` stub 頁。 **無障礙:** - Trigger 可 Tab 聚焦、Enter / Space 展開 - 下拉內容鍵盤可 Arrow 導航 - `aria-label` = 使用者 Email --- ### 10.2 `PairingTokenCard` **用途**:`/devices/pair` 頁面的主要卡片,展示 Pairing Token 並提供複製功能。 **視覺**: ``` ┌────────────────────────────────────────────────┐ │ 🔗 Pairing Token │ │ │ │ 使用這組 token 讓你的 local agent 連上雲端 │ │ ─────────────────────────────────────── │ │ │ │ ┌────────────────────────────────────────┐ │ │ │ vAc_a1b2c3d4e5f6a7b8 │ │ │ │ c9d0e1f2a3b4c5d6e7f8 │ │ │ │ (vAc_ + 32 hex,視覺切兩行; │ │ │ │ 複製為完整 36 字元無空格) │ │ │ └────────────────────────────────────────┘ │ │ │ │ [📋 複製] [🔄 重新產生] │ │ │ │ ⏱ 剩餘 14:52 ──────────────── │ │ (進度條 bg-primary → amber(≤10:00) │ │ → red(≤3:00);過期轉灰 disabled) │ │ 📅 產生時間:14:30 │ └────────────────────────────────────────────────┘ ``` **規格:** - 基底:`Card` + CardHeader + CardContent + CardFooter - Token 格式:`vAc_` + 32 字元 hex(總長 36),TTL **15 分鐘**,一次性使用 - Token 顯示區:`bg-muted font-mono text-xl tracking-wider p-4 rounded-md select-all` - 視覺切兩行(第 1 行 `vAc_` + 16 hex,第 2 行 16 hex);Mobile 降為 `text-lg` - 複製到剪貼簿永遠是**完整 36 字元無空格無換行** - 過期狀態:`text-muted-foreground line-through` - 複製按鈕:`variant=default` + Lucide `Copy` icon;按下後暫態改為「已複製 ✓」2 秒,伴隨 toast;過期時 disabled - 重新產生:`variant=outline` + Lucide `RefreshCw`;點擊後 AlertDialog 確認「重新產生會讓舊 token 失效,新 token 有效期 15 分鐘」 - 倒數計時器(15 分鐘 TTL 必備): - 格式 `mm:ss`,1 秒更新一次 - 進度條 `h-1.5 w-full`,初始 `bg-primary`,≤ 10:00 → `bg-amber-500`,≤ 3:00 → `bg-red-500` 且卡片加 `ring-1 ring-red-300` - ≤ 0:30 發一次 Toast「Token 即將過期」 - 0:00 自動切過期狀態;主 CTA 變「重新產生 token」 **互動:** - 按複製:`navigator.clipboard.writeText(token)` + `showSuccess(t('pairing.copied'))` - 按重新產生:開 AlertDialog,確認後呼叫 API → 更新 token + 重置計時 **i18n key:** - `pairing.tokenTitle` - `pairing.tokenDescription` - `pairing.copy` / `pairing.copied` / `pairing.regenerate` - `pairing.expiresIn` / `pairing.generatedAt` - `pairing.regenerateConfirm` / `pairing.regenerateWarning` **無障礙:** - Token 區塊:`aria-label="Pairing token"`,`role="text"` - 複製按鈕:Enter / Space 可觸發 - 重新產生:AlertDialog 的焦點陷阱(shadcn 內建) --- ### 10.3 `RemoteDeviceBadge` **用途**:在雲端場景下顯示單一遠端裝置的連線狀態,取代 / 擴充既有 `DeviceStatusBadge`。 **視覺變體:** ``` 🟢 在線 ← bg-green-500 + 文字 🟡 重新連線中 (pulse) ← bg-yellow-400 animate-pulse ⚪ 離線・最後心跳 2 分鐘前 ← bg-gray-400 + 文字 + 次要文字(timestamp) 🔴 錯誤・無法建立 tunnel ← bg-red-500 + 文字 + tooltip 顯示錯誤詳情 ``` **規格:** - 基底:`div.flex.items-center.gap-2` - 狀態點:`h-2.5 w-2.5 rounded-full`(沿用 `DeviceStatusBadge` 樣式) - 狀態文字:`text-sm` - 次要資訊(最後心跳):`text-xs text-muted-foreground` - `pulse` 狀態:`animate-pulse` class **Props:** ```tsx interface RemoteDeviceBadgeProps { status: 'online' | 'offline' | 'reconnecting' | 'error'; lastSeenAt?: string; // ISO 8601 timestamp errorMessage?: string; // tooltip 顯示 size?: 'sm' | 'md'; // 預設 md } ``` **使用情境:** - `/devices` 裝置卡片右上角(取代 `DeviceStatusBadge`) - `/devices/[id]` 裝置詳情頁的大型狀態顯示(size=md) - Dashboard `ConnectedDevicesList` 的每個項目右側 - `/workspace/[deviceId]` 頂部(裝置掉線時要能即時看到) **i18n key(新增):** - `remote.status.online` → 在線 - `remote.status.offline` → 離線 - `remote.status.reconnecting` → 重新連線中 - `remote.status.error` → 連線錯誤 - `remote.lastSeen` → 最後心跳 {time} - `remote.lastSeenNever` → 從未連線 **無障礙:** - 角色:`role="status"` + `aria-live="polite"`(狀態改變時通知 SR) - 不只靠顏色:圓點 + 文字雙保險 - 錯誤狀態提供 `aria-describedby` 指向 tooltip 的完整訊息 **時間格式化(建議用 `date-fns` 或輕量 util):** - < 60 秒 → 「剛剛」 - < 60 分 → 「X 分鐘前」 - < 24 時 → 「X 小時前」 - ≥ 24 時 → 絕對時間「04/20 14:30」 --- ### 10.4 `NetworkErrorBanner` **用途**:全域警告橫幅,當雲端 API 不可達時顯示於 Header 下方。 **視覺**: ``` ┌────────────────────────────────────────────────────────┐ │ ⚠ 連線中斷 — 無法連上雲端服務。正在重試... [立即重試] │ └────────────────────────────────────────────────────────┘ ``` **規格:** - 背景:`bg-amber-50 dark:bg-amber-950/30` - 邊框:`border-b border-amber-300 dark:border-amber-700` - 文字:`text-amber-800 dark:text-amber-200 text-sm` - 圖示:Lucide `AlertTriangle`,`text-amber-600` - 位置:Header 下方 sticky(`sticky top-14 z-40`) - 右側按鈕:`Button variant=outline size=sm` **狀態變化:** - API 正常 → 不顯示 - API 失敗 > 3 次連續 → 顯示 banner - API 恢復 → banner 切換為「已恢復連線」短暫顯示 3 秒後消失 **i18n key:** - `network.disconnected.title` → 連線中斷 - `network.disconnected.description` → 無法連上雲端服務。正在重試... - `network.disconnected.retryButton` → 立即重試 - `network.restored` → 已恢復連線 ✓ **無障礙:** - `role="alert"` + `aria-live="assertive"`(重要警告) - 重試按鈕:`aria-label="重試連線"` --- ## 11. 元件狀態覆蓋檢查表(給 Frontend QA 與 Design QA) 每個互動元件都應有以下狀態: | 狀態 | 所有按鈕 / Input | 卡片列表 | 頁面 | 資料載入 | |------|-----------------|---------|------|---------| | Default | ✅ | ✅ | ✅ | — | | Hover | ✅ | ✅(整個卡片)| — | — | | Active / Pressed | ✅ | — | — | — | | Focus-visible | ✅ | ✅ | — | — | | Disabled | ✅ | — | — | — | | Loading | ✅(Loader2 icon)| ✅(skeleton)| ✅(skeleton)| ✅(skeleton)| | Empty | — | ✅(EmptyState)| ✅(EmptyState)| — | | Error | — | ✅(error banner)| ✅(error page)| ✅(retry)| | Success | ✅(toast)| — | — | — | --- ## 12. UX Writing 規範(簡版,完整版見 Flow 子檔) **產品語調:** - 對象是開發者,可用技術術語(如「Pairing Token」、「tunnel」、「session」)但不濫用 - 錯誤訊息要**說清楚發生什麼事 + 使用者能做什麼** - 避免過度客氣(不需要「請」「您」滿天飛) - 繁中台灣用語(不用「鏈接」用「連結」;不用「您」頻繁出現) **常用文案(整合到 i18n):** | 場景 | 繁中 | English | |------|------|---------| | 載入中 | 載入中... | Loading... | | 儲存成功 | 已儲存 | Saved | | 儲存失敗 | 儲存失敗,請重試 | Save failed, please retry | | 刪除確認 | 確定要刪除「{name}」嗎?此操作無法復原。 | Delete "{name}"? This cannot be undone. | | 空列表 | 還沒有任何紀錄 | Nothing here yet | | API 錯誤 | 連線失敗,請檢查網路或稍後再試 | Connection failed, check network or try later | | 權限不足 | 沒有權限進行此操作 | No permission for this action | | Session 過期 | 登入已過期,請重新登入 | Session expired, please sign in | --- ## 13. 實作建議(給 Frontend Agent) 1. **Monorepo 結構**:若 `visionA-frontend` 與 `local-tool` 之後都在同 repo,可抽出 `packages/ui/`、`packages/i18n/` 共用,避免重複。Phase 0 不需要,直接複製即可。 2. **新元件檔案放 `src/components/cloud/`**,命名清晰,不要與既有元件混放。 3. **測試**:新增的元件(`UserMenu`、`PairingTokenCard`、`RemoteDeviceBadge`、`NetworkErrorBanner`)都要有對應 Vitest 測試。 4. **Storybook / Playground**:Phase 0 不要求;Phase 1 考慮引入 Storybook。