mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-27 21:33:58 +02:00
fix: resolve Quick Prompt hotkey registration and startup resilience
- Fix accelerator: convert 'Super' to 'Meta' in toElectronAccelerator() since Electron uses 'Meta' for the Win/Cmd key, not 'Super' - Defer quick prompt window creation until after workspace loads so a failure cannot block the main window from rendering - Extract registerQuickPromptIpcHandlers into a separate function called after the quick prompt window is created - Wrap quick prompt window creation in try/catch for resilience Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
+13
-4
@@ -1,7 +1,7 @@
|
|||||||
import electron from 'electron';
|
import electron from 'electron';
|
||||||
import type { BrowserWindow as BrowserWindowType } from 'electron';
|
import type { BrowserWindow as BrowserWindowType } from 'electron';
|
||||||
|
|
||||||
import { registerIpcHandlers } from '@main/ipc/registerIpcHandlers';
|
import { registerIpcHandlers, registerQuickPromptIpcHandlers } from '@main/ipc/registerIpcHandlers';
|
||||||
import { AryxAppService } from '@main/AryxAppService';
|
import { AryxAppService } from '@main/AryxAppService';
|
||||||
import { AutoUpdateService } from '@main/services/autoUpdater';
|
import { AutoUpdateService } from '@main/services/autoUpdater';
|
||||||
import { GlobalHotkeyService } from '@main/services/globalHotkey';
|
import { GlobalHotkeyService } from '@main/services/globalHotkey';
|
||||||
@@ -29,8 +29,10 @@ async function bootstrap(): Promise<void> {
|
|||||||
autoUpdateService = new AutoUpdateService({ isPackaged: app.isPackaged });
|
autoUpdateService = new AutoUpdateService({ isPackaged: app.isPackaged });
|
||||||
|
|
||||||
mainWindow = createMainWindow();
|
mainWindow = createMainWindow();
|
||||||
quickPromptWindow = createQuickPromptWindow();
|
|
||||||
registerIpcHandlers(mainWindow, appService, autoUpdateService, quickPromptWindow);
|
// 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.
|
// Start workspace loading in parallel — don't block window from showing.
|
||||||
// The renderer fetches the workspace via its own IPC call after mount.
|
// The renderer fetches the workspace via its own IPC call after mount.
|
||||||
@@ -57,7 +59,14 @@ async function bootstrap(): Promise<void> {
|
|||||||
systemTray?.updateRunningCount(updatedWorkspace);
|
systemTray?.updateRunningCount(updatedWorkspace);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register global hotkey for Quick Prompt
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
globalHotkeyService = new GlobalHotkeyService();
|
globalHotkeyService = new GlobalHotkeyService();
|
||||||
const hotkeySettings = workspace.settings.quickPrompt ?? createDefaultQuickPromptSettings();
|
const hotkeySettings = workspace.settings.quickPrompt ?? createDefaultQuickPromptSettings();
|
||||||
globalHotkeyService.register(hotkeySettings, () => {
|
globalHotkeyService.register(hotkeySettings, () => {
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ export function registerIpcHandlers(
|
|||||||
window: BrowserWindow,
|
window: BrowserWindow,
|
||||||
service: AryxAppService,
|
service: AryxAppService,
|
||||||
autoUpdateService: AutoUpdateService,
|
autoUpdateService: AutoUpdateService,
|
||||||
quickPromptWindow?: BrowserWindow,
|
|
||||||
): void {
|
): void {
|
||||||
window.on('focus', () => {
|
window.on('focus', () => {
|
||||||
if (service.isGitAutoRefreshEnabled()) {
|
if (service.isGitAutoRefreshEnabled()) {
|
||||||
@@ -354,9 +353,39 @@ export function registerIpcHandlers(
|
|||||||
window.webContents.send(ipcChannels.terminalExit, info);
|
window.webContents.send(ipcChannels.terminalExit, info);
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- Quick Prompt IPC ---
|
// --- Quick Prompt IPC (window-independent) ---
|
||||||
|
|
||||||
// Track the active quick prompt session so events can be routed
|
ipcMain.handle(ipcChannels.quickPromptGetCapabilities, async () => {
|
||||||
|
const capabilities = await service.describeSidecarCapabilities();
|
||||||
|
const settings = service.getQuickPromptSettings();
|
||||||
|
const models = buildAvailableModelCatalog(capabilities.models);
|
||||||
|
return {
|
||||||
|
models,
|
||||||
|
defaultModel: settings.defaultModel,
|
||||||
|
defaultReasoningEffort: settings.defaultReasoningEffort,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
ipcChannels.quickPromptSetSettings,
|
||||||
|
(_event, settings: Partial<QuickPromptSettings>) => service.setQuickPromptSettings(settings),
|
||||||
|
);
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
ipcChannels.quickPromptGetSettings,
|
||||||
|
() => service.getQuickPromptSettings(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register IPC handlers that depend on the quick prompt BrowserWindow.
|
||||||
|
* Called separately after the quick prompt window is created (deferred from bootstrap).
|
||||||
|
*/
|
||||||
|
export function registerQuickPromptIpcHandlers(
|
||||||
|
mainWindow: BrowserWindow,
|
||||||
|
service: AryxAppService,
|
||||||
|
quickPromptWindow: BrowserWindow,
|
||||||
|
): void {
|
||||||
let quickPromptSessionId: string | undefined;
|
let quickPromptSessionId: string | undefined;
|
||||||
|
|
||||||
ipcMain.handle(ipcChannels.quickPromptSend, async (_event, input: QuickPromptSendInput) => {
|
ipcMain.handle(ipcChannels.quickPromptSend, async (_event, input: QuickPromptSendInput) => {
|
||||||
@@ -392,12 +421,12 @@ export function registerIpcHandlers(
|
|||||||
await service.deleteSession(quickPromptSessionId);
|
await service.deleteSession(quickPromptSessionId);
|
||||||
quickPromptSessionId = undefined;
|
quickPromptSessionId = undefined;
|
||||||
}
|
}
|
||||||
if (quickPromptWindow) hideQuickPromptWindow(quickPromptWindow);
|
hideQuickPromptWindow(quickPromptWindow);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(ipcChannels.quickPromptClose, async () => {
|
ipcMain.handle(ipcChannels.quickPromptClose, async () => {
|
||||||
quickPromptSessionId = undefined;
|
quickPromptSessionId = undefined;
|
||||||
if (quickPromptWindow) hideQuickPromptWindow(quickPromptWindow);
|
hideQuickPromptWindow(quickPromptWindow);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(ipcChannels.quickPromptContinueInAryx, async () => {
|
ipcMain.handle(ipcChannels.quickPromptContinueInAryx, async () => {
|
||||||
@@ -405,42 +434,19 @@ export function registerIpcHandlers(
|
|||||||
await service.selectSession(quickPromptSessionId);
|
await service.selectSession(quickPromptSessionId);
|
||||||
quickPromptSessionId = undefined;
|
quickPromptSessionId = undefined;
|
||||||
}
|
}
|
||||||
if (quickPromptWindow) hideQuickPromptWindow(quickPromptWindow);
|
hideQuickPromptWindow(quickPromptWindow);
|
||||||
// Show and focus the main window
|
// Show and focus the main window
|
||||||
if (!window.isDestroyed()) {
|
if (!mainWindow.isDestroyed()) {
|
||||||
if (window.isMinimized()) window.restore();
|
if (mainWindow.isMinimized()) mainWindow.restore();
|
||||||
window.show();
|
mainWindow.show();
|
||||||
window.focus();
|
mainWindow.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(ipcChannels.quickPromptGetCapabilities, async () => {
|
|
||||||
const capabilities = await service.describeSidecarCapabilities();
|
|
||||||
const settings = service.getQuickPromptSettings();
|
|
||||||
const models = buildAvailableModelCatalog(capabilities.models);
|
|
||||||
return {
|
|
||||||
models,
|
|
||||||
defaultModel: settings.defaultModel,
|
|
||||||
defaultReasoningEffort: settings.defaultReasoningEffort,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle(
|
|
||||||
ipcChannels.quickPromptSetSettings,
|
|
||||||
(_event, settings: Partial<QuickPromptSettings>) => service.setQuickPromptSettings(settings),
|
|
||||||
);
|
|
||||||
|
|
||||||
ipcMain.handle(
|
|
||||||
ipcChannels.quickPromptGetSettings,
|
|
||||||
() => service.getQuickPromptSettings(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Route session events to the quick prompt window
|
// Route session events to the quick prompt window
|
||||||
if (quickPromptWindow) {
|
service.on('session-event', (event) => {
|
||||||
service.on('session-event', (event) => {
|
if (event.sessionId === quickPromptSessionId && !quickPromptWindow.isDestroyed()) {
|
||||||
if (event.sessionId === quickPromptSessionId && !quickPromptWindow.isDestroyed()) {
|
quickPromptWindow.webContents.send(ipcChannels.quickPromptSessionEvent, event);
|
||||||
quickPromptWindow.webContents.send(ipcChannels.quickPromptSessionEvent, event);
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ export class GlobalHotkeyService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert our portable hotkey string (e.g. "Super+Shift+A") to an Electron
|
* Convert our portable hotkey string (e.g. "Super+Shift+A") to an Electron
|
||||||
* accelerator string. Electron uses "Super" on all platforms, which maps to
|
* accelerator string. Electron uses "Meta" which maps to Cmd on macOS and
|
||||||
* Cmd on macOS and Win on Windows/Linux.
|
* Win key on Windows/Linux.
|
||||||
*/
|
*/
|
||||||
function toElectronAccelerator(hotkey: string): string {
|
function toElectronAccelerator(hotkey: string): string {
|
||||||
return hotkey;
|
return hotkey.replace(/Super/gi, 'Meta');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user