refactor(core): consolidate services and remove runtime wrappers

This commit is contained in:
kyasuda
2026-02-10 13:13:47 -08:00
committed by sudacode
parent 5cc22e3f1b
commit f868fdbbb3
62 changed files with 954 additions and 1858 deletions

View File

@@ -0,0 +1,133 @@
interface GlobalShortcutLike {
register: (accelerator: string, callback: () => void) => boolean;
unregister: (accelerator: string) => void;
}
export interface NumericShortcutRuntimeOptions {
globalShortcut: GlobalShortcutLike;
showMpvOsd: (text: string) => void;
setTimer: (
handler: () => void,
timeoutMs: number,
) => ReturnType<typeof setTimeout>;
clearTimer: (timer: ReturnType<typeof setTimeout>) => void;
}
export function createNumericShortcutRuntimeService(
options: NumericShortcutRuntimeOptions,
) {
const createSession = () =>
createNumericShortcutSessionService({
registerShortcut: (accelerator, handler) =>
options.globalShortcut.register(accelerator, handler),
unregisterShortcut: (accelerator) =>
options.globalShortcut.unregister(accelerator),
setTimer: options.setTimer,
clearTimer: options.clearTimer,
showMpvOsd: options.showMpvOsd,
});
return {
createSession,
};
}
export interface NumericShortcutSessionMessages {
prompt: string;
timeout: string;
cancelled?: string;
}
export interface NumericShortcutSessionDeps {
registerShortcut: (accelerator: string, handler: () => void) => boolean;
unregisterShortcut: (accelerator: string) => void;
setTimer: (handler: () => void, timeoutMs: number) => ReturnType<typeof setTimeout>;
clearTimer: (timer: ReturnType<typeof setTimeout>) => void;
showMpvOsd: (text: string) => void;
}
export interface NumericShortcutSessionStartParams {
timeoutMs: number;
onDigit: (digit: number) => void;
messages: NumericShortcutSessionMessages;
}
export function createNumericShortcutSessionService(
deps: NumericShortcutSessionDeps,
) {
let active = false;
let timeout: ReturnType<typeof setTimeout> | null = null;
let digitShortcuts: string[] = [];
let escapeShortcut: string | null = null;
let cancelledMessage = "Cancelled";
const cancel = (showCancelled = false): void => {
if (!active) return;
active = false;
if (timeout) {
deps.clearTimer(timeout);
timeout = null;
}
for (const shortcut of digitShortcuts) {
deps.unregisterShortcut(shortcut);
}
digitShortcuts = [];
if (escapeShortcut) {
deps.unregisterShortcut(escapeShortcut);
escapeShortcut = null;
}
if (showCancelled) {
deps.showMpvOsd(cancelledMessage);
}
};
const start = ({
timeoutMs,
onDigit,
messages,
}: NumericShortcutSessionStartParams): void => {
cancel();
cancelledMessage = messages.cancelled ?? "Cancelled";
active = true;
for (let i = 1; i <= 9; i++) {
const shortcut = i.toString();
if (
deps.registerShortcut(shortcut, () => {
if (!active) return;
cancel();
onDigit(i);
})
) {
digitShortcuts.push(shortcut);
}
}
if (
deps.registerShortcut("Escape", () => {
cancel(true);
})
) {
escapeShortcut = "Escape";
}
timeout = deps.setTimer(() => {
if (!active) return;
cancel();
deps.showMpvOsd(messages.timeout);
}, timeoutMs);
deps.showMpvOsd(messages.prompt);
};
return {
start,
cancel,
isActive: (): boolean => active,
};
}