From 2cefde61bf04f5d40efae2b589e13cb220dd6711 Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Mon, 20 Apr 2026 13:10:38 +0200 Subject: [PATCH] fix(electron): keep window reachable when show/hide shortcut has no tray The globalShowHide shortcut called mainWin.hide() unconditionally. On Windows/Linux hide() removes the taskbar entry, so with no tray icon the window became unreachable. - macOS: use hide() (dock icon always remains). - Windows/Linux: hide to tray only when minimize-to-tray is on AND the tray was created; otherwise minimize() to keep a taskbar handle. - Log whether Windows tray creation used the GUID path, to help diagnose cases where the icon lands in the hidden overflow area. Refs #7282 --- electron/indicator.ts | 5 ++++- electron/ipc-handlers/global-shortcuts.ts | 26 +++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/electron/indicator.ts b/electron/indicator.ts index ee2a9ded74..c296d47c66 100644 --- a/electron/indicator.ts +++ b/electron/indicator.ts @@ -136,11 +136,14 @@ const createTray = (): Tray => { const trayIconPath = DIR + `stopped${suf}`; let nextTray: Tray; if (IS_WINDOWS) { + const guid = getWindowsTrayGuid(); try { - nextTray = new Tray(trayIconPath, getWindowsTrayGuid()); + nextTray = new Tray(trayIconPath, guid); + log('Tray created on Windows with GUID:', guid); } catch (e) { log('Tray creation with GUID failed, retrying without GUID:', e); nextTray = new Tray(trayIconPath); + log('Tray created on Windows without GUID'); } } else { nextTray = new Tray(trayIconPath); diff --git a/electron/ipc-handlers/global-shortcuts.ts b/electron/ipc-handlers/global-shortcuts.ts index 386206c920..656aa59779 100644 --- a/electron/ipc-handlers/global-shortcuts.ts +++ b/electron/ipc-handlers/global-shortcuts.ts @@ -3,10 +3,13 @@ import { IPC } from '../shared-with-frontend/ipc-events.const'; import { KeyboardConfig } from '../../src/app/features/config/keyboard-config.model'; import { getWin } from '../main-window'; import { showOrFocus } from '../various-shared'; +import { ensureIndicator } from '../indicator'; +import { getIsMinimizeToTray } from '../shared-state'; +import { IS_MAC } from '../common.const'; import { errorHandlerWithFrontendInform } from '../error-handler-with-frontend-inform'; export const initGlobalShortcutsIpc = (): void => { - ipcMain.on(IPC.REGISTER_GLOBAL_SHORTCUTS_EVENT, (ev, cfg) => { + ipcMain.on(IPC.REGISTER_GLOBAL_SHORTCUTS_EVENT, (_ev, cfg) => { registerShowAppShortCuts(cfg); }); }; @@ -32,12 +35,27 @@ const registerShowAppShortCuts = (cfg: KeyboardConfig): void => { switch (key) { case 'globalShowHide': actionFn = () => { - if (mainWin.isFocused()) { - // we need to blur the window for windows + if (!mainWin.isFocused()) { + showOrFocus(mainWin); + return; + } + // Hide strategy differs by platform: + // - macOS: the dock icon always remains after hide(), so the + // window stays reachable without any tray. Match the native + // Cmd+H gesture users expect from a "show/hide" shortcut. + // - Windows/Linux: hide() removes the taskbar entry. Without a + // visible tray icon the window becomes unreachable (see #7282). + // Only hide to tray when minimize-to-tray is enabled AND the + // tray was successfully (re)created; otherwise minimize so a + // taskbar handle remains as a safety net. blur() is a Windows + // focus workaround (electron#20464) and a no-op elsewhere. + if (IS_MAC) { + mainWin.hide(); + } else if (getIsMinimizeToTray() && ensureIndicator()) { mainWin.blur(); mainWin.hide(); } else { - showOrFocus(mainWin); + mainWin.minimize(); } }; break;