Skip to content

Right-sidebar mode shortcuts do nothing when the key view briefly clears - #8599

Closed
ejc3 wants to merge 1 commit into
manaflow-ai:mainfrom
ejc3:fix/right-sidebar-mode-shortcut-responder
Closed

ejc3 wants to merge 1 commit into
manaflow-ai:mainfrom
ejc3:fix/right-sidebar-mode-shortcut-responder

Conversation

@ejc3

@ejc3 ejc3 commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

What you'd hit

Focus the right sidebar (files, sessions, and so on), then do something that briefly clears the key view. Press a right-sidebar mode shortcut (Ctrl+1..5 by default) during that window and nothing happens, instead of switching the sidebar mode.

Why

shouldRouteRightSidebarModeShortcut decides whether a mode shortcut routes to the sidebar. It bails to the active sidebar intent only when there is no first responder:

guard let responder = window.firstResponder else { return sidebarIntentActive }

But makeFirstResponder(nil) does not leave firstResponder nil. AppKit parks the first responder on the window itself, so window.firstResponder is the window. The guard treats that as a real responder and falls through to the terminal-surface checks, which can't resolve a view from a window and return false, so the shortcut does nothing even though a sidebar intent is active. The nil-responder fallback was added for exactly this "responder temporarily clears" case (#6472), but it keyed on a state AppKit essentially never produces.

Fix

Treat a responder that is the window itself like no responder:

guard let responder = window.firstResponder, responder !== window else { return sidebarIntentActive }

This only changes the one case where the responder is parked on the window and a sidebar intent is active. With no intent the result is unchanged, and a real view responder still takes the terminal path.

Tests

