2025-07-18 07:37:12 +06:00
|
|
|
import { useEffect } from "react";
|
2026-01-31 00:20:04 +01:00
|
|
|
import { invokeAction } from "@/lib/actions";
|
2025-07-18 07:37:12 +06:00
|
|
|
import { useKeybindingsStore } from "@/stores/keybindings-store";
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-31 00:20:04 +01:00
|
|
|
* a composable that hooks to the caller component's
|
2025-07-18 07:37:12 +06:00
|
|
|
* lifecycle and hooks to the keyboard events to fire
|
|
|
|
|
* the appropriate actions based on keybindings
|
|
|
|
|
*/
|
|
|
|
|
export function useKeybindingsListener() {
|
2026-01-31 00:20:04 +01:00
|
|
|
const { keybindings, getKeybindingString, keybindingsEnabled, isRecording } =
|
|
|
|
|
useKeybindingsStore();
|
2025-07-18 07:37:12 +06:00
|
|
|
|
2026-01-31 00:20:04 +01:00
|
|
|
useEffect(() => {
|
|
|
|
|
const eventOptions: AddEventListenerOptions = { capture: true };
|
|
|
|
|
const handleKeyDown = (ev: KeyboardEvent) => {
|
|
|
|
|
// do not check keybinds if the mode is disabled
|
|
|
|
|
if (!keybindingsEnabled) return;
|
|
|
|
|
// ignore key events if user is changing keybindings
|
|
|
|
|
if (isRecording) return;
|
2025-07-18 07:37:12 +06:00
|
|
|
|
2026-01-31 00:20:04 +01:00
|
|
|
const binding = getKeybindingString(ev);
|
|
|
|
|
if (!binding) return;
|
2025-07-18 07:37:12 +06:00
|
|
|
|
2026-01-31 00:20:04 +01:00
|
|
|
const boundAction = keybindings[binding];
|
|
|
|
|
if (!boundAction) return;
|
2025-07-18 07:37:12 +06:00
|
|
|
|
2026-01-31 00:20:04 +01:00
|
|
|
const activeElement = document.activeElement;
|
|
|
|
|
const isTextInput =
|
|
|
|
|
activeElement &&
|
|
|
|
|
(activeElement.tagName === "INPUT" ||
|
|
|
|
|
activeElement.tagName === "TEXTAREA" ||
|
|
|
|
|
(activeElement as HTMLElement).isContentEditable);
|
2025-07-27 23:52:59 +05:30
|
|
|
|
2026-01-31 00:20:04 +01:00
|
|
|
if (isTextInput) return;
|
2025-07-27 23:52:59 +05:30
|
|
|
|
2026-01-31 00:20:04 +01:00
|
|
|
ev.preventDefault();
|
2025-07-18 07:37:12 +06:00
|
|
|
|
2026-01-31 00:20:04 +01:00
|
|
|
switch (boundAction) {
|
|
|
|
|
case "seek-forward":
|
|
|
|
|
invokeAction("seek-forward", { seconds: 1 }, "keypress");
|
|
|
|
|
break;
|
|
|
|
|
case "seek-backward":
|
|
|
|
|
invokeAction("seek-backward", { seconds: 1 }, "keypress");
|
|
|
|
|
break;
|
|
|
|
|
case "jump-forward":
|
|
|
|
|
invokeAction("jump-forward", { seconds: 5 }, "keypress");
|
|
|
|
|
break;
|
|
|
|
|
case "jump-backward":
|
|
|
|
|
invokeAction("jump-backward", { seconds: 5 }, "keypress");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
invokeAction(boundAction, undefined, "keypress");
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-07-18 07:37:12 +06:00
|
|
|
|
2026-01-31 00:20:04 +01:00
|
|
|
document.addEventListener("keydown", handleKeyDown, eventOptions);
|
2025-07-18 07:37:12 +06:00
|
|
|
|
2026-01-31 00:20:04 +01:00
|
|
|
return () => {
|
|
|
|
|
document.removeEventListener("keydown", handleKeyDown, eventOptions);
|
|
|
|
|
};
|
|
|
|
|
}, [keybindings, getKeybindingString, keybindingsEnabled, isRecording]);
|
2025-07-18 07:37:12 +06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-31 00:20:04 +01:00
|
|
|
* this composable allows for the UI component to be disabled if the component in question is mounted
|
2025-07-18 07:37:12 +06:00
|
|
|
*/
|
|
|
|
|
export function useKeybindingDisabler() {
|
2026-01-31 00:20:04 +01:00
|
|
|
const { disableKeybindings, enableKeybindings } = useKeybindingsStore();
|
2025-07-18 07:37:12 +06:00
|
|
|
|
2026-01-31 00:20:04 +01:00
|
|
|
return {
|
|
|
|
|
disableKeybindings,
|
|
|
|
|
enableKeybindings,
|
|
|
|
|
};
|
2025-07-18 07:37:12 +06:00
|
|
|
}
|