Add leader-key (multi-key sequence) shortcuts — foundation (#431)#438
Draft
cloud-on-prem wants to merge 1 commit into
Draft
Add leader-key (multi-key sequence) shortcuts — foundation (#431)#438cloud-on-prem wants to merge 1 commit into
cloud-on-prem wants to merge 1 commit into
Conversation
Add configurable leader-key + multi-key sequence shortcuts that lower to Ghostty native sequence keybinds (leader>k>k=action), as an additive, opt-in layer alongside the existing single-chord shortcutOverrides. Refs supabitapp#431. v1 scope is foundation-only (Swift, no Ghostty source patch): a sequence may target only Ghostty host-routable built-in actions (new/close/goto/move tab, toggle command palette, splits). Menu-only app actions cannot be reached from a Ghostty sequence without a source patch (verified), so they are deferred via the additive LeaderActionTarget seam (.ghostty in v1, .appShortcut later). The which-key discoverability overlay is also deferred to a follow-up. - LeaderKeySequence.swift: model types (SequenceKeyStroke, GhosttyLeaderAction, LeaderActionTarget, LeaderKeySequence, LeaderKeyConfig) - GlobalSettings.leaderKey: additive optional persistence (decodeIfPresent ?? nil, lossy element decode) - zero migration, single chords untouched - AppShortcuts.leaderKeyGhosttyKeybindArguments: lowering + GhosttyCLI.argv wiring; emits a leader>escape=end_key_sequence cancel bind; returns [] when no leader set - LeaderKeyConflictValidator: pure trie-based prefix / duplicate / leader / reserved / single-chord conflict detection, surfaced inline in Settings - SettingsFeature: reducer arms (set/clear leader, add/edit/remove sequence) via @shared(.settingsFile) - Settings UI: leader row + sequence editor + GhosttyLeaderAction-only target picker - Tests: 37 cases (model codec/lossy-drop, lowering strings, validator, reducer TestStore, backward-compat decode) Notes: Ghostty has no sequence timeout (none exists in Ghostty), so unmatched sequences pass through natively rather than timing out. Leader binds are injected at launch and apply after relaunch. Generated with AI Co-Authored-By: rp1 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds configurable leader-key + multi-key sequence shortcuts (e.g.
⌘Kthentn) as an additive, opt-in layer alongside the existing single-chordshortcutOverrides. Closes the foundation for #431.Sequences are lowered to Ghostty's native sequence-keybind engine (
leader>k>k=action) and injected at launch through the existingGhosttyCLI.argv→ghostty_initpath — no new input layer.Scope of this PR (foundation, v1)
This is deliberately a Swift-only foundation PR. While investigating, I confirmed a load-bearing constraint:
So the issue's menu-only app actions (Create/Archive Worktree, Toggle Sidebar, Open PR, Open Repo, Reveal in Finder, Next/Prev Worktree) cannot be bound to a sequence without an out-of-tree Ghostty source patch. Rather than ship that patch in the first PR, this PR builds the entire mechanism and binds the actions that work natively today, with a clean, additive seam for the rest.
In this PR
⌘K, verified free in-app), persisted in a new additiveGlobalSettings.leaderKey.leader>k>k=actionkeybind lowering.leader>escape=end_key_sequencecancel; unmatched sequences pass through to the terminal natively.Bindable actions in v1 (Ghostty host-routable built-ins): New/Close Tab, Jump-to-Worktree-N (
goto_tab:N), Move Tab, Toggle Command Palette, and the five split actions. Menu-only app actions are structurally unrepresentable in the target picker so they can't silently no-op.Deferred (follow-ups)
LeaderActionTargetis an extensible sum type (.ghostty(...)today,.appShortcut(...)later) and the codec lossy-drops unknown kinds, so the follow-up is zero-migration.escapecancel.Implementation
SupacodeSettingsShared/App/LeaderKeySequence.swift(new)SupacodeSettingsShared/Models/GlobalSettings.swiftSupacodeSettingsShared/App/AppShortcuts.swift,supacode/App/supacodeApp.swiftSupacodeSettingsShared/App/LeaderKeyConflictValidator.swift(new)SupacodeSettingsFeature/Reducer/SettingsFeature.swiftsupacode/Features/Settings/Views/KeyboardShortcutsSettingsView.swift,SequenceKeyRecorder.swift(new)supacodeTests/(model codec/lossy, lowering strings, validator, reducer TestStore, backward-compat)Testing
leaderKey == nilfor pre-feature settings), lowering strings (incl. theleader>escape=end_key_sequencecancel), all conflict cases, and the reducer arms viaTestStore.--strict: 0 violations across all changed files.I could not build/run the app or execute the test bundle in my environment:
Frameworks/GhosttyKit.xcframeworkis absent and a from-source zig build fails here with libSystem link errors (environment/toolchain, unrelated to this change). The code is verified byswiftc -typecheck(against real types where possible) + SwiftLint + the authored test suite, but the full-graph compile and runtime behavior have not been exercised. Please run, before merge:make build-appandmake test(executes the 5 new test classes) in a build-capable environment / CIgoto_tab, a split) and confirm it fires while Supacode is focusedless)leaderthenescapecancels cleanly🤖 Generated using: 🎮 rp1.run