From 63200cf5c94199635ce0d59485cb02b30ac0afae Mon Sep 17 00:00:00 2001 From: Paul Horn Date: Sun, 31 May 2026 19:02:10 +0200 Subject: [PATCH] refactor(main): dedupe window restore+focus into a shared focusWindow helper The un-minimize-then-focus idiom was duplicated across three call sites: singleInstance.focusRunningInstanceWindow, deliverOpenPath in index.ts, and the notification-click handler. #224 introduced a tested helper for the second-instance case but left the two pre-existing inline copies. Extract focusWindow(win) in singleInstance.ts and route all three through it, so there is a single place that decides how the app surfaces an existing window. No behavior change; adds direct unit tests for the helper. --- src/main/index.ts | 5 ++--- src/main/ipc/notifications.ts | 4 ++-- src/main/singleInstance.test.ts | 18 +++++++++++++++++- src/main/singleInstance.ts | 11 +++++++++-- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index 88f937b5..1fa528b5 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -49,7 +49,7 @@ import { PERF_GET } from '../shared/ipc-channels' import { installWebContentsSecurity } from './webSecurity' import { installThemeSkill } from './installThemeSkill' import { releaseAllProjectLocks } from './projectLock' -import { focusRunningInstanceWindow } from './singleInstance' +import { focusRunningInstanceWindow, focusWindow } from './singleInstance' import { startCrossWindowDrag, updateCrossWindowCursor, @@ -1184,8 +1184,7 @@ function deliverOpenPath(p: string): void { return } try { - if (win.isMinimized()) win.restore() - win.focus() + focusWindow(win) } catch { /* noop */ } win.webContents.send(APP_OPEN_PATH, p) } diff --git a/src/main/ipc/notifications.ts b/src/main/ipc/notifications.ts index bfc93c6e..68fb4d94 100644 --- a/src/main/ipc/notifications.ts +++ b/src/main/ipc/notifications.ts @@ -5,6 +5,7 @@ import { ipcMain, Notification, app } from 'electron' import { NOTIFY_OS, NOTIFY_ACTION } from '../../shared/ipc-channels' import { sendToWindow, windowFromEvent } from '../windowRegistry' +import { focusWindow } from '../singleInstance' import type { NotificationAction } from '../../shared/types' export function registerHandlers(): void { @@ -26,8 +27,7 @@ export function registerHandlers(): void { notification.on('click', () => { // Focus the owning window if (win && !win.isDestroyed()) { - if (win.isMinimized()) win.restore() - win.focus() + focusWindow(win) } // Send the action back to the renderer so it can execute it diff --git a/src/main/singleInstance.test.ts b/src/main/singleInstance.test.ts index ce60fea8..1bf9879b 100644 --- a/src/main/singleInstance.test.ts +++ b/src/main/singleInstance.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest' import type { BrowserWindow } from 'electron' -import { focusRunningInstanceWindow } from './singleInstance' +import { focusRunningInstanceWindow, focusWindow } from './singleInstance' type FakeWin = { isDestroyed: ReturnType @@ -59,3 +59,19 @@ describe('focusRunningInstanceWindow', () => { expect(() => focusRunningInstanceWindow(asWindows([]))).not.toThrow() }) }) + +describe('focusWindow', () => { + it('focuses the given window', () => { + const win = fakeWin() + focusWindow(win as unknown as BrowserWindow) + expect(win.focus).toHaveBeenCalledTimes(1) + expect(win.restore).not.toHaveBeenCalled() + }) + + it('restores a minimized window before focusing', () => { + const win = fakeWin({ minimized: true }) + focusWindow(win as unknown as BrowserWindow) + expect(win.restore).toHaveBeenCalledTimes(1) + expect(win.focus).toHaveBeenCalledTimes(1) + }) +}) diff --git a/src/main/singleInstance.ts b/src/main/singleInstance.ts index 98e1d2f0..849391ab 100644 --- a/src/main/singleInstance.ts +++ b/src/main/singleInstance.ts @@ -1,5 +1,13 @@ import type { BrowserWindow } from 'electron' +/** Un-minimize (if needed) and bring a single window to the foreground. + * The shared "make this window the active one" idiom used wherever the app + * surfaces an existing window (second-instance, open-path, notification click). */ +export function focusWindow(win: BrowserWindow): void { + if (win.isMinimized()) win.restore() + win.focus() +} + /** * Bring the already-running instance's window to the foreground. * @@ -13,6 +21,5 @@ import type { BrowserWindow } from 'electron' export function focusRunningInstanceWindow(windows: BrowserWindow[]): void { const win = windows.find((w) => !w.isDestroyed()) if (!win) return - if (win.isMinimized()) win.restore() - win.focus() + focusWindow(win) }