Repository navigation
Support tmux-style bare-key chord leaders - #3311
robinjoseph08 wants to merge 16 commits into
Conversation
Spec for letting users bind tmux-style bare-key leaders (e.g. backtick) as chord prefixes, with implicit double-tap-to-send-literal.
The early-return guard in handleCustomShortcut bails on bare-key events when no chord is armed, which prevents a tmux-style bare-key leader (e.g. backtick) from ever arming. Narrow the guard to skip only when no configured shortcut has a bare-key chord prefix. Fixes the failing testBareKeyChordPrefixArmsAndSplitsOnSecondKey.
When a bare-key chord prefix is armed and the user presses the same key again with no modifiers and no configured chord binding matched, forward the prefix character to the focused Ghostty surface. This matches tmux's default send-prefix behavior, requires no configuration, and lets users with a bare-key leader still type the literal character by double-tapping.
Assert first responder identity after makeFirstResponder so a focus redirect produces a clear failure instead of a confusing empty captured array. Drop unused manager binding.
Regression test: when a user binds `<prefix><prefix>` to an action (e.g. splitRight), the configured-chord dispatch fires and the implicit literal-send must not run. Verifies the existing dispatch order in handleCustomShortcut where matchConfiguredShortcut runs before the double-tap-literal block.
StoredShortcut.parseConfig(strokes:) was rejecting bare-key first strokes unconditionally. Allow them when the binding is a two-stroke chord so that settings.json entries like `"splitRight": ["\`", "d"]` are parsed and dispatched correctly. Adds testSettingsFileBareKeyChordDispatchesSplitRight which verifies the full parser→file-store→routing path for a bare-key chord leader.
Add chordsRuleBareKey and chordsRuleDoubleTap translation keys to both en.json and ja.json, wire them into the keyboard-shortcuts docs page, and add an equivalent prose paragraph to the configuration docs page.
Recompute only when configured shortcuts change instead of allocating arrays and JSON-decoding UserDefaults on every keystroke. Addresses the typing-latency-sensitive paths policy in CLAUDE.md.
The previous test only asserted both events were consumed; a future refactor that consumes the chord without dispatching would have silently slipped through. Mirror testPerformSplitShortcutSplitsFocusedTerminalSurfaceWhenSelectedWorkspaceIsStale to assert the actual panel-count change.
|
@robinjoseph08 is attempting to deploy a commit to the Manaflow Team on Vercel. A member of the Team first needs to authorize it. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
✅ Files skipped from review due to trivial changes (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughImplements bare-key chord leader support for keyboard shortcuts. AppDelegate detects bare-key chord prefixes, prevents early event discarding, and introduces an implicit Changes
Sequence DiagramsequenceDiagram
actor User
participant AppDelegate as AppDelegate<br/>(handleCustomShortcut)
participant TerminalSurface as TerminalSurface<br/>(focused)
User->>AppDelegate: Press bare-key leader (e.g., backtick)
AppDelegate->>AppDelegate: Detect bare-key leader configured<br/>Arm chord state
AppDelegate->>AppDelegate: Return (consume event)
User->>AppDelegate: Press same bare-key again<br/>(no explicit chord binding)
AppDelegate->>AppDelegate: Check if armed + same key<br/>No explicit binding match
AppDelegate->>TerminalSurface: sendText(leader character)
AppDelegate->>AppDelegate: Return (consume event)
TerminalSurface-->>User: Display literal character
User->>AppDelegate: Press bare-key leader<br/>then different key
AppDelegate->>AppDelegate: Detect bare-key leader<br/>Arm chord state
AppDelegate->>AppDelegate: Next key detected<br/>Lookup explicit chord binding
alt Binding Found
AppDelegate->>AppDelegate: Dispatch bound action
else No Binding
AppDelegate->>TerminalSurface: sendText(leader)
AppDelegate->>AppDelegate: Pass second key through
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
Greptile SummaryThis PR adds tmux-style bare-key chord leaders (e.g.
Confidence Score: 3/5Safe to merge for settings.json users; socket-configured bare-key chords are broken until a follow-up lands. One P1 defect: bare-key chord prefixes added via socket commands silently fail because the cache is only invalidated on settings.json changes. The settings.json path (the primary user-facing scenario) works correctly and is well-tested. The socket path breakage is new behavior introduced by this PR, not a pre-existing limitation. Sources/AppDelegate.swift — specifically the Important Files Changed
Sequence DiagramsequenceDiagram
participant U as User
participant H as handleCustomShortcut
participant C as hasConfiguredBareKeyChordPrefixCache
participant A as armConfiguredShortcutChordIfNeeded
participant M as matchConfiguredShortcut
participant S as sendLiteralChordPrefixToFocusedSurface
participant T as TerminalSurface
U->>H: keyDown "`" (no modifiers)
H->>C: hasConfiguredBareKeyChordPrefix()
C-->>H: true (bare-key chord configured)
H->>A: armConfiguredShortcutChordIfNeeded(event)
A-->>H: prefix "`" armed
H-->>U: return true (event consumed)
U->>H: keyDown "d" (chord second stroke)
H->>M: matchConfiguredShortcut(.splitRight)
M-->>H: match, execute splitRight
H-->>U: return true (action dispatched)
Note over U,T: Double-tap path
U->>H: keyDown "`" (prefix armed, no match)
H->>M: matchConfiguredShortcut (all actions)
M-->>H: no match
H->>S: sendLiteralChordPrefixToFocusedSurface(prefix, event)
S->>T: sendText("`")
S-->>H: true
H-->>U: return true (literal forwarded)
Reviews (1): Last reviewed commit: "Assert splitRight side effect in setting..." | Re-trigger Greptile |
| hasConfiguredBareKeyChordPrefixCache = recomputeHasConfiguredBareKeyChordPrefix() | ||
| } | ||
|
|
||
| private func recomputeHasConfiguredBareKeyChordPrefix() -> Bool { | ||
| let context = preferredRegisteredMainWindowContext() | ||
| let configuredShortcuts = configuredCmuxShortcutActions(for: context) | ||
| .compactMap(\.shortcut) | ||
| for action in configuredShortcutChordActions { | ||
| let shortcut = KeyboardShortcutSettings.shortcut(for: action) | ||
| guard shortcut.hasChord else { continue } | ||
| if shortcut.firstStroke.modifierFlags.isEmpty { | ||
| return true | ||
| } | ||
| } | ||
| for shortcut in configuredShortcuts { | ||
| guard shortcut.hasChord else { continue } | ||
| if shortcut.firstStroke.modifierFlags.isEmpty { | ||
| return true | ||
| } | ||
| } | ||
| return false |
There was a problem hiding this comment.
Cache not invalidated for socket-configured bare-key chord prefixes
recomputeHasConfiguredBareKeyChordPrefix() is only called from refreshConfiguredShortcutChordActions(), which fires on KeyboardShortcutSettings.didChangeNotification (i.e., settings.json changes). If a bare-key chord leader is added via a socket command (cmuxConfigStore.loadedActions), hasConfiguredBareKeyChordPrefixCache stays false. Every bare-key event will still hit the early-return guard in handleCustomShortcut and return false, silently discarding the prefix event and making the socket-configured bare-key chord non-functional until the next settings.json write triggers a refresh.
The PR description acknowledges this but frames it as a pre-existing pattern; however, the observable breakage (bare-key chord configured via socket does not arm) is new behavior introduced here, not pre-existing.
| } | ||
| """.write(to: settingsFileURL, atomically: true, encoding: .utf8) | ||
|
|
||
| KeyboardShortcutSettings.settingsFileStore = KeyboardShortcutSettingsFileStore( | ||
| primaryPath: settingsFileURL.path, | ||
| fallbackPath: nil, | ||
| startWatching: false | ||
| ) | ||
|
|
||
| window.makeKeyAndOrderFront(nil) | ||
| RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.05)) | ||
|
|
||
| guard let terminalView = surfaceView(in: focusedPanel.hostedView) else { | ||
| XCTFail("Expected a GhosttyNSView inside the focused panel's hosted view") | ||
| return |
There was a problem hiding this comment.
Test name implies split fires, but no panel-count assertion
testBareKeyChordPrefixArmsAndSplitsOnSecondKey only asserts that both events return true (consumed). The "splits" claim in the test name — and the assertion message "Second stroke after a bare-key chord prefix must dispatch the bound action" — is not actually verified; there is no XCTAssertEqual(workspace.panels.count, initialCount + 1, …). If the routing consumes the event without dispatching splitRight, this test still passes. testSettingsFileBareKeyChordDispatchesSplitRight (which does check panel count) covers the end-to-end path, but for the withTemporaryShortcut path the assertion gap means regression coverage is weaker than the name suggests.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
web/messages/en.json (1)
441-441: Double-tap tmux send-prefix docs look consistent; optionally mention explicit chord precedence.Line 441 matches the intended “consume first keystroke, send literal on second” behavior. Since the implementation also ensures explicit
<leader><leader>chord bindings take precedence over implicit behavior, consider adding a short clause like “If you explicitly bind the leader+leader chord, that configured binding wins.”🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/messages/en.json` at line 441, Update the "chordsRuleDoubleTap" message to note that an explicit leader+leader chord binding takes precedence over the implicit double-tap send behavior; edit the value for the "chordsRuleDoubleTap" key to append a short clause such as "If you explicitly bind the leader+leader chord, that configured binding wins" so users know explicit chord bindings override the default double-tap behavior.Sources/AppDelegate.swift (1)
5287-5300: Send the actual second-tap text, not the normalized shortcut token.Line 5299 forwards
prefix.key, which is the stored shortcut representation. That can differ from what the user actually typed on the second tap (for example with Caps Lock or layout-dependent bare keys), so<leader><leader>can send the wrong character to the terminal. Preferevent.characters, withprefix.keyonly as a fallback.Possible fix
private func sendLiteralChordPrefixToFocusedSurface( prefix: ShortcutStroke, event: NSEvent ) -> Bool { @@ guard let ghosttyView = cmuxOwningGhosttyView(for: responder), let surface = ghosttyView.terminalSurface else { return false } - surface.sendText(prefix.key) + let literal = + (event.characters?.isEmpty == false ? event.characters : nil) + ?? (event.charactersIgnoringModifiers?.isEmpty == false ? event.charactersIgnoringModifiers : nil) + ?? prefix.key + surface.sendText(literal) return true }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/AppDelegate.swift` around lines 5287 - 5300, The sendLiteralChordPrefixToFocusedSurface function currently forwards prefix.key to the terminal which is the stored shortcut token; change the send to use the actual characters from the NSEvent (use event.characters, falling back to prefix.key if nil/empty) so the real second-tap text (respecting Caps Lock/layout) is delivered to surface.sendText; keep the existing ghosttyView and surface lookup (cmuxOwningGhosttyView, terminalSurface) and only replace the argument passed to surface.sendText.cmuxTests/AppDelegateShortcutRoutingTests.swift (2)
972-1047: Explicit-binding precedence test should also prove the action firedRight now this only proves “no literal was sent.” It doesn’t prove the explicit
` + `chord actually dispatchedsplitRight. Add a panel-count assertion so this cannot pass on unrelated consumption paths.Suggested tightening
- withTemporaryShortcut(action: .splitRight, shortcut: shortcut) { + let initialPanelCount = workspace.panels.count + withTemporaryShortcut(action: .splitRight, shortcut: shortcut) { @@ `#if` DEBUG XCTAssertTrue(appDelegate.debugHandleCustomShortcut(event: prefixEvent)) XCTAssertTrue( appDelegate.debugHandleCustomShortcut(event: secondEvent), "Configured `+` chord must dispatch the action" ) XCTAssertEqual(captured, [], "Explicit binding must suppress the implicit literal-send") `#else` XCTFail("debugHandleCustomShortcut is only available in DEBUG") `#endif` } + RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.05)) + XCTAssertEqual( + workspace.panels.count, + initialPanelCount + 1, + "Explicit `+` binding should execute splitRight" + )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmuxTests/AppDelegateShortcutRoutingTests.swift` around lines 972 - 1047, The test currently only asserts no literal was sent but doesn't verify the explicit .splitRight action ran; capture the workspace's panel count (or another reliable indicator of a split) before invoking the chord in testBareKeyChordDoubleTapWithExplicitBindingFiresActionInsteadOfLiteral and assert that the panel count increased (or the expected split result exists) after the two backtick events inside the withTemporaryShortcut block (use the same workspace/selectedWorkspace and focusedPanel references and the .splitRight action) so the test proves the action was dispatched rather than just suppressing literal-send.
771-833: Assert the split side effect, not only event consumptionThis test can pass even if
splitRightdidn’t run, because it only checksdebugHandleCustomShortcut(...)booleans and then discardsmanageron Line 832. Please assert panel-count delta to prove the action executed.Suggested tightening
- withTemporaryShortcut(action: .splitRight, shortcut: shortcut) { + let initialPanelCount = manager.selectedWorkspace?.panels.count ?? 0 + withTemporaryShortcut(action: .splitRight, shortcut: shortcut) { guard let prefixEvent = makeKeyDownEvent( key: "`", modifiers: [], keyCode: 50, windowNumber: window.windowNumber @@ `#if` DEBUG XCTAssertTrue( appDelegate.debugHandleCustomShortcut(event: prefixEvent), "Bare-key chord prefix must be consumed so the terminal does not receive `" ) XCTAssertTrue( appDelegate.debugHandleCustomShortcut(event: actionEvent), "Second stroke after a bare-key chord prefix must dispatch the bound action" ) `#else` XCTFail("debugHandleCustomShortcut is only available in DEBUG") `#endif` } - _ = manager + RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.05)) + XCTAssertEqual( + manager.selectedWorkspace?.panels.count, + initialPanelCount + 1, + "Bare-key chord should perform splitRight on second stroke" + )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmuxTests/AppDelegateShortcutRoutingTests.swift` around lines 771 - 833, The test currently only asserts that appDelegate.debugHandleCustomShortcut returned true but never verifies the split side-effect; capture the panel/tab count from the TabManager (manager) before firing the prefix/action events and assert the expected delta after the events to prove splitRight executed (e.g., record let beforeCount = manager.panelCount or similar accessor, then after handling both events assert manager.panelCount == beforeCount + 1); keep using the existing test function testBareKeyChordPrefixArmsAndSplitsOnSecondKey and the existing withTemporaryShortcut/appDelegate.debugHandleCustomShortcut flow but replace the discarded reference to manager with an actual before/after assertion to validate the split action occurred.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/superpowers/plans/2026-04-29-bare-key-chord-leader.md`:
- Around line 19-21: Update the plan text to reflect the actual shipped parser
change: replace the claim that "No parser change needed" with a short note that
StoredShortcut.parseConfig(strokes:) now treats bare-key first strokes in
multi-stroke chords differently (see StoredShortcut.parseConfig(strokes:) and
ShortcutStroke.parseConfig(_:)), and mention that the parser recognizes
backtick/grave tokens as before; also adjust the paragraph about the recorder
(KeyboardShortcutSettings.swift) to clarify that bareKeyNotAllowed still blocks
single-stroke recording but does not prevent bare-key first strokes when parsed
from settings.json. Ensure the plan references the functions
StoredShortcut.parseConfig(strokes:), ShortcutStroke.parseConfig(_:), and the
bareKeyNotAllowed rejection behavior so readers can locate the implemented
behavior.
In `@Sources/AppDelegate.swift`:
- Around line 9979-10000: The cache hasConfiguredBareKeyChordPrefixCache is
being recomputed only against preferredRegisteredMainWindowContext() which
misses bare-key leaders in other live MainWindowContext instances; update
recomputeHasConfiguredBareKeyChordPrefix() to iterate all live registered
MainWindowContext instances and their cmuxConfigStore-backed
configuredCmuxShortcutActions and configuredShortcutChordActions (instead of
using preferredRegisteredMainWindowContext()), checking each
shortcut.firstStroke.modifierFlags.isEmpty for chord prefixes, and return true
if any match; also ensure the cache is invalidated/recomputed when any
MainWindowContext or its cmuxConfigStore changes so window-scoped config updates
correctly refresh the cache.
---
Nitpick comments:
In `@cmuxTests/AppDelegateShortcutRoutingTests.swift`:
- Around line 972-1047: The test currently only asserts no literal was sent but
doesn't verify the explicit .splitRight action ran; capture the workspace's
panel count (or another reliable indicator of a split) before invoking the chord
in testBareKeyChordDoubleTapWithExplicitBindingFiresActionInsteadOfLiteral and
assert that the panel count increased (or the expected split result exists)
after the two backtick events inside the withTemporaryShortcut block (use the
same workspace/selectedWorkspace and focusedPanel references and the .splitRight
action) so the test proves the action was dispatched rather than just
suppressing literal-send.
- Around line 771-833: The test currently only asserts that
appDelegate.debugHandleCustomShortcut returned true but never verifies the split
side-effect; capture the panel/tab count from the TabManager (manager) before
firing the prefix/action events and assert the expected delta after the events
to prove splitRight executed (e.g., record let beforeCount = manager.panelCount
or similar accessor, then after handling both events assert manager.panelCount
== beforeCount + 1); keep using the existing test function
testBareKeyChordPrefixArmsAndSplitsOnSecondKey and the existing
withTemporaryShortcut/appDelegate.debugHandleCustomShortcut flow but replace the
discarded reference to manager with an actual before/after assertion to validate
the split action occurred.
In `@Sources/AppDelegate.swift`:
- Around line 5287-5300: The sendLiteralChordPrefixToFocusedSurface function
currently forwards prefix.key to the terminal which is the stored shortcut
token; change the send to use the actual characters from the NSEvent (use
event.characters, falling back to prefix.key if nil/empty) so the real
second-tap text (respecting Caps Lock/layout) is delivered to surface.sendText;
keep the existing ghosttyView and surface lookup (cmuxOwningGhosttyView,
terminalSurface) and only replace the argument passed to surface.sendText.
In `@web/messages/en.json`:
- Line 441: Update the "chordsRuleDoubleTap" message to note that an explicit
leader+leader chord binding takes precedence over the implicit double-tap send
behavior; edit the value for the "chordsRuleDoubleTap" key to append a short
clause such as "If you explicitly bind the leader+leader chord, that configured
binding wins" so users know explicit chord bindings override the default
double-tap behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b180a0fd-daa5-4b88-a593-1b8a09b3512d
📒 Files selected for processing (10)
Sources/AppDelegate.swiftSources/GhosttyTerminalView.swiftSources/KeyboardShortcutSettings.swiftcmuxTests/AppDelegateShortcutRoutingTests.swiftdocs/superpowers/plans/2026-04-29-bare-key-chord-leader.mddocs/superpowers/specs/2026-04-29-bare-key-chord-leader-design.mdweb/app/[locale]/docs/configuration/page.tsxweb/app/[locale]/docs/keyboard-shortcuts/page.tsxweb/messages/en.jsonweb/messages/ja.json
- Recompute hasConfiguredBareKeyChordPrefix across all live MainWindowContext instances on every call instead of caching. Caching only inspected preferredRegisteredMainWindowContext(), silently missing any window whose cmuxConfigStore had bare-key chords but wasn't the preferred context at refresh time. The no-cache approach eliminates the need for per-context Combine subscriptions (and their teardown on context removal): shortcutActions() filters a typically-single-digit loadedActions list; in the common no-bare-key case the built-in loop short-circuits on the first mismatch and returns false quickly. - Forward event.characters (with charactersIgnoringModifiers / prefix.key fallback) instead of the normalized prefix.key so Caps Lock and layout-dependent leaders deliver the actually-typed character. - chordsRuleDoubleTap mentions that an explicit <leader><leader> user binding takes precedence over the implicit literal-send behavior.
Add focused-surface setup and panel-count assertions to testBareKeyChordPrefixArmsAndSplitsOnSecondKey and testBareKeyChordDoubleTapWithExplicitBindingFiresActionInsteadOfLiteral, mirroring the pattern already used by testSettingsFileBareKeyChordDispatchesSplitRight.
Summary
Enables tmux-style bare-key chord leaders (e.g.
`thend) configured via~/.config/cmux/settings.json. Pressing the leader twice in a row sends one literal copy of the character to the focused terminal, mimicking tmux's defaultsend-prefixbehavior. Single-stroke bare-key bindings remain rejected.Builds on chord support added in #2528.
{ "shortcuts": { "bindings": { "splitRight": ["`", "d"], "focusLeft": ["`", "h"], "focusRight": ["`", "l"], "focusUp": ["`", "k"], "focusDown": ["`", "j"], "toggleSplitZoom": ["`", "z"] } } }Partially addresses #1450 (tmux-style keybindings inside cmux) and #1711 (Space as a bindable key — usable now as a chord first stroke; single-stroke
spacestill rejected).What changed
Sources/AppDelegate.swift— narrowed the bare-key early-return guard inhandleCustomShortcutso bare-key keyDown events reach the chord-arming code path when at least one configured shortcut has a bare-key chord prefix. Added a cachedhasConfiguredBareKeyChordPrefix()flag (recomputed only when shortcuts change) so the typing-latency-sensitive path stays allocation-free. AddedsendLiteralChordPrefixToFocusedSurfaceand the implicit<leader><leader>dispatch at the tail ofhandleCustomShortcut(after everymatchConfiguredShortcutso explicit user bindings always win).Sources/KeyboardShortcutSettings.swift— relaxedStoredShortcut.parseConfig(strokes:)to accept bare-key first strokes only when the shortcut is a chord (single-stroke bare keys remain rejected). Original spec assumed the parser already accepted bare keys; it didn't.Sources/GhosttyTerminalView.swift—#if DEBUG-gated test seam inTerminalSurface.sendText(zero release cost).Tests
5 new tests in
cmuxTests/AppDelegateShortcutRoutingTests.swift, plus all 7 existing chord regression tests still pass:testBareKeyChordPrefixArmsAndSplitsOnSecondKeytestBareKeyChordMismatchDoesNotConsumeSecondKey— locks in Q1=a (eat prefix, pass second key on mismatch)testBareKeyChordDoubleTapSendsLiteralToFocusedTerminaltestBareKeyChordDoubleTapWithExplicitBindingFiresActionInsteadOfLiteraltestSettingsFileBareKeyChordDispatchesSplitRight— end-to-end through the file store, assertssplitRightactually executes (panel count + 1)Test plan
cmux-unitsuite: 13 chord-related tests pass viaxcodebuild ... -scheme cmux-unit ... test["","d"]-style bindings — split / focus / zoom / double-tap-literal / mismatch-passthrough all behave as expectedSpec/plan
docs/superpowers/specs/2026-04-29-bare-key-chord-leader-design.mddocs/superpowers/plans/2026-04-29-bare-key-chord-leader.mdKnown follow-ups (not in this PR)
KeyboardShortcutSettings.didChangeNotification(covers settings.json — the user-facing path). It does NOT yet refresh oncmuxConfigStore.loadedActionschanges (socket-configured custom shortcuts). Pre-existing pattern; separate follow-up.resizeLeft/Right/Up/Down) — explicitly out of scope per the spec.shift+5instead of%). Pre-existing in Support chorded keyboard shortcuts #2528; not in this PR.Summary by cubic
Adds tmux-style bare-key chord leaders (e.g.
then d) configurable via~/.config/cmux/settings.json`. Double-tap the leader to send a single literal to the focused terminal; single-stroke bare keys are still rejected. Partially addresses #1450 and #1711.New Features
settings.json(e.g.["","d"]).<leader><leader>sends the literal leader unless that exact chord is explicitly bound; usesevent.charactersso layout and Caps Lock are respected.<leader><leader>precedence.Bug Fixes
Written for commit 96fafe9. Summary will update on new commits. Review in cubic
Summary by CodeRabbit
New Features
Documentation