From e5b7b65dd470a40feb09ef4018f7700c2232f184 Mon Sep 17 00:00:00 2001 From: NSIETeam Date: Mon, 13 Jul 2026 18:30:49 +0800 Subject: [PATCH 1/3] fix: resolve model switch UI freeze on edge cases --- packages/vscode-ui-plugin/src/extension.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/vscode-ui-plugin/src/extension.ts b/packages/vscode-ui-plugin/src/extension.ts index 85b41b8d..0291eb2d 100644 --- a/packages/vscode-ui-plugin/src/extension.ts +++ b/packages/vscode-ui-plugin/src/extension.ts @@ -2574,6 +2574,8 @@ function setupLoginHandlers() { }); } else if (config && config.setModel) { config.setModel(payload.modelName); + // Fix: notify frontend when model is set via config.setModel (no geminiClient) + await communicationService.sendModelSwitchComplete(payload.sessionId, payload.modelName); } } @@ -2599,6 +2601,10 @@ function setupLoginHandlers() { success: false, error: error instanceof Error ? error.message : 'Unknown error' }); + // Fix: notify frontend to clear isModelSwitching state on failure + if (payload.sessionId) { + await communicationService.sendModelSwitchComplete(payload.sessionId, payload.modelName); + } } }); From aeb77271d29ad7ced2ed6a525407c8f588ddd1dd Mon Sep 17 00:00:00 2001 From: NSIETeam Date: Mon, 13 Jul 2026 19:47:02 +0800 Subject: [PATCH 2/3] feat: add minimize-to-tray option for background running --- packages/desktop/src/main/index.ts | 44 ++++++++++++++++++- packages/desktop/src/main/userSettings.ts | 4 ++ .../src/components/SettingsDialog.tsx | 13 ++++++ packages/desktop/src/shared/ipc.ts | 2 + 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/packages/desktop/src/main/index.ts b/packages/desktop/src/main/index.ts index 67acd9b5..a2dcf7d9 100644 --- a/packages/desktop/src/main/index.ts +++ b/packages/desktop/src/main/index.ts @@ -7,7 +7,7 @@ * IPC bridge and ACP session hub wired in. */ -import { app, BrowserWindow, Menu, shell, nativeTheme } from 'electron'; +import { app, BrowserWindow, Menu, shell, nativeTheme, Tray, nativeImage } from 'electron'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { registerIpc } from './ipc.js'; @@ -19,10 +19,13 @@ import type { UpdateManager } from './updater.js'; // taskbar icon at runtime (dev + Linux/Windows). On macOS the dock icon comes // from the packaged .app bundle, so this is harmless there. import appIcon from '../../build/icon.png?asset'; +import { getUserSettings } from './userSettings.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); let mainWindow: BrowserWindow | null = null; +let tray: Tray | null = null; +let isQuitting = false; let hub: SessionHub | null = null; let feishu: FeishuManager | null = null; let updater: UpdateManager | null = null; @@ -94,6 +97,18 @@ function createWindow(): void { mainWindow.on('ready-to-show', () => mainWindow?.show()); + // Intercept window close: if minimizeToTray is enabled, hide instead of close + mainWindow.on('close', (e) => { + const settings = getUserSettings(); + if (settings.minimizeToTray && !isQuitting) { + e.preventDefault(); + mainWindow?.hide(); + if (tray && !tray.isDestroyed()) { + mainWindow?.webContents.send('tray:notification', 'Easy Code is running in the background. Click the tray icon to restore.'); + } + } + }); + // Open target=_blank / external links in the system browser, never in-app. mainWindow.webContents.setWindowOpenHandler(({ url }) => { void shell.openExternal(url); @@ -133,6 +148,25 @@ app.whenReady().then(() => { } ({ hub, feishu, updater } = registerIpc(() => mainWindow)); createWindow(); + + // Create system tray + const trayIcon = nativeImage.createFromPath(appIcon); + tray = new Tray(trayIcon.resize({ width: 16, height: 16 })); + tray.setToolTip('Easy Code'); + tray.on('click', () => { + if (mainWindow) { + if (mainWindow.isVisible()) { + mainWindow.hide(); + } else { + mainWindow.show(); + mainWindow.focus(); + } + } + }); + tray.setContextMenu(Menu.buildFromTemplate([ + { label: 'Show Easy Code', click: () => { mainWindow?.show(); mainWindow?.focus(); } }, + { label: 'Quit', click: () => { isQuitting = true; app.quit(); } }, + ])); // Kick off the version-update lifecycle (startup check + periodic poll). It // delays its first check internally so it never competes with boot. updater.start(); @@ -142,7 +176,13 @@ app.whenReady().then(() => { }); }); -app.on('window-all-closed', () => { +app.on('window-all-closed', (e: Event) => { + // In tray mode, prevent app from quitting when all windows are closed + const settings = getUserSettings(); + if (settings.minimizeToTray && !isQuitting) { + e.preventDefault(); + return; + } if (process.platform !== 'darwin') app.quit(); }); diff --git a/packages/desktop/src/main/userSettings.ts b/packages/desktop/src/main/userSettings.ts index 82cd0a9a..8df5dccd 100644 --- a/packages/desktop/src/main/userSettings.ts +++ b/packages/desktop/src/main/userSettings.ts @@ -63,6 +63,7 @@ function project(raw: Record): DesktopUserSettings { ) { out.projectMemoryMode = raw.projectMemoryMode; } + if (typeof raw.minimizeToTray === 'boolean') out.minimizeToTray = raw.minimizeToTray; return out; } @@ -90,6 +91,9 @@ export function updateUserSettings(patch: DesktopUserSettings): DesktopUserSetti if ('projectMemoryMode' in patch && patch.projectMemoryMode) { raw.projectMemoryMode = patch.projectMemoryMode; } + if ('minimizeToTray' in patch && typeof patch.minimizeToTray === 'boolean') { + raw.minimizeToTray = patch.minimizeToTray; + } writeRaw(raw); return project(raw); diff --git a/packages/desktop/src/renderer/src/components/SettingsDialog.tsx b/packages/desktop/src/renderer/src/components/SettingsDialog.tsx index b2e551ef..c2588e7d 100644 --- a/packages/desktop/src/renderer/src/components/SettingsDialog.tsx +++ b/packages/desktop/src/renderer/src/components/SettingsDialog.tsx @@ -245,6 +245,19 @@ function GeneralTab({ onClose }: { onClose: () => void }) {
{t('settings.healthyUseDesc')}
+
+ + +
When enabled, closing the window minimizes Easy Code to the system tray. AI tasks continue running in the background. Click the tray icon to restore.
+
+
diff --git a/packages/desktop/src/shared/ipc.ts b/packages/desktop/src/shared/ipc.ts index 159e4f98..afa53f2d 100644 --- a/packages/desktop/src/shared/ipc.ts +++ b/packages/desktop/src/shared/ipc.ts @@ -298,6 +298,8 @@ export interface DesktopUserSettings { healthyUse?: boolean; /** How project memory (DEEPV.md / AGENTS.md) is loaded. Undefined = "all". */ projectMemoryMode?: ProjectMemoryMode; + /** Minimize to system tray instead of quitting when window is closed. */ + minimizeToTray?: boolean; } export interface CreateSessionOptions { From 75391128f7e2f1f820e76ec8ea647332cf463ea1 Mon Sep 17 00:00:00 2001 From: NSIETeam Date: Mon, 13 Jul 2026 20:14:05 +0800 Subject: [PATCH 3/3] fix: address code review - before-quit and dead IPC --- packages/desktop/src/main/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/desktop/src/main/index.ts b/packages/desktop/src/main/index.ts index a2dcf7d9..2856864b 100644 --- a/packages/desktop/src/main/index.ts +++ b/packages/desktop/src/main/index.ts @@ -104,7 +104,7 @@ function createWindow(): void { e.preventDefault(); mainWindow?.hide(); if (tray && !tray.isDestroyed()) { - mainWindow?.webContents.send('tray:notification', 'Easy Code is running in the background. Click the tray icon to restore.'); + // Tray icon click restores window; no IPC needed } } }); @@ -187,6 +187,7 @@ app.on('window-all-closed', (e: Event) => { }); app.on('before-quit', () => { + isQuitting = true; hub?.disposeAll(); // Tear down the desktop-managed Feishu gateway so we never leave an orphan // gateway behind (which the next launch would otherwise detect + kill).