mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-02 06:22:42 -08:00
122 lines
3.1 KiB
TypeScript
122 lines
3.1 KiB
TypeScript
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 createNumericShortcutRuntime(options: NumericShortcutRuntimeOptions) {
|
|
const createSession = () =>
|
|
createNumericShortcutSession({
|
|
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 createNumericShortcutSession(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,
|
|
};
|
|
}
|