Skip to content

Add customizable keyboard shortcuts#2425

Closed
nwparker wants to merge 10 commits into
mainfrom
nwparker/keyboard-custom
Closed

Add customizable keyboard shortcuts#2425
nwparker wants to merge 10 commits into
mainfrom
nwparker/keyboard-custom

Conversation

@nwparker

@nwparker nwparker commented May 20, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds a real custom keybinding system for Orca while preserving the current default shortcut ownership model. A shared registry now drives shortcut matching, Settings, file-backed overrides, menu accelerators, browser/terminal handling, conflict checks, and visible shortcut labels.

Users can customize shortcuts through a polished captured-key recorder in Settings > Keyboard Shortcuts or by editing the backing ~/.orca/keybindings.json file directly. The Settings page now includes a keybindings-file split action: Edit in Orca opens the file in the floating workspace editor, while the dropdown offers default-app, external-editor, reveal, and reload paths.

Refs #1493, #713, #891, #1502, #858, #1168.
Related: #1802.

Latest UI follow-up

  • Tightened the Keybindings JSON card further by removing the explanatory helper copy; the card now shows only the backing file label, path, and split-button actions.
  • Fixed the Settings search/sidebar bug where searching keyboard and clicking Shortcuts could leave Terminal content visible.

User-facing behavior

  • Settings > Keyboard Shortcuts is now a real editor instead of the old “customization is not supported” message.
  • The Settings UI records keypresses with a focused “Change shortcut” control rather than asking users to type shortcut syntax manually.
  • Shortcut rows use fixed columns and a reserved status line, so entering recording mode, showing validation, or showing conflict text does not shift the surrounding list.
  • Users can rebind, reset, or disable Orca shortcuts from Settings.
  • Users can open ~/.orca/keybindings.json inside Orca’s floating workspace editor, open it with the OS default app, open it with an external editor, reveal it in the file manager, or reload it from disk.
  • Browser/paired clients now expose a real keybindings preload API backed by browser local storage, so Settings and dynamic shortcut labels do not fail through the fallback proxy.
  • Shortcut hints, tooltips, menu labels, context-menu labels, and other visible shortcut affordances render from the effective keybindings instead of stale hardcoded strings.
  • Conflict warnings are shown before saving in Settings, and invalid/conflicting file edits produce diagnostics instead of silently creating broken runtime state.
  • Terminal selected-text “Find in Folder” now follows the configured sidebar.search.toggle binding instead of hardcoding Cmd/Ctrl+Shift+F.

Defaults and compatibility

This is intentionally customization-first, not a broad terminal/TUI pass-through rewrite.

  • Existing Orca defaults are preserved where Orca had a real existing default behavior.
  • Cmd+J on macOS still opens the worktree switcher by default, including when focus is in a terminal/TUI. Users who need that chord for a TUI can disable or rebind worktree.palette.
  • Terminal-specific readline/helper behavior remains terminal behavior; this PR does not convert those into global app commands.
  • File Explorer delete is represented in the registry now, but its default behavior is preserved unless the user explicitly overrides or disables it.
  • sidebar.checks.toggle is intentionally unassigned by default because the previous UI hint did not correspond to a consistently owned default main-process shortcut.
  • Mod+Shift+Minus / _ is no longer treated as a default zoom-out shortcut, so readline/terminal undo can pass through. Users can bind it explicitly if they want that behavior.
  • When a user disables or rebinds an Orca shortcut, handlers that previously ignored customization now respect that choice. That is the intended behavior change for customized installs.

Implementation

  • Adds src/shared/keybindings.ts as the shared keybinding registry, parser, formatter, matcher, platform mapper, captured-key normalizer, and conflict detector.
  • Adds src/main/keybindings/keybinding-file.ts and src/main/keybindings/keybinding-service.ts for ~/.orca/keybindings.json persistence, migration, validation, and runtime broadcasts.
  • Adds IPC/preload/store support so renderer code receives effective keybindings and file diagnostics.
  • Adds a web preload keybindings adapter so paired/browser clients return valid snapshots, persist overrides, reject conflicts, and notify listeners.
  • Replaces the shortcuts settings placeholder with a visual captured-key editor in ShortcutsPane plus stable row UI in ShortcutBindingRow.
  • Adds KeybindingsFileActions for the backing file card, including in-Orca floating editor open and external/default open paths.
  • Authorizes the managed keybindings file for normal editor filesystem IPC after it is created, so Edit in Orca can read and save a config file outside workspace roots without relaxing general filesystem access.
  • Adds useShortcutLabel and routes visible hints through the effective keybindings.
  • Updates main-process window shortcuts, app menus, browser guest shortcuts, terminal pane policies, renderer keyboard handlers, tab switching, editor shortcuts, file explorer shortcuts, onboarding/settings shortcuts, and contextual labels to use the shared registry.
  • Rebuilds menu accelerators when keybindings change so native menu labels stay aligned with the effective first binding.
  • Checks customized renderer/global bindings against native menu accelerators so users are warned before assigning a chord the OS menu can consume.

