jim800121chen 8cd5751ce3 feat(local-tool): M8 重構 — Wails 控制台 + 瀏覽器 Web UI(R5 決策)
依 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>
2026-04-15 17:57:54 +08:00

12 KiB
Raw Blame History

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 + PreferenceswatchServer 改為 Error statemockMode 欄位與 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 / upload
  • server/internal/flash/ — flash serviceKneron 韌體燒錄v1 已保留)
  • server/pkg/logger/ — logger + broadcaster
  • server/scripts/kneron_bridge.py — Python KneronPLUS bridge
  • server/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-id
  • server/internal/api/handlers/camera_handler.go 第 251 行(擴充副檔名)+ 第 341-497 行(刪 yt-dlp 相關)
  • server/internal/api/handlers/system_handler.go 新增 BootID method + constructor 新增 bootID 參數
  • server/internal/api/middleware.go 整檔覆寫CORS 白名單)
  • server/internal/config/config.go 刪 MockMode / MockCamera / MockDeviceCount
  • server/internal/deps/checker.go 刪 yt-dlp entry + 註解更新
  • server/internal/camera/video_source.goResolveWithYTDLP / friendlyYTDLPError;可能砍 NewVideoSourceFromURL(視 grep 結果)
  • server/internal/camera/manager.go 砍 mockMode / mockCamera
  • server/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 client
  • frontend/src/stores/camera-store.tsstartFromUrl

修改

  • frontend/src/components/camera/source-selector.tsx — 砍 URL tab擴充副檔名
  • frontend/src/stores/camera-store.ts — 砍 startFromUrl
  • frontend/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 dir
  • shutdown() 的 watchCancel / ipcListener.Close / releaseLock
  • reportFatal() + showNativeError() 三平台原生對話框(保留給 startup 致命錯誤)
  • ensurePythonRuntime() / findSystemPython() / ensureBundledPython() 完整 Python 雙策略
  • ensureDriverInstalled() / markDriverInstalled() / InstallKneronDriver() — Kneron WinUSB driver 安裝
  • locateServerBinary() / pickPort() / waitHealthy() / writeIPCPort() — 這些 helper 不動
  • seedUserDataDir() — 首次啟動的資料 seed
  • configureSysProcAttr() / 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
    • 啟動兩個 logPump goroutine
    • mockMode 相關條件全砍
    • --mock arg 拿掉
  • 第 571-610 行:watchServer()reportFatal + os.Exit 改為 ctrl.setState(Error) + event emit

新增 bindings13 個 methodv2/control-panel.md §4.2

  • StartServer / StopServer / RestartServer
  • GetServerStatus(改版)/ GetRecentLogs / ClearLogs
  • GetSystemInfo
  • OpenInBrowser / RevealLogsFolder / ExportLog
  • GetPreferences / SetPreferences
  • RestartStartupSequencev2.1Startup Error state 的 Retry 按鈕;見 startup-pipeline.md §9

2.4 visiona-local/ 新增檔案

  • visiona-local/server_control.go~250 行)— ServerController + startServerV2 + logPump
  • visiona-local/log_buffer.go~120 行)— LogBuffer ring buffer
  • visiona-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% 沿用

新增 targetvendor-ffmpeg-macos-build~50 行)

修改

  • vendor-ffmpeg 改為驗證(~10 行)
  • vendor-ffmpeg-windows URL + Python zipfile 提取清單擴充為 ffmpeg + ffprobe + LICENSE
  • vendor-ffmpeg-linux URL + 解壓邏輯 + 加抓 ffprobe
  • payload-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不進 gitGPL build 目錄改名 vendor/ffmpeg/macos/ 進 gitLGPL 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 集中在:

  1. 把業務 UI 從 Wails WebView 搬到瀏覽器純移動Next.js 源碼不動)
  2. Wails 視窗內容從 splash 換成控制台(全新 UI但只有 ~700 行程式碼)
  3. 砍兩個功能yt-dlp, Mock— 都是局部刪除,不觸及核心架構
  4. 切 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 installerM4 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 人天是合理估算。