* Scope workspace deletion shortcuts to selected sidebar rows
* Complete shortcut event typing and web bridge cleanup
* Use stable fixture rows and typed input elements
* Align sidebar deletion and navigation with eligible focused rows
* Pass explicit absent repo for selected row host lookup
* Describe selected options in keyboard test fixture
* Bound sidebar selection and deletion work and preserve host ownership
* Keep navigation imports within the file size limit
* Separate navigation type imports consistently
* Match selected workspace repositories by explicit host
* Use Ctrl+K for search on Linux and Windows
Preserve existing search and terminal clear shortcuts for legacy profiles until reset.
* Preserve existing Windows and Linux search shortcuts on rollout
* Preserve saved shortcuts in new profiles
* Keep window shortcut tests independent of defaults
* Use Cmd+K for search on new Mac profiles
Preserve existing search and terminal clear shortcuts during migration
on desktop and web.
* Preserve legacy Mac shortcuts during web profile migration
* Preserve keybinding resets across profiles and platforms
* Preserve legacy shortcuts and markdown link bindings
* Separate profile shortcut defaults from shared overrides
* Check shortcut conflicts when restoring defaults
* Fix tab shortcuts targeting floating workspace panels
* Add collapse and expand all projects controls
Support a sidebar button, keyboard shortcut, and Alt-click on project
headers, with a contextual tip to introduce the feature.
* rm mockup file
* Clean up project header toggle handling and fixtures
* fix(shortcuts): give Windows and Linux chat switching a working default
* test(shortcuts): verify the worktree and chat chords independently
* fix(shortcuts): preserve agent newline keys while switching chat views
* fix(shortcuts): use Ctrl+Shift+F11 for Windows and Linux chat switching
The Ctrl+Alt+Shift+J default could not match non-Latin layouts or IME
input, because Ctrl+Alt chords skip the physical-key fallback that
protects AltGr characters. A function-key primary matches on every
layout; Ctrl+Shift+J stays as the alias where Switch worktree doesn't
claim it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(shortcuts): keep the chat toggle's own default alias when its shortcuts are edited
Saving any change to Switch between Chat UI and terminal on Windows and
Linux kept its untouched Ctrl+Shift+J alias, which the conflict check
then refused against the worktree palette's default, so replacing or
removing the primary failed. The check now exempts a chord per binding
when it is the action's own default and every other owner also holds it
as a default, so non-default explicit claims are still refused.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(shortcuts): expect the new Windows/Linux chat switch label
The context menus show the toggle's primary binding, which is now
Ctrl+Shift+F11 on Windows and Linux.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Change Plugins shortcut defaults by platform
* Test Plugins shortcut matching on all platforms
* Test current Plugins default in browser shortcut forwarding
* feat(browser): add a shortcut to open a browser tab in a split to the right
* fix(browser): keep new-browser handlers void so the focus-read ratchet passes
* fix(keybindings): show the ⇧ glyph for Shift on Windows and Linux
Spelled-out Shift made non-mac shortcut labels long (Ctrl+Shift+Backspace).
* test(menu): expect the ⇧ glyph in non-mac app menu hints; align double-tap hint
* fix(native-chat): make the chat view toggle a remappable keybinding
The terminal and Chat UI toggle was hardcoded to Mod+Shift+J, so users
could not rebind or disable it, Settings did not list it, and it
overrode user bindings on that chord. Declare it as
terminal.toggleNativeChat in the keybinding registry, match it through
the user's keybindings, and render its menu labels from the registry.
* fix(keybindings): align native chat toggle admission and execution
* fix(keybindings): preserve runtime input and defer closed menu hints
* test(keybindings): use typed alias read instrumentation
* Fix keybinding dependency direction and physical modifier inputs
* Preserve typed input facts in floating shortcut arbitration
* Clarify native chat shortcut precedence on Windows and Linux
* Simplify native chat shortcut resolution and nominal labels
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(keybindings): report conflicts between terminal shortcuts and chords that fire in terminals
Actions that reach a focused terminal now share a conflict bucket, and the
Shortcuts pane judges the bindings as written in the file instead of the
map the loader already stripped.
* fix(keybindings): flag only the ignored binding and say it was ignored
The other claimant keeps its own default and never stopped working, so a
warning on its row blamed an untouched action. Adds the e2e regression that
seeds a conflicting file inside the isolated E2E home.
* test(keybindings): capture the shortcuts pane at each stage of the conflict report
* test(keybindings): assert the untouched action carries no warning
Visibility alone would still pass if a regression warned on both rows.
* fix(keybindings): narrow the ignored-binding guard and tighten its negative test
Review follow-ups:
- Only warn when the file asked for a binding and the loader took it away.
The guard now also requires the action to be absent from the active map,
so an override that survived is never labelled "was ignored".
- The negative case used view.tasks, which has no default binding, so the
conflict assertion passed without exercising the terminal bucket at all.
It now uses worktree.quickOpen and asserts the definition exists, is not
terminal-reachable, and has a non-empty default list.
* fix(keybindings): compare main-process shortcuts against every scope
- List the actions resolveWindowShortcutAction consumes in main and put them in every scope's conflict bucket, so a binding elsewhere on the same chord is reported instead of silently losing it.
- Browser bindings are compared only against the actions a focused guest forwards ahead of browser chords.
- Dictation stays out, since main lets its chord through while voice is off (the default).
- The two digit ranges may still share a modifier.
- Drop the allowInTerminal terminal-bucket rule.
- Explain overrides the loader drops on a later pass.
- Pin the list to the resolver and the guest order with tests.
* fix: report main-intercepted shortcuts in Settings without dropping saved overrides
Main-process interception now only feeds the Settings conflict report.
The loader, write path and web normalization keep main's scope-only check,
so existing keybindings.json overrides load exactly as before.
---------
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
* fix(sidebar): run previous and next worktree shortcuts from a text field
The worktree list swallowed every key while a text field was focused, so the bound shortcut did nothing in chat, markdown, and the code editor.
* fix(sidebar): keep the default worktree chord with text fields
Previous/next worktree now carries a text-field rule in its keybinding definition. In a text field the default Cmd/Ctrl+Shift+Up/Down keeps its select-to-start/end meaning, while a chord the user bound still switches worktrees from chat, markdown and the code editor. Option and AltGr chords that type a character stay with the field.
* fix(sidebar): let user-bound Option arrow chords leave text fields
On Mac only Option chords that type a character or dead key stay with a focused text field, so a bound Option+Down now switches worktrees from chat, markdown and the code editor.
With a Hangul syllable still composing, macOS reports Ctrl+2 as key 'Process', so
tab.selectByIndex (and every other chord) matched nothing and the press only
committed the syllable. A Cmd/Ctrl chord (or Alt off macOS, minus AltGr) is never
IME text, so fall back to the physical code for 'Process' the way '' and
'Unidentified' already do.
Fixes#28159
* Native chat: Cmd/Ctrl+F opens find in window
Mod+F inside a focused native chat opens a find bar at the chat's top-right
corner. The bar is a separate sandboxed view stacked over the main window, so
its own input is never a match; main runs the window's find-in-page and relays
the match count back. Enter/Shift+Enter step, Escape closes and clears.
Also stops the terminal search bar from opening over a chat that covers a
terminal pane.
* Native chat: Cmd/Ctrl+F finds inside the chat transcript
Replaces the window-level find panel with a find bar inside the chat.
The panel searched the whole window, pasted into the composer, outlived
its chat, and let Escape interrupt the agent.
- Searches only the transcript text the reader can see and paints matches
with CSS highlights, reusing the markdown preview's search, now shared
as lib/dom-text-search-highlights.ts.
- Re-searches as the transcript streams or scrolls, keeping the current
match; a step leaves "follow latest" like a reader scroll.
- Escape closes the bar first and returns focus; paste and editing keys
stay in the bar's input.
- Terminal guard yields any chat.find chord inside a chat cover; a PDF
no longer takes Mod+F from another surface.
* Native chat find: round-2 fixes
- Escape: the chat root's React capture handler closes the bar before the
composer's own capture handler can interrupt the turn.
- One visibility rule for picking and revealing a match: typing no longer
scrolls to a match that is already on screen, and the bar's real rect,
not a full-width band, decides what it covers.
- Reveal scrolls nested scroll boxes (code lines, tool output, thinking);
text cut off by a box that cannot scroll is not counted.
- PDF find opens from its own tab and the file explorer again, but not
from another group, a chat, or a text field.
- Prompt cards no longer take focus from the find input; clicks on the
bar keep typing in it; buttons keep their own Enter.
- A match whose row scrolled away restarts from the first visible match.
- Streamed text growth re-searches every 120 ms instead of every frame,
and searches reuse their Range objects.
- Hover-only row chrome is marked and skipped instead of read by opacity.
- Chat find stays off in the web client, where the browser's find keeps
Mod+F.
- The text matcher moves to lib/text-match-ranges.ts with neutral names.
* Native chat find: round-3 fixes
- Text that held a match re-searches before the next paint: React
rewrites a growing text node whole, collapsing its ranges, so the
streaming paragraph's highlights no longer blink. Other text growth
keeps the 120 ms re-search.
- A disclosure's height animation ending re-searches, so an output
opened while find is open is counted.
- A prompt card that arrived while the find bar held focus gets it when
find closes.
- A reveal that cannot scroll no longer stops following the latest.
- PDF find follows the panel's chord owner (its tab is the active tab
of the focused group, the rule the browser pane uses), so it no longer
takes Mod+F from a chat, terminal or browser in another split, and it
opens again after clicking its own editor header.
* Native chat find: PDF find answers only its own window surface
The floating workspace panel keeps its own focused group, so a PDF that
owns its panel's chords no longer takes Mod+F from a chat in the other
surface (main window vs floating panel), matching how the browser pane
scopes a floating workspace's chords.
Adds a user-assignable shortcut for the existing child-workspace chip action. It stays unassigned by default on macOS, Linux and Windows. Final-source rendered checks cover Settings recording/reset, guards, scroll preservation and restart.
Fixes#24163
Continues mmarabel’s original contribution in this PR. Issue #24163 has no sibling implementation PR. Existing bot findings are fixed, withdrawn or addressed in the PR review.
Co-authored-by: mmarabel <166927047+mmarabel@users.noreply.github.com>
Use the physical digit key for explicitly assigned Mac Option+digit shortcuts while retaining modifier checks.
Co-authored-by: marcuslannister <marcus@lannister.cc>
Co-authored-by: Neil <neil@stably.ai>
Restack the original focused change onto current main, preserving every owned source and test blob and the merged CI contract and journal cleanup fixes.
Original-commit: f707cde14a
fix(shortcuts): defer TUI editing chords in terminal-first mode
Original-commit: 0c6348e49e
docs(shortcuts): describe deferred preview terminal chords
Original-commit: be62c1b6c6
Align worktree history shortcut metadata with terminal conflict policy
Restacked-from: be62c1b6c6
Restacked-onto: f7b1f9d8be
Shortcut labels were rebuilt from scratch in the render body of every
component that shows one, which kept parseKeybinding running ~120x/sec
in a fully idle app.
- Cache the label layer per overrides object (WeakMap), so a keybinding
edit hands out a new object and therefore a fresh cache.
- Memoize parseKeybinding behind a bounded cache; binding strings come
from a fixed definition set plus user overrides.
- Hoist the per-call token/label object literals in normalizeKeyToken
and formatKeyToken to module constants.
Renames seven -helpers modules for the concept their functions operate on, and
splits three that were genuine grab-bags -- each had a clean cleavage along its
importers, which is the signal AGENTS.md describes for a file holding more than
one responsibility.
Leaves keybindings/definitions-core-1..4 alone: definitions.ts spreads them in
order, so their concatenation order is the command palette order and regrouping
them thematically would be a user-visible change. Records that reasoning in a
comment so it is not re-litigated.