diff --git a/package.json b/package.json index f095c47e6..1057f8d52 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "dependencies": { "cn": "0.2.4", "electron-log": "5.4.4", - "electron-menubar": "10.2.1", + "electron-menubar": "11.0.0", "electron-updater": "6.8.9", "react": "19.2.8", "react-dom": "19.2.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e1edd5ed2..a59f16043 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,8 +21,8 @@ importers: specifier: 5.4.4 version: 5.4.4 electron-menubar: - specifier: 10.2.1 - version: 10.2.1(electron@44.1.1(supports-color@10.2.2)) + specifier: 11.0.0 + version: 11.0.0(electron@44.1.1(supports-color@10.2.2)) electron-updater: specifier: 6.8.9 version: 6.8.9(supports-color@10.2.2) @@ -2566,8 +2566,8 @@ packages: resolution: {integrity: sha512-istWgaXjBfURBSS8LWVW9C3jsc6+ac+tY1lXrQEOTp0lVj+a4OlO1Tmqb36GgnEUDv92DGC9VI1HNXwJinWpgA==} engines: {node: '>= 14'} - electron-menubar@10.2.1: - resolution: {integrity: sha512-JIpqGjVYUSloNJn/8hqotzFIRNmJ0jpGQJjXGs4GB/viy8cXIBlWj2G30w+SqJR7TKa+EBhXrMLSA73HdP/VgA==} + electron-menubar@11.0.0: + resolution: {integrity: sha512-KepfKfvbP5FKVTAno3IqBmNJwFxjSToYaZYjB3Cu3J3eae+L2Hc3JBlNIaeYJyzeO5KL73s+zYveuxF8AJ87Yw==} peerDependencies: electron: 44.1.1 @@ -6868,7 +6868,7 @@ snapshots: electron-log@5.4.4: {} - electron-menubar@10.2.1(electron@44.1.1(supports-color@10.2.2)): + electron-menubar@11.0.0(electron@44.1.1(supports-color@10.2.2)): dependencies: electron: 44.1.1(supports-color@10.2.2) diff --git a/src/main/lifecycle/window.test.ts b/src/main/lifecycle/window.test.ts index 12d42ad28..8756f468e 100644 --- a/src/main/lifecycle/window.test.ts +++ b/src/main/lifecycle/window.test.ts @@ -65,6 +65,7 @@ describe('main/lifecycle/window.ts', () => { setPlatform('linux'); menubar = { + setOption: vi.fn(), hideWindow: vi.fn(), recenterOnTray: vi.fn(), tray: { @@ -81,6 +82,7 @@ describe('main/lifecycle/window.ts', () => { on: vi.fn(), webContents: { on: vi.fn(), + isDevToolsOpened: vi.fn().mockReturnValue(false), }, }, } as unknown as Menubar; @@ -152,38 +154,15 @@ describe('main/lifecycle/window.ts', () => { }); describe('applyKeepWindowOnBlur', () => { - it('forwards the value to the underlying window', () => { - applyKeepWindowOnBlur(menubar, true); - - expect(menubar.window?.setAlwaysOnTop).toHaveBeenCalledWith(true); + it.each([false, true])('forwards the keep-open preference %s to menubar', (keepOpen) => { + applyKeepWindowOnBlur(menubar, keepOpen); + expect(menubar.setOption).toHaveBeenCalledWith('hideOnBlur', !keepOpen); }); - it('skips the call when the window is destroyed', () => { - // oxlint-disable-next-line no-unsafe-optional-chaining -- window is guaranteed defined in this test - (menubar.window?.isDestroyed as ReturnType).mockReturnValue(true); - - applyKeepWindowOnBlur(menubar, true); - - expect(menubar.window?.setAlwaysOnTop).not.toHaveBeenCalled(); - }); - - it('is restored after DevTools closes', () => { - configureWindowEvents(menubar, menuBuilder); + it('remembers the preference through menubar before a window exists', () => { + Object.defineProperty(menubar, 'window', { value: undefined }); applyKeepWindowOnBlur(menubar, true); - // oxlint-disable-next-line no-unsafe-optional-chaining -- window is guaranteed defined in this test - (menubar.window?.setAlwaysOnTop as ReturnType).mockClear(); - - findWebContentsHandler(menubar, 'devtools-closed')?.(); - - expect(menubar.window?.setAlwaysOnTop).toHaveBeenCalledWith(true); - }); - - it('is cleared after DevTools closes when the user did not opt in', () => { - configureWindowEvents(menubar, menuBuilder); - - findWebContentsHandler(menubar, 'devtools-closed')?.(); - - expect(menubar.window?.setAlwaysOnTop).toHaveBeenCalledWith(false); + expect(menubar.setOption).toHaveBeenCalledWith('hideOnBlur', false); }); }); diff --git a/src/main/lifecycle/window.ts b/src/main/lifecycle/window.ts index 2694722b4..3d6e83e9d 100644 --- a/src/main/lifecycle/window.ts +++ b/src/main/lifecycle/window.ts @@ -7,7 +7,6 @@ import { WindowConfig } from '../config'; import type MenuBuilder from '../menu'; let isQuitting = false; -let keepWindowOnBlur = false; let windowVibrancyEnabled = false; /** @@ -19,7 +18,6 @@ let windowVibrancyEnabled = false; */ export function __resetWindowLifecycleForTests(): void { isQuitting = false; - keepWindowOnBlur = false; windowVibrancyEnabled = false; } @@ -42,19 +40,8 @@ export function applyWindowVibrancy(mb: Menubar, enabled: boolean): void { mb.window.setBackgroundColor(enabled ? '#00000000' : '#ffffff'); } -/** - * Apply the user's "keep window open when it loses focus" preference. - * - * Implemented by toggling the window's `alwaysOnTop` flag, which the - * `menubar` library checks to short-circuit its blur-driven hide. The - * value is also remembered so the `devtools-closed` handler can restore - * it after DevTools temporarily forces it on. - */ export function applyKeepWindowOnBlur(mb: Menubar, value: boolean): void { - keepWindowOnBlur = value; - if (mb.window && !mb.window.isDestroyed()) { - mb.window.setAlwaysOnTop(value); - } + mb.setOption('hideOnBlur', !value); } /** @@ -114,15 +101,12 @@ export function configureWindowEvents(mb: Menubar, menuBuilder: MenuBuilder): vo mb.window.setSize(800, 600); mb.window.center(); mb.window.resizable = true; - mb.window.setAlwaysOnTop(true); }); /** * When DevTools is closed, restore the window to its original size and position it centered on the tray icon. * - * `devtools-opened` forces `alwaysOnTop` true for usability while - * debugging; restore it to the user's preference here so DevTools - * doesn't leave the flag stuck on. + * Menubar restores focus behavior; Gitify restores its preferred layout. */ mb.window.webContents.on('devtools-closed', () => { if (!mb.window) { @@ -132,6 +116,5 @@ export function configureWindowEvents(mb: Menubar, menuBuilder: MenuBuilder): vo mb.window.setSize(WindowConfig.width!, WindowConfig.height!); mb.recenterOnTray(); mb.window.resizable = false; - mb.window.setAlwaysOnTop(keepWindowOnBlur); }); }