How Keyboard Shortcuts Are Configured and Matched Against User Input in OpenScreen

OpenScreen implements a type-safe shortcut system in src/lib/shortcuts.ts that maps discrete actions to key bindings, validates conflicts against fixed and mutable shortcuts, and matches runtime KeyboardEvent objects using platform-aware modifier detection.

OpenScreen treats keyboard shortcuts as strongly typed configuration objects that bridge user preferences with runtime input handling. The entire workflow—from defining default bindings to detecting conflicts and matching live keystrokes—lives in a single utilities file that ensures deterministic behavior across Windows, macOS, and Linux.

Defining the Shortcut Schema

The foundation of the system is a finite set of actions and their corresponding data structures declared at the top of src/lib/shortcuts.ts. The SHORTCUT_ACTIONS array enumerates every available command, while the ShortcutBinding interface defines the shape of a single mapping—specifying the key, ctrl, shift, and alt properties. These are aggregated into the ShortcutsConfig type, which represents the complete user-facing configuration object.

This strict typing guarantees that every action referenced in the UI or settings panel corresponds to a valid entry in the configuration schema. Source: src/lib/shortcuts.ts#L1-L22

Default and Fixed Shortcut Mappings

OpenScreen maintains two distinct categories of shortcuts to separate user-customizable actions from system-reserved commands.

Default mutable shortcuts (DEFAULT_SHORTCUTS) provide sensible initial bindings for every action that users can later override through the settings interface. These mappings cover common operations like play/pause or zoom controls.

Fixed shortcuts (FIXED_SHORTCUTS) represent UI-only shortcuts that cannot be remapped—such as Undo and Redo. These are used exclusively for display purposes and conflict detection, ensuring that critical system conventions remain protected. Source: src/lib/shortcuts.ts#L29-L52 and src/lib/shortcuts.ts#L85-L103

Matching Runtime Keyboard Events

When a KeyboardEvent bubbles through the application, OpenScreen evaluates it against stored configurations using the matchesShortcut function. This utility performs three precise checks:

  1. The pressed key matches the binding’s key property (case-insensitive).
  2. The primary modifier matches the binding’s ctrl flag—using Meta on macOS and Ctrl on Windows/Linux.
  3. The Shift and Alt modifiers match their respective boolean flags in the binding.

If all conditions satisfy, the function returns true, signaling that the event corresponds to the specific shortcut action. Source: src/lib/shortcuts.ts#L105-L118

import { matchesShortcut, DEFAULT_SHORTCUTS } from "@/lib/shortcuts";

function onKeyDown(e: KeyboardEvent) {
  const shortcuts = DEFAULT_SHORTCUTS; // normally `mergeWithDefaults(savedConfig)`
  const playBinding = shortcuts.playPause; // { key: " " }
  
  if (matchesShortcut(e, playBinding, navigator.platform.includes("Mac"))) {
    togglePlayPause();
    e.preventDefault();
  }
}

Conflict Detection Before Persistence

Before persisting a new key binding, OpenScreen validates it against existing mappings using findConflict. This function traverses both FIXED_SHORTCUTS and the current ShortcutsConfig to detect collisions.

It returns a conflict object specifying whether the clash is fixed (against a non-remappable system shortcut) or configurable (against another user-defined action). This prevents users from accidentally breaking essential navigation patterns or duplicating existing commands. Source: src/lib/shortcuts.ts#L67-L82

import { findConflict, DEFAULT_SHORTCUTS } from "@/lib/shortcuts";

const newBinding = { key: "z", ctrl: true };
const conflict = findConflict(newBinding, "addZoom", DEFAULT_SHORTCUTS);

if (conflict) {
  if (conflict.type === "fixed") {
    console.warn(`Clashes with fixed shortcut: ${conflict.label}`);
  } else {
    console.warn(`Clashes with action: ${conflict.action}`);
  }
}

Merging User Settings with Defaults

When the application loads stored preferences, the mergeWithDefaults function overlays the user’s partial configuration onto DEFAULT_SHORTCUTS. This guarantees that any newly added actions—present in the defaults but absent from the user’s saved data—automatically receive their standard bindings without requiring manual migration.

The merge operation preserves user customizations while ensuring the configuration object always contains a complete, valid set of actions. Source: src/lib/shortcuts.ts#L40-L48

Summary

  • Typed Configuration: All keyboard shortcuts in OpenScreen are defined as type-safe objects in src/lib/shortcuts.ts, with separate schemas for mutable (DEFAULT_SHORTCUTS) and fixed (FIXED_SHORTCUTS) bindings.
  • Platform-Aware Matching: The matchesShortcut function normalizes platform differences by detecting macOS via the isMac parameter to correctly validate Meta versus Ctrl modifiers.
  • Conflict Validation: The findConflict utility prevents users from remapping fixed system shortcuts or creating duplicate bindings, distinguishing between fixed and configurable collisions.
  • Graceful Upgrades: mergeWithDefaults ensures backward compatibility by filling gaps in user configurations with default values when the application updates.

Frequently Asked Questions

How does OpenScreen handle platform differences for modifier keys?

The matchesShortcut function accepts an isMac boolean parameter derived from navigator.platform. When true, it validates the Meta key against the binding’s ctrl property; otherwise, it checks the Ctrl key. This allows a single configuration schema to work across macOS, Windows, and Linux without duplicating entries.

Can users override fixed shortcuts like Undo and Redo?

No. Fixed shortcuts defined in FIXED_SHORTCUTS are immutable and used only for display and conflict detection. The findConflict function explicitly checks new bindings against this set and returns a type: "fixed" conflict if the user attempts to remap a protected shortcut.

What happens if a user’s saved shortcuts are missing new actions?

The mergeWithDefaults function overlays the persisted user configuration onto DEFAULT_SHORTCUTS. Any actions missing from the saved data automatically inherit their default bindings from the base configuration, ensuring the application always has a complete shortcut map.

Where are the human-readable labels for shortcuts stored?

While the core logic resides in src/lib/shortcuts.ts, localized UI labels are maintained in JSON files such as src/i18n/locales/en/shortcuts.json, src/i18n/locales/zh-CN/shortcuts.json, and src/i18n/locales/es/shortcuts.json. This separation keeps the matching logic platform-agnostic while supporting multilingual interfaces.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →