From 2457519b1f6182ac7f91675bccb97c19dd6fb3fc Mon Sep 17 00:00:00 2001 From: Lambda Date: Wed, 20 May 2026 10:59:21 +0800 Subject: [PATCH] fix(desktop): tray left-click always shows + focuses, never hides The previous toggle behavior hid the main window when it was already visible, surprising users who expected the menu bar icon to bring the app forward. Hiding belongs to the closeToTray pref (PR2); the tray icon itself should be unambiguously a "bring window to front" affordance. Co-authored-by: multica-agent --- apps/desktop/src/main/tray-manager.test.ts | 26 ++++++++++++++++++++++ apps/desktop/src/main/tray-manager.ts | 16 ++++--------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/apps/desktop/src/main/tray-manager.test.ts b/apps/desktop/src/main/tray-manager.test.ts index 2203be1119..8a9990ac80 100644 --- a/apps/desktop/src/main/tray-manager.test.ts +++ b/apps/desktop/src/main/tray-manager.test.ts @@ -252,6 +252,32 @@ describe("setupTray", () => { } }); + it("tray click on darwin always shows + focuses a visible window, never hides it", () => { + const orig = Object.getOwnPropertyDescriptor(process, "platform"); + Object.defineProperty(process, "platform", { value: "darwin" }); + try { + const showOrCreateWindow = vi.fn(); + const visibleWindow = { + isDestroyed: () => false, + isVisible: () => true, + isMinimized: () => false, + hide: vi.fn(), + show: vi.fn(), + focus: vi.fn(), + restore: vi.fn(), + } as unknown as Electron.BrowserWindow; + setupTray({ getWindow: () => visibleWindow, showOrCreateWindow }); + const click = h.trayInstances[0]!.clickListeners[0]!; + click(); + click(); + click(); + expect(showOrCreateWindow).toHaveBeenCalledTimes(3); + expect(visibleWindow.hide).not.toHaveBeenCalled(); + } finally { + if (orig) Object.defineProperty(process, "platform", orig); + } + }); + it("replays current status on subscribe and renders an initial menu", () => { h.lastEmit.value = { state: "running", pid: 99, agents: ["a"] }; setupTray({ getWindow: () => null, showOrCreateWindow: vi.fn() }); diff --git a/apps/desktop/src/main/tray-manager.ts b/apps/desktop/src/main/tray-manager.ts index 82d72c87b1..f80eb1ded4 100644 --- a/apps/desktop/src/main/tray-manager.ts +++ b/apps/desktop/src/main/tray-manager.ts @@ -154,20 +154,12 @@ export function setupTray(opts: TrayOptions): void { // Left-click handler is a macOS/Windows nice-to-have only. Linux's // AppIndicator surface doesn't fire `click`, so all actions must remain // reachable via the context menu — which they are (see buildMenuTemplate). + // Click always shows + focuses the main window; hiding is reserved for + // the closeToTray pref (PR2). showOrCreateWindow restores from minimized, + // shows if hidden, and focuses an already-visible window. if (process.platform !== "linux") { tray.on("click", () => { - const win = opts.getWindow(); - // The window may have been closed (mainWindow === null after the - // owner's `closed` handler) or destroyed; in either case recreate. - if (!win || win.isDestroyed()) { - opts.showOrCreateWindow(); - return; - } - if (win.isVisible() && !win.isMinimized()) { - win.hide(); - } else { - opts.showOrCreateWindow(); - } + opts.showOrCreateWindow(); }); }