/** * Central keymap module (#132). * * The single source of truth for every **global** and **app-command** keyboard * shortcut. It exists because the browser is not one platform: bindings must be * planned around what is actually interceptable per OS/browser (see * `docs/KEYMAP.md`), conflicts between bindings (or with an embedding host) must * be detectable, and a host must be able to remap or disable any binding. * * This file is pure mechanism — no React, no styling — so it stays engine-side * of the #143 layering (`guard:purity`). React wiring lives in * `src/context/KeymapContext.tsx`. * * ## Modifier normalization * `Mod` is the platform primary modifier: **Ctrl** on Windows/Linux, **⌘** on * macOS. A single `Mod+A` registration is Ctrl+A on Windows and Cmd+A on a Mac. * `Ctrl`/`Cmd`(`Meta`)/`Alt`/`Shift` in a combo are literal physical keys. */ export type KeymapScope = 'global' | 'desktop' | 'app'; export interface ShortcutSpec { /** Stable id, e.g. `'window.close'`, `'startMenu.toggle'`, `'calc.equals'`. */ id: string; /** Combo string, e.g. `'Mod+A'`, `'Alt+F4'`, `'Ctrl+Esc'`, `'F2'`, 'Alt+`'`. */ combo: string; /** * - `'global'` — fires whenever the instance is active (even with a window focused). * - `'desktop'` — fires only when the desktop plane is focused (no window/dialog/input). * - `'app'` — fires only when `appId`'s window is focused. */ scope: KeymapScope; /** Required for `scope: 'app'` — the owning app's id. */ appId?: string; /** Allow the binding while a text input/textarea/contentEditable is focused. Default false. */ allowInInput?: boolean; /** Call `preventDefault()` when matched. Default true. */ preventDefault?: boolean; /** Higher values dispatch first. Use for temporary modal interaction modes. */ priority?: number; /** Human label for docs/UI (optional). */ label?: string; } export interface ParsedCombo { mod: boolean; ctrl: boolean; meta: boolean; alt: boolean; shift: boolean; key: string; } /** Normalize a key token (from a combo string or a KeyboardEvent.key) to a canonical form. */ export declare function normalizeKey(raw: string): string; /** Parse a combo string like `'Mod+Shift+S'` into its canonical parts. */ export declare function parseCombo(combo: string): ParsedCombo; /** Best-effort macOS detection (overridable for tests via the Keymap constructor). */ export declare function detectIsMac(): boolean; interface ModifierEvent { ctrlKey: boolean; metaKey: boolean; altKey: boolean; shiftKey: boolean; key: string; } /** * Does a parsed combo match a keyboard event on this platform? `Mod` resolves to * the physical primary modifier (Cmd on macOS, Ctrl elsewhere). */ export declare function comboMatchesEvent(p: ParsedCombo, e: ModifierEvent, isMac: boolean): boolean; /** Canonicalize a combo string so `'Shift+Mod+s'` and `'mod+shift+S'` collide. */ export declare function normalizeCombo(combo: string): string; export interface DispatchContext { /** appId of the currently focused window, if any. */ focusedAppId?: string; /** Whether a text input / textarea / contentEditable currently has focus. */ inInput: boolean; /** Whether focus is inside a window/dialog/menu (i.e. NOT on the bare desktop plane). */ inWindow?: boolean; } export interface KeymapOptions { isMac?: boolean; /** Explicit OS-package modifier for `Mod`; takes precedence over host detection. */ primaryModifier?: 'ctrl' | 'meta'; /** Per-id overrides: a new combo string, or `null` to disable that binding. */ overrides?: Record; /** Disable every `scope: 'global'` binding (sugar for the `disableGlobalShortcuts` prop). */ disableGlobalShortcuts?: boolean; /** Emit dev conflict warnings. Default: `import.meta.env.DEV`. */ warnOnConflict?: boolean; } /** * A per-instance shortcut registry. Register bindings, then feed it keyboard * events via {@link Keymap.dispatch}. Two `` instances each get their * own `Keymap` so their shortcuts never cross-fire. */ export declare class Keymap { readonly isMac: boolean; private entries; private overrides; private disableGlobal; private warn; constructor(opts?: KeymapOptions); setOverrides(overrides: Record): void; setDisableGlobalShortcuts(disable: boolean): void; /** The effective combo for an id after overrides: the string, or `null` if disabled. */ effectiveCombo(spec: ShortcutSpec): string | null; /** Register a binding; returns an unregister function. Re-registering the same id replaces it. */ register(spec: ShortcutSpec, handler: () => unknown): () => void; /** Feed a keyboard event. Returns true if a binding handled it. */ dispatch(e: KeyboardEvent, ctx: DispatchContext): boolean; /** Snapshot of active (id, combo, scope) — used by docs/tests. */ list(): { id: string; combo: string | null; scope: KeymapScope; appId?: string; }[]; } /** True when `el` is a text-entry surface where global shortcuts should stand down. */ export declare function isTextEntryElement(el: Element | null): boolean; /** * True when `el` sits inside a window, dialog, menu or resize handle — i.e. NOT * on the bare desktop plane. Mirrors the focus guard the Desktop already uses, so * `desktop`-scope shortcuts don't fire while a window/dialog is focused. */ export declare function isInsideWindow(el: Element | null): boolean; export {};