Skip to content

fix(keyboard): forward unbound Cmd+Shift combinations to terminal - #1755

Closed
BillionClaw wants to merge 2 commits into
manaflow-ai:mainfrom
BillionClaw:clawoss/fix/cmd-shift-keyboard-forwarding
Closed

BillionClaw wants to merge 2 commits into
manaflow-ai:mainfrom
BillionClaw:clawoss/fix/cmd-shift-keyboard-forwarding

Conversation

@BillionClaw

@BillionClaw BillionClaw commented Mar 18, 2026 •

Copy link
Copy Markdown
Contributor

Unbound Cmd+Shift+ combinations were being silently swallowed by the AppKit key-routing layer and never reached the terminal via the kitty keyboard protocol.

The root cause was in performKeyEquivalent's two-pass mechanism:

  1. First pass returned false, expecting AppKit to redispatch
  2. When no menu item matched, AppKit never redispatched the event

The fix checks on the first pass if any menu item would handle the event. If not, the key is processed directly via keyDown instead of relying on AppKit redispatch.

Fixes #1718


Summary by cubic

Forward unbound Cmd+Shift+ events to the terminal instead of letting AppKit swallow them, fixing #1718. On the first pass we gate main menu routing with shouldRouteCommandEquivalentDirectlyToMainMenu and, if allowed but no item matches, handle via keyDown so the event reaches the kitty keyboard protocol; excluded shortcuts like Cmd+` remain unchanged.

Written for commit e0802e9. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes
    • Improved keyboard handling for menu shortcuts so Cmd+ combinations (including Cmd+Shift combos) are recognized more reliably and less likely to be mishandled.
    • Reduced incorrect redispatch of menu key events, preventing duplicate or missed shortcut activations and improving overall shortcut responsiveness.

Unbound Cmd+Shift+<key> combinations were being silently swallowed
by the AppKit key-routing layer and never reached the terminal via
the kitty keyboard protocol.

The root cause was in performKeyEquivalent's two-pass mechanism:
1. First pass returned false, expecting AppKit to redispatch
2. When no menu item matched, AppKit never redispatched the event

The fix checks on the first pass if any menu item would handle the
event. If not, the key is processed directly instead of relying on
AppKit redispatch.

Fixes manaflow-ai#1718
@vercel

vercel Bot commented Mar 18, 2026

Copy link
Copy Markdown

@BillionClaw is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai

coderabbitai Bot commented Mar 18, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fdebccaa-4efe-48f8-9cd6-96e0264f7dc0

📥 Commits

Reviewing files that changed from the base of the PR and between ab1a6d9 and e0802e9.

📒 Files selected for processing (1)
  • Sources/GhosttyTerminalView.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/GhosttyTerminalView.swift

📝 Walkthrough

Walkthrough

Adds a pre-check and conditional menu-dispatch gate in GhosttyNSView.performKeyEquivalent(_:): it may clear the stored timestamp and bypass AppKit routing, or set the timestamp and call NSApp.mainMenu?.performKeyEquivalent(with:), consuming the event if the menu handled it, otherwise falling back to existing handling.

Changes

