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 <jack@MacBookPro.lan>
This commit is contained in:
muckymucky
2026-05-25 19:24:15 +01:00
committed by GitHub
parent 28b8894b49
commit 3122f4121e
3 changed files with 21 additions and 1 deletions
+11 -1
View File
@@ -280,6 +280,16 @@ let currentRepeatMode: PlayerRepeat = PlayerRepeat.NONE;
let currentSidebarCollapsed = false; let currentSidebarCollapsed = false;
let currentShuffleEnabled = false; let currentShuffleEnabled = false;
let playbackMenuAccelerators: MenuPlaybackState['accelerators'] = {}; 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') { if (process.env.NODE_ENV === 'production') {
import('source-map-support').then((sourceMapSupport) => { import('source-map-support').then((sourceMapSupport) => {
@@ -340,7 +350,7 @@ const rebuildMainMenu = () => {
if (!menuBuilder || !mainWindow) return; if (!menuBuilder || !mainWindow) return;
menuBuilder.buildMenu({ menuBuilder.buildMenu({
accelerators: playbackMenuAccelerators, accelerators: commandPaletteOpen ? {} : playbackMenuAccelerators,
playbackStatus: currentPlaybackStatus, playbackStatus: currentPlaybackStatus,
privateMode: currentPrivateMode, privateMode: currentPrivateMode,
repeatMode: currentRepeatMode, repeatMode: currentRepeatMode,
+5
View File
@@ -54,6 +54,10 @@ const forceGarbageCollection = (): boolean => {
} }
}; };
const setCommandPaletteOpen = (opened: boolean) => {
ipcRenderer.send('command-palette-state', opened);
};
const rendererOpenSettings = (cb: () => void) => { const rendererOpenSettings = (cb: () => void) => {
ipcRenderer.on('renderer-open-settings', () => cb()); ipcRenderer.on('renderer-open-settings', () => cb());
}; };
@@ -101,6 +105,7 @@ export const utils = {
rendererTogglePrivateMode, rendererTogglePrivateMode,
rendererToggleSidebar, rendererToggleSidebar,
rendererUpdateAvailable, rendererUpdateAvailable,
setCommandPaletteOpen,
startPowerSaveBlocker, startPowerSaveBlocker,
stopPowerSaveBlocker, stopPowerSaveBlocker,
}; };
+5
View File
@@ -201,17 +201,22 @@ export const useAppStore = createWithEqualityFn<AppSlice>()(
set((state) => { set((state) => {
state.commandPalette.opened = false; state.commandPalette.opened = false;
}); });
window.api?.utils?.setCommandPaletteOpen?.(false);
}, },
open: () => { open: () => {
set((state) => { set((state) => {
state.commandPalette.opened = true; state.commandPalette.opened = true;
}); });
window.api?.utils?.setCommandPaletteOpen?.(true);
}, },
opened: false, opened: false,
toggle: () => { toggle: () => {
let nextOpened = false;
set((state) => { set((state) => {
state.commandPalette.opened = !state.commandPalette.opened; state.commandPalette.opened = !state.commandPalette.opened;
nextOpened = state.commandPalette.opened;
}); });
window.api?.utils?.setCommandPaletteOpen?.(nextOpened);
}, },
}, },
commandPaletteSearchSectionsExpanded: {}, commandPaletteSearchSectionsExpanded: {},