Skip to content

Keyboard shortcuts: one remappable keymap, page conflicts, keys in every window (keys lane: KS) - #23

Merged
siyabendoezdemir merged 14 commits into
mainfrom
lane/keys
Oct 11, 2026
Merged

siyabendoezdemir merged 14 commits into
mainfrom
lane/keys

Conversation

@siyabendoezdemir

@siyabendoezdemir siyabendoezdemir commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

The keys lane makes every Orbit action one keymap that people can change. It sets whether a key goes to the web page or to Orbit first, and which keys work in each kind of window. It's built from lanes/keys.md and the spec's KS rows, on top of chrome's action registry, with main (PR #18) merged in.

What it builds

  • One keymap with remapping (KS-01, ST-08). Settings → Shortcuts lists every action, each with up to 5 bindings, stored per device. Three can't be changed: Settings (⌘,), the ⌃Tab switcher, and F1 for Help on Windows and Linux.
  • Conflicts with web pages (KS-02):
    • On Windows and Linux, the page gets the first press for Toggle Sidebar (Ctrl+S) and Copy URL (Ctrl+Shift+C). On macOS, and for every other action, Orbit gets the first press (D60). People can change this per action.
    • No key waits more than 250 ms for a busy page.
    • If the page uses a press, a toast offers the action, and a second press within 1 s runs it.
  • Platform rules and default keys:
    • platform rules (KS-03);
    • the command bar and address (KS-04);
    • tabs and windows (KS-05);
    • switching tabs (KS-06) and Spaces (KS-07);
    • split view (KS-09);
    • Peek and Mini Window (KS-10);
    • media (KS-11);
    • Library, history, settings and help (KS-12, with Open File… on ⌘O / Ctrl+O outside a Peek);
    • standard app and window keys (KS-15).
  • Text-editing keys keep their meaning (KS-16). A page gets the keyboard only when the user selects, opens or clicks it, never just because it loaded.
  • Shortcut hints everywhere (KS-18): menus, tooltips and command bar rows show the current binding. The essential shortcuts sheet comes with onboarding (OB-12).
  • Keys inside overlays (KS-19) and in special windows (KS-20):
    • an open overlay takes its keys first;
    • in a private window, actions that need Spaces, pins, Favorites or folders do nothing, and a toast says they aren't available there. Before this, ⌘⇧K archived every private tab.
  • Partial:
    • KS-02: there's no hold-⌘-tap-E action yet.
    • KS-03: Linux doesn't ask at first run about desktop keys that clash.
    • KS-05: it waits for TB-17 and SB-17, and ⌘Z can't be remapped.
    • KS-12: Open Downloads and pinned extensions have no keys yet.
    • KS-13: the DevTools keys are audited against the spec; DT-06's ⌃D comes with Developer mode (D64).
    • KS-19: Esc on a loading page now goes to the page first (D59). Full screen's Esc (NV-20) and the print dialog (NV-07) aren't built yet.
    • KS-20: Mini Windows have no find bar.
  • Not started: KS-14 (P2) waits for Notes, Canvases and Tweaks.

