Focus
Focus handling keeps canvas keyboard shortcuts from stealing keystrokes meant for a text field. Whenever the user is typing into an <input>, <textarea>, <select>, or any contenteditable host, global handlers must stand down.
isEditableTargetโ
isEditableTarget returns true when the given node is an <input>, <textarea>, <select>, or contenteditable element. It is exported from both @oh-just-another/editor and @oh-just-another/state. Note it checks exactly the node you pass โ inside a shadow root, resolve the real focused node first with event.composedPath()[0] (where event.target would be retargeted to the host), which is what bindEditorHotkeys does internally.
import { isEditableTarget } from "@oh-just-another/editor";
window.addEventListener("keydown", (ev) => {
if (isEditableTarget(ev.target)) return; // user is typing โ leave it alone
// ... canvas shortcut handling
});
Built into hotkey bindingโ
bindEditorHotkeys already applies this check for you: keystrokes aimed at an editable target are left alone, with one exception โ Escape still passes through, blurring the field first.
import { bindEditorHotkeys } from "@oh-just-another/editor";
const unbind = bindEditorHotkeys(editor); // listens on window by default
// later: unbind();
Scope shortcuts to a specific element (useful when several editors share a page, or the editor lives in a shadow root) via the target option:
bindEditorHotkeys(editor, { target: editor.hostElement });
So for the common case you never call isEditableTarget directly โ it is the guard that makes canvas hotkeys coexist with text editing.