Skip to content

Add customizable keyboard shortcuts#2581

Merged
nwparker merged 10 commits into
mainfrom
nwparker/keyboard-custom
May 22, 2026
Merged

Add customizable keyboard shortcuts#2581
nwparker merged 10 commits into
mainfrom
nwparker/keyboard-custom

Conversation

@nwparker

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 captured-key recorder in Settings > Keyboard Shortcuts or by editing the backing ~/.orca/keybindings.json file directly. The Settings page includes a compact keybindings-file split action: Edit File 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.
Supersedes #2425, which GitHub would not reopen after the branch was recreated during conflict resolution.

Latest follow-up

  • Rebased onto current main and resolved the sidebar conflict by preserving both repository badge settings navigation and keybinding-aware worktree shortcut handling.
  • Removed the extra The visual editor saves here. helper copy from the Keybindings JSON card.
  • Fixed the Settings search/sidebar bug where searching keyboard and clicking Shortcuts could leave Terminal content visible.
  • Tightened the Keybindings JSON card so it shows only the backing file label, path, and split-button actions.

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 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” 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 File 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 rebase and the final Keybindings JSON copy removal:

  • pnpm lint
  • pnpm typecheck
  • git diff --check
  • rg -n "The visual editor saves here" src/renderer/src/components/settings/KeybindingsFileActions.tsx src || true
  • pnpm exec vitest run --config config/vitest.config.ts src/shared/keybindings.test.ts src/main/keybindings/keybinding-file.test.ts src/main/ipc/keybindings.test.ts

Earlier focused validation for this 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
  • Electron visual/runtime check on the dev app:
    • Opened Settings > Shortcuts.
    • Verified the keybindings-file split action and dropdown placement.
    • Verified Edit File in Orca opens /Users/nwparker/.orca/keybindings.json in the floating workspace editor and loads JSON content without filesystem access errors.
    • Verified searching keyboard in Settings and then clicking Shortcuts shows the Keyboard Shortcuts content instead of leaving Terminal content visible.
    • Verified shortcut row positions are unchanged before/after entering recording mode.
    • Verified the recording state renders inline without overlapping or moving adjacent rows.

@nwparker
nwparker merged commit 6408e49 into main May 22, 2026
2 checks passed
@nwparker
nwparker deleted the nwparker/keyboard-custom branch May 22, 2026 02:10
thomaszdxsn pushed a commit to thomaszdxsn/orca that referenced this pull request Jun 15, 2026
* Add customizable keyboard shortcuts

- Add customizable keybindings
- Fix keybinding customization bypasses

* Keep shortcut UI labels dynamic

* Fix keybinding review gaps

* Improve shortcut recording UX

* Include settings section metadata in search

* Polish shortcut settings editor

* Tighten shortcut settings rows

* Clarify keybindings file card

* Clear settings search on section select

* Remove keybindings file helper copy
yejinh added a commit to yejinh/orca that referenced this pull request Jul 13, 2026
On macOS, tab.newSimulator (New mobile emulator tab) and
sidebar.explorer.toggle (Show Explorer) both defaulted to Mod+Shift+E.
Show Explorer had the chord first (stablyai#2581); stablyai#4754 reused it for the
emulator. When focus is in a terminal, the terminal-scope keydown handler
fires first, calls preventDefault(), and opens the emulator, so the global
Show Explorer toggle never runs.

The conflict detector missed this because the two actions live in
different scopes (tabs vs global) and tab.newSimulator never declared a
conflictGroup, unlike the comparable tab.openQuickCommandsMenu action.

- Move tab.newSimulator's macOS default to Mod+Shift+S (S = Simulator),
  leaving Cmd+Shift+E to Show Explorer (matches VS Code).
- Add conflictGroup: 'global' so findKeybindingConflicts and the Settings
  UI flag any future overlap with a global chord.
- Add a regression test covering the default chord and the guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
yejinh added a commit to yejinh/orca that referenced this pull request Jul 13, 2026
On macOS, tab.newSimulator (New mobile emulator tab) and
sidebar.explorer.toggle (Show Explorer) both defaulted to Mod+Shift+E.
Show Explorer had the chord first (stablyai#2581); stablyai#4754 reused it for the
emulator. When focus is in a terminal, the terminal-scope keydown handler
opens the emulator, so the chord no longer reliably shows the Explorer.

The conflict detector missed this because the two actions live in
different scopes (tabs vs global) and tab.newSimulator never declared a
conflictGroup, unlike the comparable tab.openQuickCommandsMenu action.

- Move tab.newSimulator's macOS default to Mod+Shift+S (S = Simulator),
  leaving Cmd+Shift+E to Show Explorer (matches VS Code).
- Add conflictGroup: 'global' so findKeybindingConflicts and the Settings
  UI flag any future overlap with a global chord.
- Add a regression test covering the default chord and the guard.
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