Review

  1. The review (reviews/lane-keys.md) approved it with 3 must-fixes:
    • Back and Forward did nothing while the keyboard was in a frame on the page, and waited 1 s on a busy page;
    • in a Mini Window, ⌘L then Esc left the keyboard with nothing to type into;
    • the private window's Clear button archived every private tab, and its tooltip offered a key that is refused.
  2. Fix round 1 (8688b76..d742522) merged downloads (PR Downloads and DevTools (downloads lane: DL, DT) #20) and fixed all three, each with a test that fails without it. It also:
    • made Esc go to a loading page first, through one bit from the page's preload (D59);
    • set D60's key defaults: Toggle Sidebar prefers Orbit; the page gets at most 250 ms to answer; ⌃` works only while the switcher is up; macOS refuses plain ⌃-letter bindings;
    • fixed F4, F5, F6 and F9, and audited the DevTools keys (KS-13) and the mouse bindings (KS-17).
  3. Fix check: APPROVE, no must-fix (reviews/lane-keys-fixcheck.md).
    • Each fix's test fails without it: six unit cuts and two cut builds.
    • Real typing into a cross-site frame, sent through XTEST under Xvfb, shows that M1's frame path works. The harness's own input can't reach that path.
    • A page can only misjudge its own keys. D59's bit can only keep Esc from stopping that page's own load.
    • The keys, settings, command bar, Mini Window, Peek, core, nav, downloads and devtools specs ran 257 passed, 2 skipped, 0 failed.
    • Its follow-ups go to later lanes:
      • pages: a field in a shadow root counts as the page (K1), and pages can see the focus mirror (K2, D67);
      • finish-views: a Mini Window's empty address, then Enter (K3);
      • finish-downloads: DevTools that are already open keep their old keys after a remap (K4);
      • three checklist notes.
  4. The merge with main (75ffb3a, after import, PR Import: other browsers, bookmarks files, pasted links, history (IM) #22):
    • SidebarItems.tsx and src/shared/actions.ts keep both sides' import lines, and FEATURES.md is regenerated.
    • docs/feature-status.json merged without conflicts and matches both sides leaf by leaf.
    • Import's fix check had read the same merge: src/main/commandbar/sources.ts keeps both lanes' changes.

Merge gate (D42), run locally on Linux on 75ffb3a

  • Static checks, in the PR's worktree: npm run build, npm run typecheck, npm run lint and ./node_modules/.bin/vitest run all exit 0. Unit: 850 passed of 850, in 83 files.
  • E2E: xvfb-run -a -s "-screen 0 1440x900x24" ./node_modules/.bin/playwright test --workers 1 --retries 0 ran 589 tests in 27.8 min: 585 passed, 4 skipped, 0 failed, and it exited 0.
    • TB-22's expected failure (test.fail()) counts as passed.
    • The run was on 75ffb3a itself, with main (fbb9103) in it.
  • macOS and Windows checks, not run here (unverified):
    • macOS: F6's menu (⌘O with no window shown), ⌘S preferring Orbit (D60 (1)), Settings refusing plain ⌃-letter bindings (D60 (4)), and ⌃` and ⌘← in text fields;
    • Windows: all of it.

🤖 Generated with Claude Code


Note

High Risk
Centralizes all keyboard input through new main-process routing and injected page scripts; incorrect precedence or focus handling would affect every window, overlay, and web page interaction.

Overview
Adds the keys lane: a unified, per-device remappable keymap wired through main-process routing, Settings → Shortcuts, and live hints in menus, the command bar, and the new Help → Keyboard Shortcuts sheet.

Routing behavior is documented and implemented in src/main/keys/: keyRouter runs before actions (recording in Settings, recent-tab switcher keys, Open File vs Peek/Mini Window), then decides Orbit vs page first (KS-02) using isolated-world page watchers and a focus mirror so Back/Forward and Ctrl+S / Copy URL can defer to sites with toast + second-press fallback. Esc while loading consults orbit-page:escape before stopping navigation (D59). KEEP_KEYBOARD disables focus-on-navigation so background loads don’t steal focus from the command bar or sidebar (KS-16).

Integration touches include registering registerKeys before other features, keys.* commands and persistence normalization, macOS menu rebuild on remap, private-window action blocking with toasts, and command-bar action shortcut cache invalidation on keymap version changes. Feature checklist and architecture docs are updated to reflect the new KS/ST/OB coverage.

Reviewed by Cursor Bugbot for commit 75ffb3a. Bugbot is set up for automated code reviews on this repo. Configure here.

siyabendoezdemir and others added 10 commits October 10, 2026 14:48
…-16)

Electron gives a web contents the keyboard whenever its page commits a navigation
(focusOnNavigation). That held for a tab hidden behind another and for one that never had
the keyboard, so a slow page finishing its load took the keys from the open command bar,
from a sidebar field being typed in, or from the page in front (keys typed after it went
to the page that loaded). Every web contents Orbit makes now turns it off: tab pages,
pages a tab or Peek opens with window.open, and Orbit's own views (uiWebPreferences).
Orbit already gives a page the keyboard itself when the user selects, opens or clicks it.

keys.spec.ts types where the keyboard is while a slow page commits; the three tests fail
with focusOnNavigation on and pass with it off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…istence (KS-01, KS-18)

Every action's bindings now come from one keymap: the defaults in
src/shared/actions.ts with the user's changes from settings.shortcuts on
top. The key router, the macOS menu bar (rebuilt after a remap), the
command bar's action rows and the tooltips read it live, so a remap
reaches every hint without a restart.

- keys.bind / keys.unbind / keys.reset / keys.resetAll: replace a
  binding or add one (up to 5 per action), take one off (the command bar
  and menus still run the action), reset to the defaults. A binding needs
  a modifier except F1-F12; keys the system keeps are refused; a key
  another action has is reported as a conflict until the caller asks to
  clear or swap it; fixed actions (Settings, the recent-tab switcher)
  keep theirs.
- Bindings that type or edit text (Shift+letter, the caret keys) act
  only outside text fields, like Cmd+Left already did; a Ctrl+Alt press
  that typed an AltGr character acts only outside text fields; plain keys
  belong to an input method while it composes.
- settings.shortcuts and settings.shortcutPriority are cleaned on load.
- keys.record lets Settings -> Shortcuts record a binding in main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…w and F6 (KS-03, KS-05)

- Every default binding is checked against the spec's shortcut tables on macOS, Windows and
  Linux (unit test), with no key bound twice and each one a binding Orbit accepts.
- Close Window is an action: Cmd+Shift+W / Ctrl+Shift+W closes the window, or the Mini Window,
  Settings or onboarding window that has focus; the macOS File menu item runs it.
- F6 moves the keyboard between the sidebar and the page.
- F12 opens DevTools on macOS too; F1 opens Help on Windows and Linux.
- The macOS menu shows bindings that edit text without registering them, so a text field
  keeps them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on prefers websites (KS-02, KS-16)

- before-input-event leaves the press of an action that prefers websites to a
  tab's page; an isolated-world watcher reports whether the page called
  preventDefault. Left alone, the action runs; used, a toast offers the action
  and a second press within a second runs it. Held-key repeats stay the page's.
- Cmd+S / Ctrl+S (Toggle Sidebar) prefers websites everywhere; Ctrl+Shift+C
  (Copy URL) on Windows and Linux (NV-17). Settings' per-action priority wins.
- In a page text field, Cmd+[ / Cmd+] (Alt+Left / Alt+Right) go to the page
  first and go back or forward only when the page leaves them alone (KS-16).
- The macOS menu runs a let-through key's action once.
- e2e: fixture pages that use and ignore the keys, priority set to Orbit,
  Copy URL, the text-field rule, and Cmd+Z undoing typing in a field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ab switcher; tab, window, split and overlay keys checked (KS-02, KS-05, KS-06, KS-09, KS-19)

- KS-02: Electron runs no isolated-world script in a page while its main frame
  loads, so a loading page can't say what it did with a key press. Orbit now
  takes the keys at once then (pageCanAnswer), and every answer it waits for
  has a 1 s limit. Before, ⌘S on a page that was on its way did nothing.
- KS-06 / TB-05: W with ⌃ held while switching to a recent tab closes the
  highlighted tab; the next tab in the switcher takes the highlight.
- KS-05: Rename Tab, Archive Tab and Move Tab to Space… are assignable
  actions with no default keys.
- Main side of Settings → Shortcuts (ST-08): keys.setOptions (Go to Tab
  options) and keys.systemClashes (macOS Mission Control and GNOME
  workspace keys), with unit tests for the parsers.
- e2e tests for KS-05, KS-06, KS-09 and KS-19 from a page; feature status
  for KS-02, KS-05, KS-06, KS-09, KS-19 and TB-05.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…riorities and Go to Tab options (ST-08)

Settings gets a Shortcuts section, added through the section registry. It lists every action main
has registered, grouped, with a search by name, search words or keys. Click a key and press the new
one: the router records it (keys.record), so Orbit's own keys run nothing while Settings listens.
Esc cancels, Backspace removes the binding, + adds another (up to five). A key another action has
opens a dialog offering Swap (when replacing) or Remove from the other action. Right-click an action
for Reset to Default, Remove Shortcut and Prefer Websites / Prefer Orbit (KS-02). Reset All asks
first. Also on the page: the Go to Tab options (Include Favorites, the last or ninth item on ⌘9), and
keys the system keeps marked on their rows (KS-03). keys.available names the registered actions;
settings.shortcuts opens the section from the command bar (KS-12).

The right-click menu closes when the list under it moves, not on a late scroll event from a scroll
made just before it opened.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tcuts sheet (KS-18, OB-12)

KS-18: the tab menu, a multi-selection's menu, the page menu and a split
pane's menu show an item's key when the item does what the key does,
read from the live keymap each time the menu opens. Items that only look
alike get no hint (Copy Page URL copies the raw address; the Markdown
copy key copies one link).

OB-12: Help → Keyboard Shortcuts (macOS Help menu, the command bar
everywhere, assignable) opens a sheet over the window with the essential
keys as they are now, grouped Everyday, Navigation, Spaces and Split
View. Esc, the close button, a click outside, Close Tab's keys or asking
again close it; its link opens Settings → Shortcuts. From Settings or a
Mini Window it opens over the browser window behind.

Tests: menu hints, the sheet's content and the macOS menu template
(KS-15 roles, live keys, Help → Keyboard Shortcuts) as unit tests; two
e2e tests in keys.spec. Status: KS-01, KS-18, OB-12, OB-13 built,
KS-12 partial.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/feature-status.json: SB-11 keeps the keys lane's KS-02 note, SB-12
takes chrome's native-menu note; FEATURES.md regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-20, KS-10)

- A private window has no Spaces, pinned tabs, Favorites or folders: the
  actions that need them (⌘D, ⌃1–9 / Alt+1–9, ⌘⌥← / ⌘⌥→, ⌘⇧K Clear, every
  Space action) do nothing there, from a key, a menu or the command bar, and a
  toast says "Not available in a private window". Before, ⌘⇧K archived every
  private tab. Tab keys still work.
- Mini Window: ⌘L now moves the keyboard into the address field, so what is
  typed next lands there (it stayed with the page). Help → Keyboard Shortcuts
  from a Mini Window opens over the browser window (it was swallowed).
  Both are small edits in src/main/miniwindow/index.ts.
- Tests: private-window keys, Mini Window keys (⌘S, ⌃2, zoom, ⌘R, ⌘⌥O, ⌘L,
  the sheet, ⌘W) and full screen with the sidebar hidden (⌘1–9, ⌘⌥↑/↓).
- The browser window an action from Settings or a Mini Window acts in is
  shared (keys/browser-window.ts) for the sheet and Open File.
- Status: KS-10 yes, KS-20 partial (Mini Window ⌘F, the blank window and
  extension keys are not built).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n Windows and Linux (KS-12, KS-15)

- Open File… (macOS File menu, the command bar): the files chosen open in
  new tabs of the window, the last one selected. ⌘O / Ctrl+O are Open Peek
  as Tab's keys; where no Peek or Mini Window is open, the key router runs
  Open File on them instead (BORROWED_KEYS, keys/open-file.ts). The menu
  shows the key without registering it, and Settings → Shortcuts says it
  under Open File…, which can get keys of its own. From a Mini Window or
  Settings the files open in the browser window behind, which comes forward.
- windows.ts: one line asks keyRouter.actionIn for the action before it runs.
  miniwindow/index.ts: file.open passes through like help.shortcuts.
- `fixed` can be 'other' (Windows and Linux only), read through isFixed:
  F1 for Help can't be changed or taken there; on macOS Help has no keys
  and can be given some.
- Tests: ⌘O with no Peek (two files, cancel), Open File from a Mini Window,
  the Settings note and the locked F1, menu and keymap unit tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 13f39aa3-5f72-4b20-a014-7d9254b5e007)