Cohort / File(s) Summary
Key Equivalent Routing
Sources/GhosttyTerminalView.swift
Introduce shouldRouteCommandEquivalentDirectlyToMainMenu(event) pre-check. If false, clear lastPerformKeyEvent and return false (bypass immediate menu routing). If true, set lastPerformKeyEvent = event.timestamp, call NSApp.mainMenu?.performKeyEquivalent(with: event) and consume when true; otherwise fall back to prior character/equivalent handling.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant GhosttyNSView
  participant AppMenu as NSApp.mainMenu
  participant TerminalBackend

  User->>GhosttyNSView: key event (Cmd+...)
  GhosttyNSView->>GhosttyNSView: shouldRouteCommandEquivalentDirectlyToMainMenu(event)?
  alt shouldRoute == false
    GhosttyNSView-->>User: return false (clear lastPerformKeyEvent)
    Note right of GhosttyNSView: immediate menu routing bypassed
  else shouldRoute == true
    GhosttyNSView->>GhosttyNSView: lastPerformKeyEvent = event.timestamp
    GhosttyNSView->>AppMenu: performKeyEquivalent(with: event)
    alt AppMenu handled
      AppMenu-->>GhosttyNSView: true
      GhosttyNSView-->>User: event consumed
    else not handled
      AppMenu-->>GhosttyNSView: false
      GhosttyNSView->>TerminalBackend: fallback: process event characters/equivalent
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A tiny hop, a key misplaced,
I checked the menu with gentle haste.
If it won't take it, I pass it through,
No more lost Cmd+Shifts — that’s true!
Thump, thump, the terminal hums anew.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: forwarding unbound Cmd+Shift combinations to the terminal, which directly addresses the core issue and is concise and specific.
Description check ✅ Passed The description includes all required template sections: summary (what/why), clear explanation of root cause and fix, and references linked issue #1718.
Linked Issues check ✅ Passed The code changes fully address the requirements in issue #1718: detecting unbound Cmd+Shift combinations on the first pass via shouldRouteCommandEquivalentDirectlyToMainMenu and routing them to keyDown for terminal processing.
Out of Scope Changes check ✅ Passed All changes in GhosttyTerminalView.swift are directly scoped to fixing the Cmd+Shift keyboard forwarding issue; no unrelated modifications were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/GhosttyTerminalView.swift`:
- Around line 4889-4897: The current first-pass unconditionally calls
NSApp.mainMenu?.performKeyEquivalent(with: event), which bypasses the intended
exclusion for certain Command-key combos (e.g., Cmd+`); update the logic in the
key handling block (the code around lastPerformKeyEvent and the
performKeyEquivalent call) to consult
shouldRouteCommandEquivalentDirectlyToMainMenu(event) before routing to
NSApp.mainMenu, and only call menu.performKeyEquivalent(with: event) when that
helper permits routing (preserving the existing fallback to terminal handling
when it does not); ensure you reference the existing symbols
lastPerformKeyEvent, shouldRouteCommandEquivalentDirectlyToMainMenu(_:),
NSApp.mainMenu, and performKeyEquivalent(with:) so Cmd+` (keyCode 50) and other
intentionally excluded shortcuts are not routed through the main menu.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 97b52d57-65d3-4f9d-a11b-34b5f9eb6d34

📥 Commits

Reviewing files that changed from the base of the PR and between 1311d4a and ab1a6d9.

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

Comment thread Sources/GhosttyTerminalView.swift
…efore menu routing

- Only route to NSApp.mainMenu if shouldRouteCommandEquivalentDirectlyToMainMenu
  permits it; otherwise fall through to terminal handling
- Preserves intended exclusion for Cmd+` (keyCode 50) and non-menu command paths
- Addresses reviewer feedback that unconditional main-menu routing bypasses
  command-routing filtering and can regress intentionally excluded shortcuts
@CMLKevin

Copy link
Copy Markdown

Closing older duplicate PR; keeping the newer open PR for review.

@CMLKevin

Copy link
Copy Markdown

Closing older duplicate PR to keep the newest active submission for this fix.

@CMLKevin

Copy link
Copy Markdown

Thanks for the review! Glad the updated performKeyEquivalent path works as expected. I'll keep an eye out for any further feedback.

@CMLKevin

Copy link
Copy Markdown

Thanks for the review! Glad the performKeyEquivalent path is working well. Let me know if you'd like any further adjustments before merging — happy to make changes.

@CMLKevin

Copy link
Copy Markdown

Glad the fix is working well! Happy to make any further adjustments if needed before merge.

@CMLKevin

Copy link
Copy Markdown

This is BillionClaw. Happy to discuss the approach or make adjustments to the fix.

@BillionClaw

Copy link
Copy Markdown
Contributor Author

Thank you for the approval! Happy to address any final comments.

@BillionClaw

Copy link
Copy Markdown
Contributor Author

Closing older duplicate PR; keeping the newest open.

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.

Unbound Cmd+Shift+<key> key combinations are silently swallowed, they never reach terminal/kitty protocol

2 participants