* 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.