AppDelegateSurfaceShortcutRoutingTests.rightSidebarModeShortcutsDoNotFallThroughWhenResponderTemporarilyClears already exists (it shipped with the fallback in #6472) and is red on main, because the fallback keyed on nil while AppKit parks the responder on the window. Before, the mode shortcut falls through to selectSurfaceByNumber (the recorded issues are three keys across ten cycles). After, all 9 tests in the suite pass.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Fix right-sidebar mode shortcuts (Ctrl+1–5) to still switch modes when the key view briefly clears. When makeFirstResponder(nil) parks focus on the window, treat window.firstResponder === window as no responder and route to the active right-sidebar intent.

Written for commit 7c3659b. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved right-sidebar keyboard shortcut handling when no specific window control is focused.
    • Shortcuts now correctly follow the active right-sidebar mode in this situation.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c04bb6a5-ecac-4503-a54e-a853436ceb50

📥 Commits

Reviewing files that changed from the base of the PR and between da8a58a and 7c3659b.

📒 Files selected for processing (1)
  • Sources/AppDelegate.swift

📝 Walkthrough

Walkthrough

Updated right-sidebar shortcut routing so an NSWindow acting as its own first responder is treated as having no real responder and uses the active sidebar intent.

Changes

Right-sidebar shortcut routing

Layer / File(s) Summary
Handle window-level first responder
Sources/AppDelegate.swift
shouldRouteRightSidebarModeShortcut(in:) now returns sidebarIntentActive when window.firstResponder is the window itself.

Estimated code review effort: 1 (Trivial) | ~3 minutes

Possibly related PRs

Suggested reviewers: austinywang, azooz2003-bit, lawrencecchen

🚥 Pre-merge checks | ✅ 25
✅ Passed checks (25 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the sidebar shortcut fix.
Description check ✅ Passed The description covers the summary, why, fix, and testing, matching the template well enough despite some optional sections being omitted.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed PASS: The change is a small logic tweak inside @MainActor AppDelegate; it adds no new Sendable/shared mutable types or background UI access.
Cmux Swift Blocking Runtime ✅ Passed Patch only changes the right-sidebar responder guard; it adds no waits, sleeps, asyncAfter, sync, locks, or polling.
Cmux Browser Automation Off-Main ✅ Passed PR only changes AppDelegate.swift’s right-sidebar shortcut routing; it adds no browser.* socket commands or worker-lane WebKit/AppKit changes, and the policy allows direct focus routing on main.
Cmux Expensive Synchronous Load ✅ Passed The patch only changes right-sidebar shortcut routing in shouldRouteRightSidebarModeShortcut; it adds no synchronous agent-history/JSON load calls.
Cmux Cache Substitution Correctness ✅ Passed The diff only changes transient shortcut routing in AppDelegate; it does not replace any authoritative persistence/history/snapshot read with a cache or introduce stale/cold-cache trust.
Cmux No Hacky Sleeps ✅ Passed PASS: The PR only changes a Swift responder guard in AppDelegate; no sleeps, timers, polling, or delayed dispatch were added.
Cmux Algorithmic Complexity ✅ Passed The diff only adds a constant-time responder identity check in AppDelegate; no scalable collection scans or repeated filtering/sorting are introduced.
Cmux Swift Concurrency ✅ Passed Diff only tweaks a responder guard in AppDelegate; it adds no new Dispatch/Combine/Task/completion-handler async patterns and doesn't expand existing legacy concurrency.
Cmux Swift @Concurrent ✅ Passed The change only tweaks a synchronous @MainActor boolean guard; no @concurrent, nonisolated async, or heavy async call sites were introduced.
Cmux Swift Package Boundaries ✅ Passed Tiny AppDelegate/AppKit routing tweak around NSWindow firstResponder; the boundary doc explicitly allows app-delegate and UI/glue code.
Cmux Swiftpm Lockfiles ✅ Passed Only Sources/AppDelegate.swift changed; no .gitignore, Package.swift, Package.resolved, or Xcode package-reference files were modified.
Cmux Swift Logging ✅ Passed The diff only changes responder routing comments/logic; no added or modified print/debugPrint/dump/NSLog/Logger appears in the commit diff.
Cmux User-Facing Error Privacy ✅ Passed The change only adjusts shortcut-routing logic and a code comment; it adds no user-facing errors, alerts, or diagnostic text.
Cmux Full Internationalization ✅ Passed The PR only changes shortcut-routing logic and a developer comment; no user-facing text, localized APIs, or locale assets were added or changed.
Cmux Swiftui State Layout ✅ Passed Only an AppKit AppDelegate routing guard changed; no new SwiftUI state, GeometryReader, lazy-row store refs, or render-time state writes were introduced.
Cmux Architecture Rethink ✅ Passed Single guard-clause fix treats NSWindow as no responder; no sleeps, observers, side channels, or split ownership added.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR only updates shortcut routing in AppDelegate; no new/changed NSWindow/NSPanel/WindowGroup or cmux identifier registration, so the rule doesn't apply.
Cmux Source Artifacts ✅ Passed Only Sources/AppDelegate.swift changed, and the diff is a small hand-written logic fix with comments—no generated artifacts or scratch outputs.
Cmux No Test Or Debug Seam In Production Source ✅ Passed Only production routing logic changed in shouldRouteRightSidebarModeShortcut; no new #if DEBUG/test-only member, accessor, or visibility widening was added.
Cmux No Ambient Global State ✅ Passed Only an existing AppDelegate method changed; no new file-scope funcs, globals, namespaces, or singleton/runtime state were added.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ejc3
ejc3 force-pushed the fix/right-sidebar-mode-shortcut-responder branch 3 times, most recently from 5f66bca to 4b35b4b Compare July 22, 2026 10:32
@ejc3
ejc3 marked this pull request as ready for review July 25, 2026 05:39
@greptile-apps

greptile-apps Bot commented Jul 25, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes a routing bug where right-sidebar mode shortcuts (Ctrl+1–5) were silently dropped during any window state that briefly clears the key view. The root cause was shouldRouteRightSidebarModeShortcut treating window.firstResponder === window as a real view responder — the nil-responder fallback from #6472 could never fire because AppKit parks the responder on the window itself rather than setting it to nil.

  • Extends the guard on window.firstResponder with responder !== window, so a window-parked responder is treated identically to no responder and returns sidebarIntentActive immediately — matching the documented AppKit contract for makeFirstResponder(nil).
  • Leaves all other code paths (real view responders, terminal surface checks, Ghostty panel resolution) unchanged; the if sidebarIntentActive, responder is NSWindow { return true } fallback below the guard still handles the distinct case where a different NSWindow (sheet, panel) holds focus.

Confidence Score: 5/5

Single-line guard extension in a keyboard-routing predicate; all other code paths are untouched and the AppKit behavioral contract is correctly modeled.

The change is minimal and precisely targeted: it adds one identity check (responder !== window) to an existing guard, correctly matching AppKit's documented behavior where makeFirstResponder(nil) parks the first responder on the window object rather than clearing it to nil. All other branches of shouldRouteRightSidebarModeShortcut are unchanged and still reachable for their respective cases. The comment explains the AppKit contract, and the PR description confirms a previously-red regression test now passes.

Files Needing Attention: No files require special attention.

Important Files Changed

Filename Overview
Sources/AppDelegate.swift One-line guard fix in shouldRouteRightSidebarModeShortcut: adds responder !== window to correctly handle AppKit's makeFirstResponder(nil) parking behavior; no other paths affected.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A([shouldRouteRightSidebarModeShortcut]) --> B{window == nil?}
    B -- yes --> C([return false])
    B -- no --> D[sidebarIntentActive = activeRightSidebarMode != nil]
    D --> E{firstResponder == nil OR firstResponder === window?}
    E -- yes --> F([return sidebarIntentActive])
    E -- no --> G{isRightSidebarFocusResponder?}
    G -- yes --> H([return true])
    G -- no --> I{sidebarIntentActive && responder is NSWindow?}
    I -- yes --> J([return true])
    I -- no --> K{terminalKeyboardFocusRequest non-nil?}
    K -- yes --> L([return false])
    K -- no --> M{cmuxStrictOwningGhosttyView && terminalSurface.id?}
    M -- no --> N([return false])
    M -- yes --> O([return isRightSidebarDockSurface])
Loading

Reviews (2): Last reviewed commit: "app-delegate: route right-sidebar mode s..." | Re-trigger Greptile

…s parked on the window

shouldRouteRightSidebarModeShortcut treated window.firstResponder === window
(what makeFirstResponder(nil) leaves behind when a responder temporarily
clears) as a real responder and fell through to the terminal-surface checks,
which return false, so a right-sidebar mode shortcut did nothing while an
intent was active. Treat a responder that is the window itself like no
responder and route on the active sidebar intent.
@ejc3
ejc3 force-pushed the fix/right-sidebar-mode-shortcut-responder branch from 4b35b4b to 7c3659b Compare July 25, 2026 06:45
@austinywang

Copy link
Copy Markdown
Contributor

Closing as superseded by #8621.

On the #8599 base, shouldRouteRightSidebarModeShortcut already contains if sidebarIntentActive, responder is NSWindow { return true }. Since responder === window necessarily implies responder is NSWindow, this PR guard returns exactly the same result and cannot produce the claimed red-to-green transition.

Hosted verification confirms the complete AppDelegateSurfaceShortcutRoutingTests suite passes on both revisions:

The branch is conflict-free and all reported checks are green, but merging it would land a semantic no-op under a bug-fix title.

@austinywang austinywang closed this Aug 4, 2026
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.

2 participants