siyabendoezdemir and others added 3 commits October 10, 2026 19:38
Conflicts:
- LibraryButton.tsx: downloads' button (recent files on hover, right-click) with the
  title from useShortcutLabel('app.library'); the static TITLE and its imports go.
- OverlayApp.tsx: both imports (downloads flyout and the shortcut sheet), in Biome's order.
- feature-status.json: SB-09 from main (the recent-files popover is built); SB-11 keeps
  the KS-02 note. FEATURES.md regenerated.

attachShortcuts keeps its order (record, switcher, actionIn, has, leaveToPage, run); the
run line now passes repeat from main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ading page goes to the page first; D60 priorities (PR #23 review)

- M1: main mirrors where the keyboard is in each tab's page
  (src/main/keys/page-focus.ts). The main frame, and the frame inside it
  that has the keyboard (contents.focusedFrame), say where focus is as it
  moves. So ⌘[ / Alt+← outside text fields goes back at once, in a frame
  too, and a press in a frame is judged by that frame's fields and handlers.
- M2: Esc in a Mini Window's address field gives the keyboard back to its
  page (mini.focusPage).
- M3: a private window's sidebar has no Clear button.
- D59: the page preload sends one bit after a trusted Esc in a tab's main
  frame: whether the page used it. While the tab loads, main waits for it
  250 ms at most and stops the load only if the page left Esc alone
  (src/main/keys/escape.ts).
- D60: ⌘S prefers Orbit on macOS (Ctrl+S prefers websites on Windows and
  Linux); a page's answer is waited for 250 ms at most, no answer counting
  as left alone; ⌃` steps back in the recent-tab switcher on every
  platform, only while it is up; macOS Settings refuses ⌃ with a lone
  letter.
- F4: ⌘Y / Ctrl+H and the Library's keys from a Mini Window act in the
  browser window behind it.
- F5: View Archive in a private window shows the "Not available" hint.
- F6: on macOS the menu takes ⌘O itself while no Orbit window is shown; it
  is built again as windows show, hide, minimize, restore and close.
- KS-13: DevTools rows in the default-keymap table, and remap tests.
  KS-17: e2e for ⌘-click / middle-click on Forward and a double-click on
  the empty sidebar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…2, KS-13, KS-16, KS-17, KS-19, KS-20, SB-11, TB-05, ST-08 and NV-04 notes; regenerate FEATURES.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 4766449b-dcdc-4085-b1a0-3a0a36f75e0a)

Conflicts: SidebarItems.tsx and src/shared/actions.ts each keep both
sides' import lines; FEATURES.md regenerated with scripts/features.mjs.
docs/feature-status.json merged on its own and matches both sides leaf
by leaf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 11, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: a8ba8ecf-74b2-4b88-8c40-78b7613077e7)

@siyabendoezdemir
siyabendoezdemir merged commit 332fb99 into main Oct 11, 2026
1 check passed
siyabendoezdemir added a commit that referenced this pull request Oct 11, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
siyabendoezdemir added a commit that referenced this pull request Oct 11, 2026
…to lane/feel

Conflicts, both sides kept:
- OverlayApp.tsx, PeekLayer.tsx, SidebarItems.tsx: import lines only (keys' ShortcutSheet,
  useKeymap and useShortcutLabel beside the motion imports).
- feature-status.json: keys' KS-09 and the lane's FL-15; FEATURES.md regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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