mirror of
https://github.com/davidkaya/aryx.git
synced 2026-07-28 23:48:39 +02:00
fix: resolve hotkey registration failures and renderer crash
- Change default hotkey from Super+Shift+A to Alt+Shift+A — Win key combinations are frequently reserved by the OS and fail to register - Make quick prompt window creation lazy (first hotkey press) so it cannot interfere with main window startup - Track failed accelerators to prevent error log spam from repeated workspace-updated events - Fix process.platform crash in renderer SettingsPanel — use isMac from @renderer/lib/platform instead (renderer has no node globals) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
+24
-11
@@ -23,6 +23,26 @@ let systemTray: SystemTray | undefined;
|
||||
let autoUpdateService: AutoUpdateService | undefined;
|
||||
let globalHotkeyService: GlobalHotkeyService | undefined;
|
||||
|
||||
let quickPromptInitialized = false;
|
||||
|
||||
/** Lazily creates the quick prompt window on first use. */
|
||||
function ensureQuickPromptWindow(): BrowserWindowType | undefined {
|
||||
if (quickPromptWindow && !quickPromptWindow.isDestroyed()) return quickPromptWindow;
|
||||
if (quickPromptInitialized) return undefined;
|
||||
if (!mainWindow || !appService) return undefined;
|
||||
|
||||
try {
|
||||
quickPromptWindow = createQuickPromptWindow();
|
||||
registerQuickPromptIpcHandlers(mainWindow, appService, quickPromptWindow);
|
||||
quickPromptInitialized = true;
|
||||
} catch (err) {
|
||||
console.error('[aryx] Failed to create quick prompt window:', err);
|
||||
quickPromptInitialized = true;
|
||||
}
|
||||
|
||||
return quickPromptWindow;
|
||||
}
|
||||
|
||||
async function bootstrap(): Promise<void> {
|
||||
appService = new AryxAppService();
|
||||
autoUpdateService?.dispose();
|
||||
@@ -30,8 +50,6 @@ async function bootstrap(): Promise<void> {
|
||||
|
||||
mainWindow = createMainWindow();
|
||||
|
||||
// Defer quick prompt window creation — create it lazily when workspace is ready
|
||||
// so a failure here cannot block the main window from loading.
|
||||
registerIpcHandlers(mainWindow, appService, autoUpdateService);
|
||||
|
||||
// Start workspace loading in parallel — don't block window from showing.
|
||||
@@ -59,18 +77,13 @@ async function bootstrap(): Promise<void> {
|
||||
systemTray?.updateRunningCount(updatedWorkspace);
|
||||
});
|
||||
|
||||
// Create quick prompt window and register global hotkey
|
||||
try {
|
||||
quickPromptWindow = createQuickPromptWindow();
|
||||
registerQuickPromptIpcHandlers(mainWindow!, appService!, quickPromptWindow);
|
||||
} catch (err) {
|
||||
console.error('[aryx] Failed to create quick prompt window:', err);
|
||||
}
|
||||
|
||||
// Register global hotkey — the quick prompt window is created lazily on
|
||||
// first press so it cannot interfere with main window startup.
|
||||
globalHotkeyService = new GlobalHotkeyService();
|
||||
const hotkeySettings = workspace.settings.quickPrompt ?? createDefaultQuickPromptSettings();
|
||||
globalHotkeyService.register(hotkeySettings, () => {
|
||||
if (quickPromptWindow) toggleQuickPromptWindow(quickPromptWindow);
|
||||
const win = ensureQuickPromptWindow();
|
||||
if (win) toggleQuickPromptWindow(win);
|
||||
});
|
||||
|
||||
// Re-register hotkey when settings change
|
||||
|
||||
@@ -2,10 +2,9 @@ import electron from 'electron';
|
||||
|
||||
import type { QuickPromptSettings } from '@shared/domain/tooling';
|
||||
|
||||
const { globalShortcut } = electron;
|
||||
|
||||
export class GlobalHotkeyService {
|
||||
private currentAccelerator: string | undefined;
|
||||
private lastFailedAccelerator: string | undefined;
|
||||
private callback: (() => void) | undefined;
|
||||
|
||||
register(settings: QuickPromptSettings, callback: () => void): void {
|
||||
@@ -17,16 +16,41 @@ export class GlobalHotkeyService {
|
||||
}
|
||||
|
||||
const accelerator = toElectronAccelerator(settings.hotkey);
|
||||
|
||||
// Already registered this accelerator — nothing to do
|
||||
if (accelerator === this.currentAccelerator) return;
|
||||
|
||||
this.unregister();
|
||||
const registered = globalShortcut.register(accelerator, callback);
|
||||
// Don't retry an accelerator that already failed (avoids log spam on
|
||||
// repeated workspace-updated events during startup)
|
||||
if (accelerator === this.lastFailedAccelerator) return;
|
||||
|
||||
if (registered) {
|
||||
this.currentAccelerator = accelerator;
|
||||
} else {
|
||||
console.warn(`[globalHotkey] Failed to register accelerator: ${accelerator}`);
|
||||
this.unregister();
|
||||
|
||||
// Access globalShortcut lazily to ensure electron is fully initialised
|
||||
const { globalShortcut } = electron;
|
||||
if (!globalShortcut) {
|
||||
console.error('[globalHotkey] electron.globalShortcut is not available');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const registered = globalShortcut.register(accelerator, callback);
|
||||
if (registered) {
|
||||
console.info(`[globalHotkey] Registered: ${accelerator}`);
|
||||
this.currentAccelerator = accelerator;
|
||||
this.lastFailedAccelerator = undefined;
|
||||
} else {
|
||||
console.warn(
|
||||
`[globalHotkey] Failed to register accelerator: ${accelerator}` +
|
||||
' — it may be reserved by the OS or another application',
|
||||
);
|
||||
this.currentAccelerator = undefined;
|
||||
this.lastFailedAccelerator = accelerator;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[globalHotkey] Error registering ${accelerator}:`, err);
|
||||
this.currentAccelerator = undefined;
|
||||
this.lastFailedAccelerator = accelerator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,9 +62,15 @@ export class GlobalHotkeyService {
|
||||
|
||||
unregister(): void {
|
||||
if (this.currentAccelerator) {
|
||||
globalShortcut.unregister(this.currentAccelerator);
|
||||
try {
|
||||
const { globalShortcut } = electron;
|
||||
globalShortcut?.unregister(this.currentAccelerator);
|
||||
} catch {
|
||||
// best-effort
|
||||
}
|
||||
this.currentAccelerator = undefined;
|
||||
}
|
||||
this.lastFailedAccelerator = undefined;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -50,10 +80,11 @@ export class GlobalHotkeyService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert our portable hotkey string (e.g. "Super+Shift+A") to an Electron
|
||||
* accelerator string. Electron uses "Meta" which maps to Cmd on macOS and
|
||||
* Win key on Windows/Linux.
|
||||
* Convert a portable hotkey string to an Electron accelerator.
|
||||
*
|
||||
* - "Super" → "Meta" (Win key on Windows, Cmd on macOS)
|
||||
* - Strings already using Electron-native names pass through unchanged.
|
||||
*/
|
||||
function toElectronAccelerator(hotkey: string): string {
|
||||
return hotkey.replace(/Super/gi, 'Meta');
|
||||
return hotkey.replace(/\bSuper\b/gi, 'Meta');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user