mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-20 08:57:57 +00:00
fix(gui): desktop shell stability — gated ready state, iframe retry with backoff, theme toggle
- coreReady now only flips after the embedded core actually answers HTTP (fixes blank-screen race where the iframe loaded before the listener) - renderer probes reachability, retries frame load with exponential backoff, and recovers via onerror instead of giving up forever - keep per-user theme in localStorage - embedded profile no longer preconfigures a zen source (desktop users add their own upstreams on the sources page)
This commit is contained in:
@ -53,6 +53,7 @@ let mainWindow = null;
|
||||
let tray = null;
|
||||
let coreProc = null;
|
||||
let coreStartedAt = 0;
|
||||
let coreReady = false; // true only after the embedded core actually answers HTTP
|
||||
let settings = {}; // { port, autoStart, minimizeToTray }
|
||||
let authRules = [];
|
||||
|
||||
@ -120,9 +121,9 @@ function coreStarted() {
|
||||
|
||||
function writeDefaultEmbeddedConfig() {
|
||||
// Generate a self-contained embedded profile with a fresh random admin key.
|
||||
// (Like Clash Verge generates its profile; the core's EnsureDefault default
|
||||
// is 127.0.0.1:8080 — we want our own port + key on first run.) If a config
|
||||
// already exists we do NOT overwrite it (keep user's edits).
|
||||
// No upstream sources are preconfigured — the desktop user adds them via
|
||||
// the WebUI's sources page (like Clash Verge's empty-by-default profile).
|
||||
// If a config already exists we do NOT overwrite it (keep user's edits).
|
||||
if (fs.existsSync(CONFIG_FILE)) return;
|
||||
const key = "sk-gw-" + crypto.randomBytes(16).toString("hex");
|
||||
const yaml = [
|
||||
@ -132,16 +133,6 @@ function writeDefaultEmbeddedConfig() {
|
||||
"adapter_dir: " + JSON.stringify(path.join(PROFILE_DIR, "adapters")),
|
||||
"runtime_file: " + JSON.stringify(path.join(PROFILE_DIR, "runtime.json")),
|
||||
"",
|
||||
"sources:",
|
||||
" - name: zen",
|
||||
" base_url: https://opencode.ai/zen/v1",
|
||||
" api_key: public",
|
||||
" adapter: opencode",
|
||||
" models:",
|
||||
" - id: deepseek-v4-flash-free",
|
||||
" priority: 100",
|
||||
" kind: chat",
|
||||
"",
|
||||
// Admin key as a plain `keys` entry (no seed flag) so the web UI never
|
||||
// shows the replace-the-initial-key warning. gateway_keys entries are
|
||||
// marked seed:true by the core.
|
||||
@ -211,6 +202,7 @@ function startCore() {
|
||||
coreProc.on("exit", (code) => {
|
||||
const wasManaged = coreStartedAt > 0;
|
||||
coreStartedAt = 0;
|
||||
coreReady = false;
|
||||
coreProc = null;
|
||||
console.log("core exited:", code);
|
||||
// auto-restart if still running GUI (crash recovery, max every ~5s)
|
||||
@ -223,6 +215,12 @@ function startCore() {
|
||||
}, 1500);
|
||||
}
|
||||
});
|
||||
// Broadcast starting (NOT ready) immediately. The renderer must not
|
||||
// attempt to load the iframe until the core actually answers HTTP —
|
||||
// otherwise the frame load races the listener and fails permanently.
|
||||
// (Fix for blank-screen: running=true was previously broadcast while the
|
||||
// core was still booting, so the iframe ERR_CONNECTION_REFUSED and the
|
||||
// renderer never retried.)
|
||||
notifyCoreState();
|
||||
// pump until the core answers, then inject the admin key and tell the
|
||||
// renderer the web UI is ready (the iframe must load only after auth is set)
|
||||
@ -230,6 +228,7 @@ function startCore() {
|
||||
.then((ok) => {
|
||||
if (ok) {
|
||||
setEmbeddedAuth();
|
||||
coreReady = true;
|
||||
notifyCoreState();
|
||||
}
|
||||
})
|
||||
@ -255,6 +254,7 @@ let coreLastExit = 0;
|
||||
|
||||
function stopCore() {
|
||||
app.isQuitting = true;
|
||||
coreReady = false;
|
||||
if (coreProc && coreProc.exitCode === null) {
|
||||
try {
|
||||
coreProc.kill();
|
||||
@ -438,7 +438,8 @@ function createTray() {
|
||||
function notifyCoreState() {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send("core:state", {
|
||||
running: coreStarted(),
|
||||
running: coreStarted() && coreReady,
|
||||
ready: coreReady,
|
||||
startedAt: coreStartedAt,
|
||||
});
|
||||
}
|
||||
@ -560,7 +561,8 @@ ipcMain.handle(
|
||||
);
|
||||
|
||||
ipcMain.handle("core:state", () => ({
|
||||
running: coreStarted(),
|
||||
running: coreStarted() && coreReady,
|
||||
ready: coreReady,
|
||||
startedAt: coreStartedAt,
|
||||
baseUrl: embeddedBaseUrl(),
|
||||
port: settings.port || DEFAULT_PORT,
|
||||
|
||||
Reference in New Issue
Block a user