mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-17 16:37:43 +00:00
* fix(keyboard): resolve macOS global shortcut layout mismatch (#8378) * fix(keyboard-layout): log layout-detection failure and resolve layoutReady with map copy * fix(keyboard-shortcut): remove debug console logs and add macOS scope comment * fix(keyboard-shortcut): preserve modifier separator when mapping plus key shortcut * refactor(keyboard-shortcut): export mapping helpers and avoid as any cast in configuration mapping * test(keyboard-shortcut): add unit tests for layout shortcut translation logic * test(keyboard-shortcut): use correct KeyboardConfig type instead of as any in test fixture * docs(keyboard-shortcut): hoist macOS physical shortcut layout comments to helper JSDoc * refactor(config): extract global shortcut keys and mapping helpers * test: add test helper capability for IS_ELECTRON and IS_MAC * test: add comprehensive integration unit tests for global shortcut effects * feat(electron): eagerly trigger layout detection on Electron startup for macOS timing fix * refactor(config): InjectionToken migration, layout detection hardening, and startup optimization * refactor: address non-blocking suggestions for layout detection and DI tokens * build(electron): move keyboard-config.model to shared-with-frontend to fix ASAR require * fix(client-id): increase entropy to 6 chars and fix related test regressions
109 lines
3.8 KiB
TypeScript
109 lines
3.8 KiB
TypeScript
import { globalShortcut, ipcMain } from 'electron';
|
|
import { IPC } from '../shared-with-frontend/ipc-events.const';
|
|
import {
|
|
KeyboardConfig,
|
|
GLOBAL_KEY_CFG_KEYS,
|
|
} from '../shared-with-frontend/keyboard-config.model';
|
|
import { getWin, setWasMaximizedBeforeHide } from '../main-window';
|
|
import { toggleTaskWidgetVisibility } from '../task-widget/task-widget';
|
|
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) => {
|
|
registerShowAppShortCuts(cfg);
|
|
});
|
|
};
|
|
|
|
const registerShowAppShortCuts = (cfg: KeyboardConfig): void => {
|
|
// unregister all previous
|
|
globalShortcut.unregisterAll();
|
|
|
|
if (cfg) {
|
|
const mainWin = getWin();
|
|
Object.keys(cfg)
|
|
.filter((key: string) => GLOBAL_KEY_CFG_KEYS.includes(key as keyof KeyboardConfig))
|
|
.forEach((key: string) => {
|
|
let actionFn: () => void;
|
|
const shortcut = cfg[key as keyof KeyboardConfig];
|
|
|
|
switch (key) {
|
|
case 'globalShowHide':
|
|
actionFn = () => {
|
|
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.
|
|
setWasMaximizedBeforeHide(mainWin.isMaximized());
|
|
if (IS_MAC) {
|
|
mainWin.hide();
|
|
} else if (getIsMinimizeToTray() && ensureIndicator()) {
|
|
mainWin.blur();
|
|
mainWin.hide();
|
|
} else {
|
|
mainWin.minimize();
|
|
}
|
|
};
|
|
break;
|
|
|
|
case 'globalToggleTaskStart':
|
|
actionFn = () => {
|
|
mainWin.webContents.send(IPC.TASK_TOGGLE_START);
|
|
};
|
|
break;
|
|
|
|
case 'globalAddNote':
|
|
actionFn = () => {
|
|
showOrFocus(mainWin);
|
|
mainWin.webContents.send(IPC.ADD_NOTE);
|
|
};
|
|
break;
|
|
|
|
case 'globalAddTask':
|
|
actionFn = () => {
|
|
showOrFocus(mainWin);
|
|
// NOTE: delay slightly to make sure app is ready
|
|
mainWin.webContents.send(IPC.SHOW_ADD_TASK_BAR);
|
|
};
|
|
break;
|
|
|
|
case 'globalToggleTaskWidget':
|
|
actionFn = toggleTaskWidgetVisibility;
|
|
break;
|
|
|
|
default:
|
|
actionFn = () => undefined;
|
|
}
|
|
|
|
if (shortcut && shortcut.length > 0) {
|
|
try {
|
|
const ret = globalShortcut.register(shortcut, actionFn) as unknown;
|
|
if (!ret) {
|
|
errorHandlerWithFrontendInform(
|
|
'Global Shortcut registration failed: ' + shortcut,
|
|
shortcut,
|
|
);
|
|
}
|
|
} catch (e) {
|
|
errorHandlerWithFrontendInform(
|
|
'Global Shortcut registration failed: ' + shortcut,
|
|
{ e, shortcut },
|
|
);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
};
|