Splash 進度: - app.go 新增 bootstrapStatus field + GetBootstrapStatus() binding - 各 startup step 呼叫 setBootstrapStatus 更新文字: "正在初始化 Python 環境..." "正在解壓 Python runtime (~10 秒)..." "正在建立 Python 虛擬環境 (~5 秒)..." "正在安裝 N 個 Python 套件 (numpy / opencv / KneronPLUS ...) (~30-60 秒)..." "正在安裝 Kneron USB 驅動程式 (請在 UAC 視窗點「是」)..." "正在準備應用程式資料..." "正在啟動伺服器..." "等待伺服器就緒..." "載入主介面..." - visiona-local/frontend/app.js 每 400ms 呼叫 GetBootstrapStatus 更新畫面 - wailsjs/go/main/App.js 手動補上新 binding export(避免等 wails generate) Timeout: - splash MAX_WAIT_MS 60s → 240s(涵蓋 UAC 被拖延 + 慢速硬碟) - healthCheckTimeout 15s → 30s(server 首次啟動內部解析 + embed fs 載入) 設定 > 硬體 > 執行模式: - 顯示預設值從 mock 改為 real(跟 app.go 實際預設對齊 - Q8 決策) - 下拉選單寬度 240 → 420px 避免文字被截斷 - i18n 說明文字改為「預設為真實硬體模式,強制 Mock 請設 VISIONA_MOCK=1」 - 仍標 disabled — 未來 M8+ 會連 backend GET /api/system/config Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
75 lines
2.4 KiB
JavaScript
75 lines
2.4 KiB
JavaScript
// visionA Local — splash / bootstrap
|
||
// 職責:顯示 app 啟動進度 → server 就緒後跳轉到 Next.js 主 UI
|
||
|
||
import { GetServerStatus, GetServerURL, GetBootstrapStatus } from './wailsjs/go/main/App.js';
|
||
|
||
const statusEl = document.getElementById('status');
|
||
const errorEl = document.getElementById('error');
|
||
|
||
const POLL_INTERVAL_MS = 400;
|
||
// 首次啟動最長容忍時間:venv 解壓(10s) + 建 venv(5s) + pip install wheels(30-60s) +
|
||
// libwdi driver install with UAC(15-30s) + server spawn(3s) + health check(2s) ≈ 60-110s
|
||
// 給到 240 秒以涵蓋慢速硬碟 / UAC 被使用者拖延的情況
|
||
const MAX_WAIT_MS = 240_000;
|
||
const startTime = Date.now();
|
||
|
||
let lastStatus = '';
|
||
|
||
async function poll() {
|
||
const elapsed = Date.now() - startTime;
|
||
if (elapsed > MAX_WAIT_MS) {
|
||
showError(
|
||
`啟動逾時(${Math.round(MAX_WAIT_MS / 1000)} 秒)。\n` +
|
||
'請關閉此視窗並重新啟動應用程式,或查看 log:\n' +
|
||
'%APPDATA%\\visiona-local\\logs\\wails.log'
|
||
);
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 先更新 bootstrap 進度文字(venv / pip / driver / server...)
|
||
const bootstrapMsg = await GetBootstrapStatus();
|
||
if (bootstrapMsg && bootstrapMsg !== lastStatus) {
|
||
statusEl.textContent = bootstrapMsg;
|
||
lastStatus = bootstrapMsg;
|
||
}
|
||
|
||
// 檢查 server 是否已就緒
|
||
const status = await GetServerStatus();
|
||
if (status && status.lastError) {
|
||
showError('伺服器啟動失敗:' + status.lastError);
|
||
return;
|
||
}
|
||
if (status && status.running && status.url) {
|
||
statusEl.textContent = '載入主介面...';
|
||
window.location.replace(status.url + '/');
|
||
return;
|
||
}
|
||
|
||
// 備用:直接問 URL
|
||
const url = await GetServerURL();
|
||
if (url) {
|
||
statusEl.textContent = '載入主介面...';
|
||
window.location.replace(url + '/');
|
||
return;
|
||
}
|
||
} catch (e) {
|
||
// binding 尚未就緒時會 throw,繼續輪詢
|
||
}
|
||
|
||
setTimeout(poll, POLL_INTERVAL_MS);
|
||
}
|
||
|
||
function showError(msg) {
|
||
statusEl.hidden = true;
|
||
errorEl.textContent = msg;
|
||
errorEl.hidden = false;
|
||
}
|
||
|
||
// 等 Wails runtime 就緒再開始輪詢
|
||
if (window.runtime) {
|
||
poll();
|
||
} else {
|
||
window.addEventListener('load', () => setTimeout(poll, 200));
|
||
}
|