From 3420ee244720a598a3f459a1e52f2c0f75cee9ef Mon Sep 17 00:00:00 2001 From: Haze <709547807@qq.com> Date: Thu, 26 Feb 2026 22:57:03 +0800 Subject: [PATCH] fix(app-state): refactor quitting logic to use setQuitting function (#199) --- electron/main/app-state.ts | 5 +++++ electron/main/index.ts | 4 ++-- electron/main/updater.ts | 21 +++++++++------------ 3 files changed, 16 insertions(+), 14 deletions(-) create mode 100644 electron/main/app-state.ts diff --git a/electron/main/app-state.ts b/electron/main/app-state.ts new file mode 100644 index 000000000..3a9aa6c12 --- /dev/null +++ b/electron/main/app-state.ts @@ -0,0 +1,5 @@ +export let isQuitting = false; + +export function setQuitting(value = true): void { + isQuitting = value; +} diff --git a/electron/main/index.ts b/electron/main/index.ts index ad23187a8..7ea78987e 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -15,13 +15,13 @@ import { warmupNetworkOptimization } from '../utils/uv-env'; import { ClawHubService } from '../gateway/clawhub'; import { ensureClawXContext, repairClawXOnlyBootstrapFiles } from '../utils/openclaw-workspace'; +import { isQuitting, setQuitting } from './app-state'; // Disable GPU acceleration for better compatibility app.disableHardwareAcceleration(); // Global references let mainWindow: BrowserWindow | null = null; -let isQuitting = false; const gatewayManager = new GatewayManager(); const clawHubService = new ClawHubService(); @@ -239,7 +239,7 @@ app.on('window-all-closed', () => { }); app.on('before-quit', () => { - isQuitting = true; + setQuitting(); // Fire-and-forget: do not await gatewayManager.stop() here. // Awaiting inside before-quit can stall Electron's quit sequence. void gatewayManager.stop().catch((err) => { diff --git a/electron/main/updater.ts b/electron/main/updater.ts index 92f8cda09..735f3c108 100644 --- a/electron/main/updater.ts +++ b/electron/main/updater.ts @@ -10,6 +10,7 @@ import { autoUpdater, UpdateInfo, ProgressInfo, UpdateDownloadedEvent } from 'el import { BrowserWindow, app, ipcMain } from 'electron'; import { logger } from '../utils/logger'; import { EventEmitter } from 'events'; +import { setQuitting } from './app-state'; /** Base CDN URL (without trailing channel path) */ const OSS_BASE_URL = 'https://oss.intelli-spectrum.com'; @@ -209,22 +210,18 @@ export class AppUpdater extends EventEmitter { /** * Install update and restart. * - * On macOS, electron-updater's MacUpdater still delegates to Squirrel.Mac - * internally. Squirrel's quitAndInstall() is unreliable — it sometimes - * fails to trigger `before-quit`, leaving the tray close handler to - * intercept and hide the window instead of quitting. We force `app.quit()` - * after a short grace period as a safety net. + * On macOS, electron-updater delegates to Squirrel.Mac (ShipIt). The + * native quitAndInstall() spawns ShipIt then internally calls app.quit(). + * However, the tray close handler in index.ts intercepts window close + * and hides to tray unless isQuitting is true. Squirrel's internal quit + * sometimes fails to trigger before-quit in time, so we set isQuitting + * BEFORE calling quitAndInstall(). This lets the native quit flow close + * the window cleanly while ShipIt runs independently to replace the app. */ quitAndInstall(): void { logger.info('[Updater] quitAndInstall called'); + setQuitting(); autoUpdater.quitAndInstall(); - - if (process.platform === 'darwin') { - setTimeout(() => { - logger.warn('[Updater] macOS: forcing app.quit() (Squirrel fallback)'); - app.quit(); - }, 3_000).unref(); - } } /**