依 R5 五輪決策把 visionA-local 從「Wails 內嵌 Next.js」重構為「Wails
本機伺服器控制台 + 瀏覽器 Web UI」模式(類比 Docker Desktop / Ollama)。
程式碼變動
- M8-1 砍 yt-dlp 全套(後端 resolver / URL handler / 前端 URL tab /
Makefile vendor / installer / bootstrap / CI workflow,-555 行)
- M8-2 砍 Mock 模式全套(driver/mock、mock_camera、Settings runtimeMode、
VISIONA_MOCK 環境變數,-528 行)
- M8-3 ffmpeg 從 GPL 切換到 LGPL 混合方案:Windows/Linux 用 BtbN 現成
LGPL binary,macOS 自 build minimal decoder-only 進 git
(vendor/ffmpeg/macos/ffmpeg 5.7MB + ffprobe 5.6MB,比 GPL 版省 85% 空間)
- M8-4 Wails Server Controller:state machine、log ring buffer 2000 行、
preferences.json atomic write、boot-id、Gin SkipPaths、shutdown 7+1 秒、
notify_*.go 三平台 OS 通知、watchServer 改 Error state 不 os.Exit
- M8-4b 啟動階段管線 R5-E:6 階段進度 event、20s soft / 60s hard timeout、
stage 5/6 skip 規則、sentinel file、RestartStartupSequence 5 步驟
- M8-5 Wails 控制台 vanilla HTML/JS/CSS(9 檔 ~2012 行)取代 M7-B splash:
state 視覺、log panel、startup progress panel、Stage 6 manual CTA
pulse、shutdown modal、Settings、Dark Mode、i18n 中英雙語
- M8-6 上傳影片副檔名擴充(mp4/avi/mov/mpeg/mpg)
- M8-7 Web UI Server Offline Overlay(role=alertdialog + focus trap +
wsEverConnected 容錯 + Page Visibility)
- M8-8 CORS middleware(127.0.0.1/localhost only + suffix attack 防護)+
ws/origin.go 獨立 WebSocket CheckOrigin 避 package cycle
- MAJ-4 server:shutdown-imminent WebSocket broadcast 機制
(/ws/system endpoint + notifyShutdownImminent helper)
- M8-9 Boot-ID + 瀏覽器 tab 自動重連(sessionStorage loop guard)
品質
- ~105+ 新 unit test + race detector (-count=2) 全綠
- 10 個 milestone 全部通過 Reviewer 審查
- 三方 v2 + v2.1 文件(PRD / Design Spec / TDD)+ 交叉互審紀錄
收錄在 .autoflow/
交付前待處理(M8-10)
- 重跑 make payload-macos 把舊 GPL 77MB binary 換成新 LGPL
- 三平台 end-to-end build 驗證
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8.1 KiB
8.1 KiB
v2.3 — source-selector 修改規格
本章對應 R5(砍 URL 推論)+ R5-6(ffmpeg decoder 支援 mp4/avi/mov/mpeg/mpg)。 影響檔案:
frontend/src/components/camera/source-selector.tsx、frontend/src/stores/camera-store.ts、frontend/src/lib/i18n/zh-TW.ts、frontend/src/lib/i18n/en.ts、frontend/src/lib/i18n/types.ts上層索引:../design-spec-v2.md
1. 修改範圍摘要
砍:
- video tab 下的
URL mode(videoMode === 'url'分支整塊) videoModestate 本身(因為 video tab 只剩單一file模式)videoUrlstate、handleUrlSubmithandlerstartFromUrlstore action 呼叫端與 store 內實作- 「正在解析影片連結,YouTube 影片可能需要 10-30 秒…」的 hard-coded 紅字提示
- YouTube / Vimeo / RTSP 相關文案
- i18n keys:
camera.uploadFile、camera.pasteUrl、camera.urlPlaceholder、camera.urlHelpText
改:
<input accept>從.mp4,.avi,.mov→.mp4,.avi,.mov,.mpeg,.mpg- dropzone filter(若有影片 drop 支援)同步
- i18n key
camera.mp4AviMov值從MP4, AVI, MOV→MP4, AVI, MOV, MPEG - Video tab 內只剩一個「選擇影片」按鈕 + 格式說明文字,無 sub-mode 切換
保留:
isUploadingstate(上傳大檔仍需 loading)uploadVideoactionsourceFilename顯示- Camera / Image tabs 的所有邏輯完全不動
2. 新的 video tab UI 狀態表
2.1 結構(ASCII)
┌─ Video tab ────────────────────────────────────────────────┐
│ │
│ [ 選擇影片 ] MP4, AVI, MOV, MPEG │
│ │
│ (可選)dropzone:拖放影片檔到此 │
│ │
└──────────────────────────────────────────────────────────────┘
2.2 狀態對照表
| 狀態 | 條件 | 可見元素 | 按鈕狀態 |
|---|---|---|---|
| Idle(預設) | 非 streaming、非 uploading | [選擇影片] button + 格式說明 MP4, AVI, MOV, MPEG |
enabled |
| Uploading | isUploading === true |
[上傳中...] button(disabled)+ 格式說明 |
disabled |
| Streaming | isStreaming === true(由父元件處理,此 tab 的內容被 [停止影片] 按鈕取代) |
[停止影片] + 檔名 |
— |
注意:Video tab 不再有 Upload file / Paste URL 兩顆 sub-mode 切換按鈕;使用者點 tab 後直接就是上傳區。
3. i18n key 異動清單
3.1 新增 key
| Key | zh-TW | en | 說明 |
|---|---|---|---|
camera.mp4AviMovMpeg |
MP4, AVI, MOV, MPEG | MP4, AVI, MOV, MPEG | 取代舊的 mp4AviMov |
(也可以選擇直接改舊 key 的值,見 §3.3 的建議)
3.2 刪除 key(zh-TW / en / types 三檔都要刪)
| Key | 原值(zh-TW) | 刪除理由 |
|---|---|---|
camera.uploadFile |
上傳檔案 | Video tab 不再有 sub-mode 切換,camera.selectVideo 已足夠 |
camera.pasteUrl |
貼上連結 | URL mode 砍除 |
camera.urlPlaceholder |
https://example.com/video.mp4 |
同上 |
camera.urlHelpText |
支援 YouTube、直接影片 URL(.mp4 等)及 RTSP 串流。 | 同上 |
3.3 修改 key(保留但更新值)
| Key | 原值(zh-TW / en) | 新值(zh-TW / en) |
|---|---|---|
camera.mp4AviMov |
MP4, AVI, MOV / MP4, AVI, MOV |
MP4, AVI, MOV, MPEG / MP4, AVI, MOV, MPEG |
注意:key 名雖然寫 mp4AviMov,但值改為包含 MPEG。保留 key 名避免 rename 造成其他地方引用失效。或者也可以 rename 為 camera.supportedVideoFormats,若 rename,type 檔也要同步。建議 rename,更語義化。
3.4 camera.selectVideo 保留
文字「選擇影片 / Select video」不變,仍用於 video tab 的主 CTA。
4. 程式碼變更預期(示意,實作以 Frontend Agent 為準)
4.1 source-selector.tsx diff 摘要
刪除(大約 50 行):
const [videoMode, setVideoMode] = useState<'file' | 'url'>('file');const [videoUrl, setVideoUrl] = useState('');const handleUrlSubmit = async () => { ... }- 從 store 解構
startFromUrl {activeTab === 'video' && (...)}內的 sub-mode 切換按鈕[上傳檔案] / [貼上連結]- URL mode 的整個 JSX 分支(輸入框 + 「正在解析」提示 +
camera.urlHelpText) import { Input }如果其他地方沒用,連 import 一起砍
修改:
- Video tab 的結構從
<div className="flex flex-col gap-2 w-full">內含兩個 mode 分支,改為直接 render file upload 區 <input accept=".mp4,.avi,.mov">→<input accept=".mp4,.avi,.mov,.mpeg,.mpg">- 格式說明文字改為
t('camera.supportedVideoFormats')(或維持mp4AviMovkey)
最終 video tab JSX 預期樣貌:
{activeTab === 'video' && (
<div className="flex items-center gap-3">
<Button
onClick={() => videoFileRef.current?.click()}
disabled={isUploading}
>
{isUploading ? t('common.uploading') : t('camera.selectVideo')}
</Button>
<span className="text-sm text-muted-foreground">
{t('camera.supportedVideoFormats')}
</span>
<input
ref={videoFileRef}
type="file"
accept=".mp4,.avi,.mov,.mpeg,.mpg"
className="hidden"
onChange={handleVideoSelect}
/>
</div>
)}
4.2 camera-store.ts diff 摘要
刪除:
- interface 中的
startFromUrl: (url: string, deviceId: string) => Promise<void>; - 實作中的
startFromUrl: async (url, deviceId) => { ... }整塊
保留:
uploadVideo不動isUploadingstate 不動
4.3 i18n/zh-TW.ts、i18n/en.ts、i18n/types.ts
三檔同步:
- 刪
uploadFile、pasteUrl、urlPlaceholder、urlHelpText - 新增
supportedVideoFormats(或修改mp4AviMov值) - types 檔移除對應 type 定義
4.4 其他可能殘留
執行 grep 檢查:
grep -r "startFromUrl" frontend/src/grep -r "pasteUrl" frontend/src/grep -r "youtube\|vimeo\|RTSP" frontend/src/
若有殘留,一併清理。
5. 無障礙考量
Video tab 變為單一按鈕後:
| 項目 | 設計 |
|---|---|
| Tab order | camera → image → video → (內部) 選擇影片 button |
| Button aria-label | aria-label="選擇影片檔案,支援 MP4 AVI MOV MPEG" |
| Focus ring | 沿用 Button 元件預設 focus ring |
| Screen reader | Button 點擊開啟 native file picker,macOS VoiceOver / Windows Narrator 會自動 announce file dialog |
6. 回歸測試重點(給 Testing Agent)
- Video tab 點擊後直接看到
[選擇影片]按鈕,無 sub-mode 切換 - 支援上傳
.mp4、.avi、.mov、.mpeg、.mpg五種副檔名 - 不支援上傳
.mkv、.webm、.flv(ffmpeg LGPL minimal build 不含這些 decoder) - 上傳中顯示
上傳中...,按鈕 disabled - 上傳完成後開始 inference,
sourceFilename顯示正確檔名 - 全站 grep 不到任何
startFromUrl/pasteUrl/youtube/vimeo字串 - 兩個語系(zh-TW / en)都沒有 dead keys
- 舊版 i18n key 被刪後,TypeScript 編譯無 dangling reference
7. 與 v1 差異對照
| 面向 | v1 | v2 |
|---|---|---|
| Video 來源類型 | file + URL(YouTube / Vimeo / RTSP / 直接 .mp4) | 僅 file |
| 支援副檔名 | .mp4, .avi, .mov |
.mp4, .avi, .mov, .mpeg, .mpg |
| Sub-mode 切換 | 有(2 顆切換按鈕) | 無 |
| URL 輸入框 | 有 | 無 |
| 解析 URL 提示 | 有(「YouTube 影片可能需要 10-30 秒…」) | 無 |
| i18n key 數 | 含 4 個 URL 相關 key | 砍 4 個、新增 / 修改 1 個 |
startFromUrl store action |
有 | 無 |
下一步:交 Frontend Agent 按上述 spec 實作,完成後交 Reviewer 審查、Testing Agent 做回歸測試。