依 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>
12 KiB
v2/code-reuse-v2.md — 沿用 vs 改寫 vs 新寫
所屬:TDD v2 §2.8 目的:以 v1 (M1-M7 已完成)為基準,量化 v2 refactor 的沿用率 承接:
architect-analysis-round2-refactor.md§D 結論:整體沿用率 85-95%(三方共識 #1),不是丟掉重做,是擴充 + 砍功能
1. 模組沿用率總表
| 模組 | 現況大小 | 改動量 | 沿用率 | 關鍵影響 |
|---|---|---|---|---|
server/ Go 後端 |
~15k LoC | ~400 行修改 + 新增 boot-id endpoint | ~96% | 砍 yt-dlp + Mock + 新增 CORS whitelist + boot-id;預置模型 / inference / camera pipeline / Python bridge / WebSocket / static serving 全部不動 |
server/web/out/ go:embed 的 Next.js static |
~20k LoC | ~100 行修改 + ~250 行新增(offline overlay + boot-id watcher + i18n) | ~88% | 砍 URL tab + Mock UI;新增 Offline Overlay + boot-id watcher;業務頁面全部不動 |
visiona-local/app.go Wails 殼 |
1584 行 | ~350 行新增 / ~30 行刪改 | ~85% | 新增 ServerController + LogBuffer + Preferences;watchServer 改為 Error state;mockMode 欄位與 VISIONA_MOCK 砍除;核心 startup / shutdown / single-instance / driver installer 全部不動 |
visiona-local/frontend/ Wails 內嵌 UI |
211 行(html + js + css) | 三檔全部改寫 + 新增 12 檔 | 程式面砍掉重寫,概念承襲 | 沿用 ES module + wailsjs 串接模式,但內容是全新控制台 UI |
installer/ 打包腳本 |
macOS dmgbuild / Windows iss / Linux AppImage | 各 ~3 行修改 | ~98% | 只改 ffmpeg/ffprobe/LGPL licnese 的 copy,其餘不變 |
Makefile |
570 行 | ~60 行修改 + ~80 行新增(macOS build) | ~85% | 砍 yt-dlp targets;改 Windows/Linux ffmpeg URL;新增 macOS 自 build |
scripts/bootstrap-*.sh/.ps1 |
中等 | 各 ~5 行修改 | ~99% | 只砍 yt-dlp 參照 |
vendor/ |
現有依賴 | 砍 yt-dlp/;改 ffmpeg/;新增 ffmpeg/macos/ 進 git |
N/A | 內容差異顯著 |
總計:整個 repo 程式碼層 ~85-95% 沿用(依粗估方式不同,但結論是「絕大多數 M1-M7 投入仍有效」)。
2. 逐目錄細節
2.1 server/ — 96% 沿用
完全不動(> 99%):
server/internal/api/ws/— WebSocket hub / handlers(除 CheckOrigin 那個 helper 是 M8-8 新增)server/internal/camera/— 除video_source.go的 yt-dlp 函式(§2.2)+mock_camera.go(整檔刪)server/internal/device/— 除manager.go的 mockMode 條件 +driver/mock/目錄server/internal/inference/— 推論 service 邏輯server/internal/model/— 模型 repository / store / uploadserver/internal/flash/— flash service(Kneron 韌體燒錄,v1 已保留)server/pkg/logger/— logger + broadcasterserver/scripts/kneron_bridge.py— Python KneronPLUS bridgeserver/data/— 預置 .nef 模型
修改(< 5% 程式碼):
server/main.go第 86-87 行(log message)、第 89-95 行(註解)、第 142 行(NewManager 簽名)、第 147 行(NewManager 簽名)server/internal/api/router.go第 83 行(刪/media/url)+ 新增/api/system/boot-idserver/internal/api/handlers/camera_handler.go第 251 行(擴充副檔名)+ 第 341-497 行(刪 yt-dlp 相關)server/internal/api/handlers/system_handler.go新增BootIDmethod + constructor 新增 bootID 參數server/internal/api/middleware.go整檔覆寫(CORS 白名單)server/internal/config/config.go刪 MockMode / MockCamera / MockDeviceCountserver/internal/deps/checker.go刪 yt-dlp entry + 註解更新server/internal/camera/video_source.go砍ResolveWithYTDLP/friendlyYTDLPError;可能砍NewVideoSourceFromURL(視 grep 結果)server/internal/camera/manager.go砍 mockMode / mockCameraserver/internal/device/manager.go砍 mockMode / mockCount
新增:
server/internal/api/ws/origin.go— CheckOrigin helper(~30 行)server/internal/api/middleware_test.go— TestIsAllowedOrigin(~50 行)
2.2 server/web/out/(go:embed 的 Next.js SPA,實際源碼在 frontend/)— 88% 沿用
完全不動:
frontend/src/app/dashboard//devices//models//workspace/— 業務頁面frontend/src/components/device//model//inference/— 業務元件frontend/src/lib/api/— API clientfrontend/src/stores/除camera-store.ts的startFromUrl
修改:
frontend/src/components/camera/source-selector.tsx— 砍 URL tab,擴充副檔名frontend/src/stores/camera-store.ts— 砍startFromUrlfrontend/src/app/settings/page.tsx— 砍 Mock 模式切換frontend/src/app/layout.tsx— 掛<ServerOfflineOverlay />+<BootIdWatcherMount />frontend/src/lib/i18n/{types,zh-TW,en}.ts— 砍 4 個 yt-dlp keys + 4 個 Mock keys +noDevices文案 + 新增serverOffline區塊
新增:
frontend/src/stores/system-store.ts(~40 行)frontend/src/hooks/use-boot-id-watcher.ts(~70 行)frontend/src/components/server-offline-overlay.tsx(~90 行)frontend/src/components/boot-id-watcher-mount.tsx(~10 行)
2.3 visiona-local/app.go — 85% 沿用
完全不動的 v1 邏輯:
startup()的 data dir migration / single-instance lock / IPC server / seed user data dirshutdown()的 watchCancel / ipcListener.Close / releaseLockreportFatal()+showNativeError()三平台原生對話框(保留給 startup 致命錯誤)ensurePythonRuntime()/findSystemPython()/ensureBundledPython()完整 Python 雙策略ensureDriverInstalled()/markDriverInstalled()/InstallKneronDriver()— Kneron WinUSB driver 安裝locateServerBinary()/pickPort()/waitHealthy()/writeIPCPort()— 這些 helper 不動seedUserDataDir()— 首次啟動的資料 seedconfigureSysProcAttr()/openBrowser()跨平台封裝
修改:
- 第 83 行:砍
mockMode欄位;新增ctrl/logBuf/prefs/startupPipeline/pipelineCancelFn欄位(R5-D3 規則:每次 Start 都開瀏覽器,不需 per-session 的 flag;詳見server-lifecycle.md§11.5) - 第 119-120 行:砍 VISIONA_MOCK env 讀取
- 第 313-326 行:砍
GetBootstrapStatus/setBootstrapStatus/bootstrapStatus - 第 425-564 行:
startServer()改名為startServerV2(),內容改為- 用
StdoutPipe / StderrPipe而非 file - 啟動兩個
logPumpgoroutine - mockMode 相關條件全砍
--mockarg 拿掉
- 用
- 第 571-610 行:
watchServer()的reportFatal + os.Exit改為ctrl.setState(Error)+ event emit
新增 bindings(13 個 method,見 v2/control-panel.md §4.2):
StartServer/StopServer/RestartServerGetServerStatus(改版)/GetRecentLogs/ClearLogsGetSystemInfoOpenInBrowser/RevealLogsFolder/ExportLogGetPreferences/SetPreferencesRestartStartupSequence(v2.1:Startup Error state 的 Retry 按鈕;見startup-pipeline.md§9)
2.4 visiona-local/ 新增檔案
visiona-local/server_control.go(~250 行)— ServerController + startServerV2 + logPumpvisiona-local/log_buffer.go(~120 行)— LogBuffer ring buffervisiona-local/preferences.go(~60 行)— JSON load/save
2.5 visiona-local/main.go — 修改 < 5 行
新增 OnBeforeClose handler;其餘不動。
2.6 visiona-local/frontend/ — 改寫
原本(M7-B 後):index.html 25 行 + app.js 74 行 + style.css 112 行 = 211 行 splash。
v2:
index.html~80 行(控制台 layout)app.js~130 行(init + binding 呼叫 + event 訂閱)style.css~300 行(控制台樣式 + dark/light tokens)- 新增 components/×4 + i18n/×3 + icons/×6 + wailsjs/(自動生成)
概念沿用:ES module + import wailsjs + EventsOn 訂閱模式——v1 M7-B splash 就是這樣寫的,v2 延續這個模式,只是元件更多。
程式碼沿用:0。三個檔案的內容全部重寫。但這三個檔案加起來才 211 行,重寫 2 天是合理的。
2.7 installer/ — 98% 沿用
installer/windows/visiona-local.iss— 改 1 行(砍 yt-dlp)+ 加 2 行(ffprobe + license)installer/linux/build-appimage.sh— 改 1 行(砍 yt-dlp 從 for loop)- macOS
dmgbuild路徑完全不動(dmg 是從visiona-local/build/bin/visiona-local.app直接建,ffmpeg/ffprobe 已透過 payload-macos 置入)
2.8 Makefile — 85% 沿用
新增 target:vendor-ffmpeg-macos-build(~50 行)
修改:
vendor-ffmpeg改為驗證(~10 行)vendor-ffmpeg-windowsURL + Python zipfile 提取清單擴充為 ffmpeg + ffprobe + LICENSEvendor-ffmpeg-linuxURL + 解壓邏輯 + 加抓 ffprobepayload-macos / payload-windows / payload-linux全部改為cp ffmpeg + ffprobe,砍cp yt-dlp
刪除:
vendor-ytdlp/vendor-ytdlp-windows/vendor-ytdlp-linux三個 target- 三個
YTDLP_URL_*變數 vendor-sync中的vendor-ytdlp依賴
2.9 scripts/bootstrap-*.sh/.ps1 — 99% 沿用
只改刪 yt-dlp 參照,其他完全不動。
2.10 vendor/ 目錄
| 子目錄 | v1 | v2 | 備註 |
|---|---|---|---|
vendor/python/ |
進 vendor(不進 git) | 不變 | - |
vendor/wheels/ |
進 vendor | 不變 | - |
vendor/ffmpeg/darwin/ |
進 vendor(不進 git,GPL build) | 目錄改名 vendor/ffmpeg/macos/ |
進 git,LGPL build |
vendor/ffmpeg/windows/ |
進 vendor | 同路徑,LGPL build | - |
vendor/ffmpeg/linux/ |
進 vendor | 同路徑,LGPL build | - |
vendor/yt-dlp/ |
進 vendor | 整個刪除 | R5-7 |
3. 文字統計估算
| 類別 | 行數 |
|---|---|
| v2 新增 Go 程式碼 | ~600 行(server_control.go + log_buffer.go + preferences.go + boot-id handler + CORS middleware + origin.go + middleware_test.go) |
| v2 新增 JS/TS 程式碼 | ~700 行(Wails 控制台 5 JS 檔 + 6 SVG + 2 JSON + frontend Offline Overlay 4 檔) |
| v2 修改 Go 程式碼 | ~400 行 diff |
| v2 修改 TS 程式碼 | ~200 行 diff |
| v2 修改 Makefile / scripts / installer | ~100 行 diff |
| v2 刪除 Go 程式碼 | ~500 行(mock_driver.go 183 + mock_camera.go 95 + camera_handler.go yt-dlp 段 160 + video_source.go yt-dlp 段 50) |
| v2 刪除 TS 程式碼 | ~80 行(source-selector URL tab 70 + camera-store startFromUrl 30 - 扣除同 section 重複計算) |
| v2 刪除 Makefile / scripts | ~60 行 |
總 diff 量:~2600 行(新增 + 修改 + 刪除) 總 repo 規模:~40k LoC diff 比例:~6.5%
→ 換算成淨沿用率:~93%,落在三方共識的「85-95%」區間上半部。
4. 為什麼沿用率這麼高
M1-M7 的投資集中在「打包」這件事上(vendor 機制、dmg/iss/AppImage、Python runtime 雙策略、single-instance、data dir migration、Kneron driver installer),這些完全不動。
v2 的 refactor 集中在:
- 把業務 UI 從 Wails WebView 搬到瀏覽器(純移動,Next.js 源碼不動)
- Wails 視窗內容從 splash 換成控制台(全新 UI,但只有 ~700 行程式碼)
- 砍兩個功能(yt-dlp, Mock)— 都是局部刪除,不觸及核心架構
- 切 ffmpeg 授權(純 vendor 層,不動執行邏輯)
等於說 v2 是在 v1 基礎上做「加工」,不是推倒重來。原本這個 refactor 有可能變成「丟掉重做」的惡夢,但 M7-B 已經把 Next.js UI 從 Wails binding 強耦合解開變成純 HTTP,少了這個前置條件 v2 真的會很痛。
5. 被保留但可能失去商業意義的部分
這些 v2 不砍,但隨 R5 決策的不同方向它們的重要性變低了:
- Python runtime 雙策略 + PBS 內嵌(M3 work) — 仍然需要,Kneron inference 必須有 Python。保留。
- 驗證 driver installer(M4 Windows WinUSB) — 仍然需要。保留。
- 預置 .nef 模型(8 個,73 MB) — 仍然需要。保留。
- Kneron KL520/KL720 韌體檔案 — 仍然需要(最近才補上)。保留。
- splash 進度訊息
setBootstrapStatus— 砍掉(R5 下 Wails 視窗是控制台,沒有「loading」階段,Start 是 async 不阻塞 UI)。
結論:v2 refactor 的沿用率接近 93%,剩下的 7% diff 主要是新增控制台 UI 與整合 boot-id 機制,總工時 ~10 人天是合理估算。