File format

The backing file is JSON and supports both common and per-platform overrides:

{
  "version": 1,
  "keybindings": {
    "worktree.quickOpen": ["Mod+P"]
  },
  "platforms": {
    "darwin": {
      "worktree.palette": ["Mod+J"]
    },
    "linux": {},
    "win32": {}
  }
}

Notes:

  • Mod maps to Cmd on macOS and Ctrl on Linux/Windows.
  • A string array assigns one or more shortcuts.
  • [], null, or false disables an action.
  • Unknown actions/platforms are ignored with diagnostics.
  • Settings writes the active platform section so hand-authored common bindings are not accidentally removed for other OSes.
  • The visual Settings recorder writes one captured chord at a time. Users who need multiple bindings for one action can still edit the JSON array directly.

Known technical tradeoffs

  • Conflict detection covers Orca-owned shortcuts in the registry. It cannot know every OS, shell, terminal app, or TUI binding.
  • Native Electron menu accelerators use the first effective binding for each menu action; extra bindings still work through Orca handlers where integrated.
  • There is no per-workspace/profile keybinding layer in this PR.
  • Settings edits target the current platform. Users who want cross-platform shared bindings can edit the keybindings section in JSON.
  • Browser/paired clients use browser local storage for keybindings because they cannot edit the desktop filesystem-backed ~/.orca/keybindings.json file directly.

Testing

Latest local validation after the Settings polish and file-open pass:

  • pnpm typecheck
  • pnpm lint
  • pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/keybindings.test.ts src/main/keybindings/keybinding-file.test.ts
  • git diff --check
  • Electron visual/runtime check on the dev app:
    • Opened Settings > Shortcuts.
    • Verified the keybindings-file split action and dropdown placement.
    • Verified Edit in Orca opens /Users/nwparker/.orca/keybindings.json in the floating workspace editor and loads JSON content without filesystem access errors.
    • Verified shortcut row positions are unchanged before/after entering recording mode.
    • Verified the recording state renders inline without overlapping or moving adjacent rows.

Earlier full focused validation for the PR also passed:

  • pnpm exec vitest run --config config/vitest.config.ts src/shared/keybindings.test.ts src/renderer/src/components/terminal-pane/keyboard-handlers.test.ts src/renderer/src/web/web-preload-api.test.ts src/main/keybindings/keybinding-file.test.ts src/main/window/createMainWindow.test.ts src/main/menu/register-app-menu.test.ts src/shared/window-shortcut-policy.test.ts src/renderer/src/components/terminal-pane/terminal-shortcut-policy.test.ts src/renderer/src/components/right-sidebar/FileExplorer.test.tsx src/renderer/src/components/sidebar/WorktreeCard.pr-display.test.tsx src/renderer/src/components/sidebar/WorktreeCard.lineage.test.tsx src/renderer/src/components/sidebar/WorktreeCard.quick-actions.test.tsx

@nwparker
nwparker force-pushed the nwparker/keyboard-custom branch from d027413 to 82fbf79 Compare May 20, 2026 09:27
@nwparker
nwparker force-pushed the nwparker/keyboard-custom branch 4 times, most recently from 96e238c to 73544ac Compare May 21, 2026 23:14
@nwparker
nwparker force-pushed the nwparker/keyboard-custom branch from 11c9264 to 83839cb Compare May 22, 2026 00:36
@nwparker nwparker closed this May 22, 2026
@nwparker
nwparker deleted the nwparker/keyboard-custom branch May 22, 2026 01:59
@nwparker

Copy link
Copy Markdown
Contributor Author

Superseded by #2581, which contains the rebased branch plus the final Keybindings JSON copy removal. GitHub would not reopen this PR after the branch was recreated during conflict resolution; #2581 has now been merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant