feat(desktop): add KeybindManager with uiohook-napi for global shortcuts

Installs uiohook-napi for OS-level non-consuming input hooks, creates
KeybindManager class that receives keybind configs from the renderer,
matches the OS-wide key/mouse event stream, and sends matched actions
back via IPC. Exposes syncKeybinds, onKeybindAction, onAccessibilityStatus,
onKeybindHookError, and checkAccessibility APIs through the preload bridge.
This commit is contained in:
Jannis Braun
2026-03-22 20:47:41 +01:00
parent 11d802f86c
commit 3ce9ee0db0
5 changed files with 213 additions and 2 deletions
+21
View File
@@ -76,4 +76,25 @@ contextBridge.exposeInMainWorld('backspace', {
return () => { ipcRenderer.removeListener('activity-detected', handler); };
},
getCurrentActivity: () => ipcRenderer.invoke('get-current-activity'),
// Keybind support
syncKeybinds: (keybinds: Array<{ actionId: string; keys: number[]; mouseButton?: number }>) => {
ipcRenderer.send('keybinds-sync', keybinds);
},
onKeybindAction: (callback: (action: { actionId: string; pressed: boolean }) => void) => {
const handler = (_event: Electron.IpcRendererEvent, action: { actionId: string; pressed: boolean }) => callback(action);
ipcRenderer.on('keybind-action', handler);
return () => { ipcRenderer.removeListener('keybind-action', handler); };
},
onAccessibilityStatus: (callback: (status: { trusted: boolean }) => void) => {
const handler = (_event: Electron.IpcRendererEvent, status: { trusted: boolean }) => callback(status);
ipcRenderer.on('accessibility-status', handler);
return () => { ipcRenderer.removeListener('accessibility-status', handler); };
},
onKeybindHookError: (callback: (error: { message: string }) => void) => {
const handler = (_event: Electron.IpcRendererEvent, error: { message: string }) => callback(error);
ipcRenderer.on('keybind-hook-error', handler);
return () => { ipcRenderer.removeListener('keybind-hook-error', handler); };
},
checkAccessibility: () => ipcRenderer.invoke('check-accessibility'),
});