@vielzeug/keymap
v1.1.1
Published
Headless keyboard shortcut manager with chord sequences, context guards, and disposable bindings
Readme
@vielzeug/keymap
Headless keyboard shortcut manager with chord sequences, per-binding context guards, dynamic bindings, trigger control, conflict detection, and disposable lifecycle.
Features
- Chord sequences —
"g g","ctrl+k ctrl+s"with a configurable timeout - Special key aliases —
space,esc,up,down,left,right,del - Modifier aliases —
cmd/command/win→meta;opt/option→alt;control→ctrl;mod→metaon Mac,ctrlelsewhere modKeyoption — explicit platform override for cross-platform tests and SSR- Per-binding
BindingOptions—{ handler, when?, trigger?, priority? }object syntax triggeroption —'keydown'(default) or'keyup'per binding- Dynamic bindings —
map.bind()/map.unbind()at any time — the most recentbind()for a shortcut always wins findShortcutConflicts()— detect prefix/duplicate conflicts before binding a user-customized shortcutformatShortcut()— platform-aware display formatter (⇧⌘Pon Mac,Ctrl+Shift+Pelsewhere)createKeymapLayer()— scoped keymap stack withactivate()/deactivate()- Headless — accepts any
EventTarget; works without a DOM (SSR, Node tests) - Disposable —
dispose()+[Symbol.dispose]forusingdeclarations
Install
pnpm add @vielzeug/keymapQuick start
import { createKeymap, formatShortcut } from '@vielzeug/keymap';
const map = createKeymap({
'mod+k mod+s': () => save(), // ⌘K⌘S on Mac, Ctrl+K Ctrl+S elsewhere
'mod+shift+p': () => openPalette(),
'g g': () => goToTop(),
esc: { handler: closePanel, when: () => isPanelOpen() },
space: { handler: togglePlay, trigger: 'keyup' },
}, { modKey: 'ctrl' }); // explicit platform for tests / SSR
const unmount = map.mount(document);
// Dynamic bindings:
const unbind = map.bind('ctrl+z', () => undo());
unbind(); // or: map.unbind('ctrl+z')
// Display helper:
formatShortcut('mod+shift+p', 'meta'); // '⇧⌘P'
formatShortcut('mod+shift+p', 'ctrl'); // 'Ctrl+Shift+P'
// Cleanup:
unmount(); // remove from this target only
map.dispose(); // or: using map = createKeymap(…)Keymap layers
Stack keymaps for modal UI. Mount the base and the layer independently — each manages its own listeners:
import { createKeymap, createKeymapLayer } from '@vielzeug/keymap';
const base = createKeymap({ 'ctrl+z': undo });
const modal = createKeymapLayer(base, {
esc: { handler: closeModal, when: () => isModalOpen() },
});
const unmountBase = base.mount(document);
const unmountModal = modal.mount(document);
modal.deactivate(); // base handles everything; layer is suspended
modal.activate(); // layer takes over again
modal.parent === base; // true — parent reference is accessible
unmountModal();
unmountBase();