diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-03-25 16:47:22 +0100 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-03-29 15:48:22 +0200 |
| commit | 8db3bead13441a5882d8689dfc6a9f09c82375d9 (patch) | |
| tree | e9d0cafa93904ffcbec3188c37961bcc16c6422d /apps/web/src/hooks/use-keybindings.ts | |
| parent | 39ea298a9ccba3104c549c0ca079c231981811f5 (diff) | |
| download | opencut-8db3bead13441a5882d8689dfc6a9f09c82375d9.tar.gz | |
feat: masks, properties refactor, shaders, storage migrations, and more
Diffstat (limited to 'apps/web/src/hooks/use-keybindings.ts')
| -rw-r--r-- | apps/web/src/hooks/use-keybindings.ts | 163 |
1 files changed, 135 insertions, 28 deletions
diff --git a/apps/web/src/hooks/use-keybindings.ts b/apps/web/src/hooks/use-keybindings.ts index e59d1eaf..69a4e847 100644 --- a/apps/web/src/hooks/use-keybindings.ts +++ b/apps/web/src/hooks/use-keybindings.ts @@ -1,6 +1,8 @@ import { useEffect } from "react"; import { invokeAction } from "@/lib/actions"; import { useKeybindingsStore } from "@/stores/keybindings-store"; +import { useTimelineStore } from "@/stores/timeline-store"; +import { isTypableDOMElement } from "@/utils/browser"; /** * a composable that hooks to the caller component's @@ -8,31 +10,141 @@ import { useKeybindingsStore } from "@/stores/keybindings-store"; * the appropriate actions based on keybindings */ export function useKeybindingsListener() { - const { keybindings, getKeybindingString, keybindingsEnabled, isRecording } = - useKeybindingsStore(); + const { + keybindings, + getKeybindingString, + overlayDepth, + isLoadingProject, + isRecording, + } = useKeybindingsStore(); + const clipboard = useTimelineStore((state) => state.clipboard); useEffect(() => { const eventOptions: AddEventListenerOptions = { capture: true }; + // #region agent log + fetch("http://127.0.0.1:7245/ingest/669b22f8-172b-4e65-aa3f-1c702ede83f7", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Debug-Session-Id": "3997d9", + }, + body: JSON.stringify({ + sessionId: "3997d9", + runId: "initial", + hypothesisId: "H1", + location: "use-keybindings.ts:effect", + message: "Keybindings listener mounted", + data: { + overlayDepth, + isLoadingProject, + isRecording, + keybindingCount: Object.keys(keybindings).length, + }, + timestamp: Date.now(), + }), + }).catch(() => {}); + // #endregion 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; + const normalizedKey = (ev.key ?? "").toLowerCase(); + const shouldLogKey = + ev.code === "Space" || + ev.code.startsWith("Key") || + ["escape", "delete", "backspace", "enter"].includes(normalizedKey); - const binding = getKeybindingString(ev); - if (!binding) return; - - const boundAction = keybindings[binding]; - if (!boundAction) return; + if (overlayDepth > 0 || isLoadingProject || isRecording) { + if (shouldLogKey) { + // #region agent log + fetch( + "http://127.0.0.1:7245/ingest/669b22f8-172b-4e65-aa3f-1c702ede83f7", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Debug-Session-Id": "3997d9", + }, + body: JSON.stringify({ + sessionId: "3997d9", + runId: "initial", + hypothesisId: "H2", + location: "use-keybindings.ts:blocked", + message: "Shortcut blocked by runtime gate", + data: { + key: ev.key, + code: ev.code, + overlayDepth, + isLoadingProject, + isRecording, + targetTag: + ev.target instanceof HTMLElement ? ev.target.tagName : null, + }, + timestamp: Date.now(), + }), + }, + ).catch(() => {}); + // #endregion + } + return; + } + const binding = getKeybindingString(ev); const activeElement = document.activeElement; const isTextInput = - activeElement && - (activeElement.tagName === "INPUT" || - activeElement.tagName === "TEXTAREA" || - (activeElement as HTMLElement).isContentEditable); + activeElement instanceof HTMLElement && + isTypableDOMElement({ element: activeElement }); + const boundAction = binding ? keybindings[binding] : undefined; + + if (shouldLogKey || binding || boundAction) { + // #region agent log + fetch( + "http://127.0.0.1:7245/ingest/669b22f8-172b-4e65-aa3f-1c702ede83f7", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Debug-Session-Id": "3997d9", + }, + body: JSON.stringify({ + sessionId: "3997d9", + runId: "initial", + hypothesisId: !binding ? "H3" : isTextInput ? "H5" : "H4", + location: "use-keybindings.ts:keydown", + message: "Shortcut keydown observed", + data: { + key: ev.key, + code: ev.code, + binding, + boundAction: boundAction ?? null, + isTextInput, + keybindingCount: Object.keys(keybindings).length, + activeTag: + activeElement instanceof HTMLElement + ? activeElement.tagName + : null, + targetTag: + ev.target instanceof HTMLElement ? ev.target.tagName : null, + }, + timestamp: Date.now(), + }), + }, + ).catch(() => {}); + // #endregion + } + + if (normalizedKey === "escape" && isTextInput) { + activeElement.blur(); + return; + } + + if (!binding) return; + if (!boundAction) return; if (isTextInput) return; + if (boundAction === "paste-copied") { + if (!clipboard?.items.length) return; + ev.preventDefault(); + invokeAction("paste-copied", undefined, "keypress"); + return; + } ev.preventDefault(); @@ -59,17 +171,12 @@ export function useKeybindingsListener() { return () => { document.removeEventListener("keydown", handleKeyDown, eventOptions); }; - }, [keybindings, getKeybindingString, keybindingsEnabled, isRecording]); -} - -/** - * this composable allows for the UI component to be disabled if the component in question is mounted - */ -export function useKeybindingDisabler() { - const { disableKeybindings, enableKeybindings } = useKeybindingsStore(); - - return { - disableKeybindings, - enableKeybindings, - }; + }, [ + keybindings, + getKeybindingString, + overlayDepth, + isLoadingProject, + isRecording, + clipboard, + ]); } |
