Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -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",
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 8 additions & 29 deletions src/main/lifecycle/window.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -65,6 +65,7 @@ describe('main/lifecycle/window.ts', () => {
setPlatform('linux');

menubar = {
setOption: vi.fn(),
hideWindow: vi.fn(),
recenterOnTray: vi.fn(),
tray: {
Expand All@@ -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;
Expand DownExpand Up@@ -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<typeof vi.fn>).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<typeof vi.fn>).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);
});
});

Expand Down
21 changes: 2 additions & 19 deletions src/main/lifecycle/window.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,6 @@ import { WindowConfig } from '../config';
import type MenuBuilder from '../menu';

let isQuitting = false;
let keepWindowOnBlur = false;
let windowVibrancyEnabled = false;

/**
Expand All@@ -19,7 +18,6 @@ let windowVibrancyEnabled = false;
*/
export function __resetWindowLifecycleForTests(): void {
isQuitting = false;
keepWindowOnBlur = false;
windowVibrancyEnabled = false;
}

Expand All@@ -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);
}

/**
Expand DownExpand Up@@ -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) {
Expand All@@ -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);
});
}