feat(hotkey): HotkeyService wrapping globalShortcut

Task 17 of the slice plan. register() pre-checks
globalShortcut.isRegistered to detect collisions with other
apps (Failure #1 in spec §6.1) and reports the conflict in
the return object so the OllamaBanner-style failure surface
in Task 29 can report it. unregisterAll() runs on app quit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
altair823
2026-04-25 12:11:44 +09:00
parent 0529387c9a
commit a2be6ed47f

View File

@@ -0,0 +1,26 @@
import { globalShortcut } from 'electron';
export interface HotkeyBinding {
accelerator: string;
onTrigger: () => void;
}
export class HotkeyService {
private registered: string[] = [];
register(binding: HotkeyBinding): { ok: boolean; reason?: string } {
const accel = binding.accelerator;
if (globalShortcut.isRegistered(accel)) {
return { ok: false, reason: `${accel} already registered by another app` };
}
const ok = globalShortcut.register(accel, binding.onTrigger);
if (!ok) return { ok: false, reason: `failed to register ${accel}` };
this.registered.push(accel);
return { ok: true };
}
unregisterAll(): void {
for (const a of this.registered) globalShortcut.unregister(a);
this.registered = [];
}
}