Create a new input context manager with default context configurations.
Initializes all predefined contexts (NAVIGATION, FLY_CONTROLS, TYPING, UI_INTERACTION) with appropriate priorities and key filters. Starts in NAVIGATION context.
PrivatecurrentPrivatecontextPrivatebindingsPrivateactionPrivatecontextPrivate ReadonlybuiltPrivateenabledPrivatekeyRe-entrance depth for handleKeyEvent. A binding handler that
(mis)configured itself to dispatch keyboard events back through
the context manager could otherwise recurse infinitely; cap at
MAX_KEY_EVENT_DEPTH and bail with a single error log.
PrivateinitializePrivate
Initialize default context configurations with priorities and key filters.
Sets up four predefined contexts:
Register a custom input context. Context identifiers must be unique.
Remove a custom context and all bindings registered under it.
Push a new context onto the stack, saving current context.
Used for nested contexts like modal dialogs over main view. The previous context is saved and will be restored when popContext() is called. If the new context is the same as current, does nothing.
Context to activate and push onto stack
Pop the current context and restore the previous one from stack.
Used to return to previous context after closing modal/dialog. If stack is empty, does nothing (stays in current context).
Set the current context, replacing current without saving to stack.
Use this for mode switches (navigation → fly controls) rather than nested contexts. The previous context is NOT saved - use pushContext() if you need to restore the previous context later.
Logs context change for debugging.
Context to switch to
Get the currently active input context.
Current context enum value (NAVIGATION, FLY_CONTROLS, etc.)
Register a key binding for a specific context.
Associates a key (with optional modifiers) to a handler function within a specific context. The binding will only be active when that context is current. Warns if the binding conflicts with an existing binding.
Context where this binding should be active
Key binding configuration with key, modifiers, and handler
Key binding configuration
Stable action identity, independent of the registered chord.
OptionalactionParameter?: string | numberOptional discriminator for parameterized actions sharing one identity.
Optionalmodifiers?: { ctrl?: boolean; shift?: boolean; alt?: boolean; meta?: boolean }Return false synchronously to leave the event available to lower-priority contexts.
OptionalkeyupHandler?: (event: KeyboardEvent) => boolean | void | Promise<void>Modifier-aware bindings match keyup only while those modifiers remain held. Async handlers are always handled; only a synchronous false can decline.
OptionalpreventDefault?: booleanShortcut-overlay metadata, or an explicit opt-out.
// Register [ key for backward navigation
manager.registerBinding(InputContext.NAVIGATION, {
actionId: 'dimension.navigate',
actionParameter: -1,
key: '[',
handler: () => navigateBackward(),
preventDefault: true,
description: 'Step along the selected dimension',
help: false
});
// Register Ctrl+S for save (with modifier)
manager.registerBinding(InputContext.UI_INTERACTION, {
actionId: 'document.save',
key: 's',
modifiers: { ctrl: true },
handler: () => save(),
preventDefault: true,
description: 'Save document',
help: false
});
Unregister a previously registered key binding.
Removes the binding for the specified key and modifiers in the given context. Has no effect if the context or binding doesn't exist, which keeps teardown idempotent after context removal.
Context containing the binding to remove
Key that was bound
Optionalmodifiers: { ctrl?: boolean; shift?: boolean; alt?: boolean; meta?: boolean }
Optional modifiers that were bound
Handle a keyboard event with context-aware routing.
Routes the event through the context system to find and execute the appropriate handler. Processing order:
Keyboard event to handle
Event type ('down' for keydown, 'up' for keyup)
true if event was handled by a binding, false if not handled (return value indicates whether to prevent default behavior)
// In event listener
document.addEventListener('keydown', (event) => {
const handled = contextManager.handleKeyEvent(event, 'down');
if (handled) {
// Event was handled by context system
console.log('Key handled by context manager');
} else {
// No handler found, let it propagate
console.log('Key not handled, continuing...');
}
});
PrivatehandlePrivateisPrivate
Check if a binding is allowed in the given context based on filters.
Checks both blockedKeys and allowedKeys filters:
Base key to check
Canonical modifier-aware binding key
Context configuration with key filters
true if key is allowed in this context, false if blocked
PrivatedispatchDispatch Escape from a typing context.
Walks built-in contexts plus the active context in priority order
and fires the first matching Escape binding. Mirrors the dispatch
shape of tryLowerContexts but does not exclude the current
context — Escape is most often registered in NAVIGATION (the
default current context), so excluding the current context like
tryLowerContexts does would skip it.
PrivatetryPrivate
Try to handle an event in the active context's declared fallbacks.
When current context doesn't handle a key and has passthrough enabled, this method tries its declared fallback contexts in descending priority order. For example, navigation shortcuts work in FLY_CONTROLS because that context explicitly falls back to NAVIGATION.
Keyboard event to handle
Event type ('down' or 'up')
true if a declared fallback handled the event, false otherwise
PrivateisPrivate
Check if currently in a typing context (should block shortcuts).
Returns true if:
Used to prevent keyboard shortcuts from interfering with text entry. For example, prevents 'p' key from toggling performance stats while user is typing "apple" in a search box.
true if in typing context, false otherwise
PrivategetPrivate
Generate unique string key for a key binding (for Map storage).
Combines key and modifiers into a sorted string representation. Format: "key+mod1+mod2" (alphabetically sorted modifiers). Used as key in Map to store and lookup bindings.
Key binding configuration
Unique string key (e.g., "w", "[", "s+ctrl", "z+ctrl+shift")
// Simple key
const key1 = getBindingKey({ key: '[', handler: () => {} });
console.log(key1); // "["
// Key with modifiers (always sorted)
const key2 = getBindingKey({
key: 's',
modifiers: { ctrl: true, shift: true },
handler: () => {}
});
console.log(key2); // "ctrl+s+shift" (sorted alphabetically)
PrivategetPrivategetPrivate
Generate binding key from keyboard event for lookup.
Converts KeyboardEvent to the same string format as getBindingKey() for Map lookup. Checks modifier properties (ctrlKey, shiftKey, etc.) and combines with key in sorted format.
Keyboard event to convert
Unique string key matching getBindingKey() format
Enable or disable the entire context manager.
When disabled, keydown events return false without processing. Keyup handlers still run so stateful bindings can release held input. Useful for temporarily suspending context-based input handling without leaving movement or modifier state latched.
true to enable context management, false to disable
Get debug information about current context manager state.
Returns snapshot of current state for debugging and diagnostics. Useful for understanding why a key isn't working or what context is active.
Object containing: - currentContext: Active context - contextStack: Stack of pushed contexts - registeredBindings: Map of context → binding keys
const debug = contextManager.getDebugInfo();
console.log('Current context:', debug.currentContext);
console.log('Context stack:', debug.contextStack);
console.log('Bindings in NAVIGATION:', debug.registeredBindings.get(InputContext.NAVIGATION));
// Output:
// Current context: navigation
// Context stack: []
// Bindings in NAVIGATION: [{ actionId: 'help.toggle', key: 'h', ... }]
Snapshot of registered binding metadata grouped by input context.
Resolve an action label in the active context, then its explicit fallbacks.
Clear all registered key bindings for a specific context.
Removes all bindings associated with the specified context. Useful when dynamically changing context configuration or cleaning up temporary bindings.
Context whose bindings should be cleared
Reset context manager to initial state.
Clears all registered bindings, empties context stack, and returns to NAVIGATION context. Useful when reinitializing the application or cleaning up for testing.
Restores the built-in context configurations and removes custom contexts.
PrivatecopyPrivaterequirePrivaterecompute
Manages context-aware keyboard input routing to prevent conflicts.
Provides a hierarchical context system where different parts of the UI can register key bindings without conflicting. FLY_CONTROLS derives its allowlist from its own registrations and falls back to NAVIGATION, while NAVIGATION reserves no chords globally.
Key features:
Example