From 3122f4121e502598376fb8b1c4ab0f036e8c06f5 Mon Sep 17 00:00:00 2001 From: muckymucky Date: Mon, 25 May 2026 19:24:15 +0100 Subject: [PATCH] fix: strip playback menu accelerators while command palette is open (#2055) On macOS, menu item accelerators (e.g. Space for Play/Pause) fire even when an input has focus, bypassing the renderer-side useHotkeys guard added in #1925. Typing a space character in the command palette search toggled play/pause instead of inserting a space. Track command palette open state in the main process via IPC. When the palette is open, rebuild the application menu with empty playback accelerators so single-key shortcuts no longer intercept keystrokes intended for the search input. Restore accelerators when the palette closes. Co-authored-by: Jack McCrea --- src/main/index.ts | 12 +++++++++++- src/preload/utils.ts | 5 +++++ src/renderer/store/app.store.ts | 5 +++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/index.ts b/src/main/index.ts index 85d4c81eb..3efc0a508 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -280,6 +280,16 @@ let currentRepeatMode: PlayerRepeat = PlayerRepeat.NONE; let currentSidebarCollapsed = false; let currentShuffleEnabled = false; let playbackMenuAccelerators: MenuPlaybackState['accelerators'] = {}; +let commandPaletteOpen = false; + +ipcMain.on('command-palette-state', (_event, opened: boolean) => { + const next = !!opened; + if (commandPaletteOpen === next) return; + commandPaletteOpen = next; + if (isMacOS()) { + rebuildMainMenu(); + } +}); if (process.env.NODE_ENV === 'production') { import('source-map-support').then((sourceMapSupport) => { @@ -340,7 +350,7 @@ const rebuildMainMenu = () => { if (!menuBuilder || !mainWindow) return; menuBuilder.buildMenu({ - accelerators: playbackMenuAccelerators, + accelerators: commandPaletteOpen ? {} : playbackMenuAccelerators, playbackStatus: currentPlaybackStatus, privateMode: currentPrivateMode, repeatMode: currentRepeatMode, diff --git a/src/preload/utils.ts b/src/preload/utils.ts index 3593dd58f..a5faded1a 100644 --- a/src/preload/utils.ts +++ b/src/preload/utils.ts @@ -54,6 +54,10 @@ const forceGarbageCollection = (): boolean => { } }; +const setCommandPaletteOpen = (opened: boolean) => { + ipcRenderer.send('command-palette-state', opened); +}; + const rendererOpenSettings = (cb: () => void) => { ipcRenderer.on('renderer-open-settings', () => cb()); }; @@ -101,6 +105,7 @@ export const utils = { rendererTogglePrivateMode, rendererToggleSidebar, rendererUpdateAvailable, + setCommandPaletteOpen, startPowerSaveBlocker, stopPowerSaveBlocker, }; diff --git a/src/renderer/store/app.store.ts b/src/renderer/store/app.store.ts index 549ced89a..2da75a668 100644 --- a/src/renderer/store/app.store.ts +++ b/src/renderer/store/app.store.ts @@ -201,17 +201,22 @@ export const useAppStore = createWithEqualityFn()( set((state) => { state.commandPalette.opened = false; }); + window.api?.utils?.setCommandPaletteOpen?.(false); }, open: () => { set((state) => { state.commandPalette.opened = true; }); + window.api?.utils?.setCommandPaletteOpen?.(true); }, opened: false, toggle: () => { + let nextOpened = false; set((state) => { state.commandPalette.opened = !state.commandPalette.opened; + nextOpened = state.commandPalette.opened; }); + window.api?.utils?.setCommandPaletteOpen?.(nextOpened); }, }, commandPaletteSearchSectionsExpanded: {},