mac: only the focused composer claims ⌘B/⌘I (#481)
Key equivalents walk the view hierarchy, not the responder chain — and every opened space's pane stays mounted (hidden at opacity 0), each with its own composer. The first composer in the tree swallowed ⌘B/⌘I and formatted a hidden draft, so the shortcuts only worked on the top space. Guard on being the window's first responder; everyone else passes the key along.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2 files changed,
+15
−0
ios/LChatMac/Info.plist+6 −0ios/LChatMac/MacComposerField.swift+9 −0
diff --git a/ios/LChatMac/Info.plist b/ios/LChatMac/Info.plist| index 7ccfc01..ea3fc0c 100644 |
| --- a/ios/LChatMac/Info.plist |
| +++ b/ios/LChatMac/Info.plist |
| @@ -38,8 +38,14 @@ |
| <key>NSAllowsLocalNetworking</key> |
| <true/> |
| </dict> |
| + <key>NSUserActivityTypes</key> |
| + <array> |
| + <string>INSendMessageIntent</string> |
| + </array> |
| <key>SUEnableAutomaticChecks</key> |
| <true/> |
| + <key>SUEnableInstallerLauncherService</key> |
| + <true/> |
| <key>SUFeedURL</key> |
| <string>https://github.com/elevenmessenger/releases/releases/latest/download/appcast.xml</string> |
| <key>SUPublicEDKey</key> |
diff --git a/ios/LChatMac/MacComposerField.swift b/ios/LChatMac/MacComposerField.swift| index e309ff3..dc52e24 100644 |
| --- a/ios/LChatMac/MacComposerField.swift |
| +++ b/ios/LChatMac/MacComposerField.swift |
| @@ -250,6 +250,15 @@ final class ComposerTextView: NSTextView { |
| // ⌘B/⌘I toggle traits; ⌘U is swallowed (no underline marker — see the |
| // representable's doc comment). Everything else keeps its meaning. |
| override func performKeyEquivalent(with event: NSEvent) -> Bool { |
| + // Key equivalents walk the VIEW hierarchy, not the responder chain — |
| + // and RootView keeps every opened space's pane mounted (hidden at |
| + // opacity 0), each with its own composer. Without the focus guard the |
| + // FIRST composer in the tree swallows ⌘B/⌘I and formats a hidden |
| + // draft, so the shortcut only ever "worked" on the top space. Only the |
| + // focused field may claim the key; everyone else passes it along. |
| + guard window?.firstResponder === self else { |
| + return super.performKeyEquivalent(with: event) |
| + } |
| if event.modifierFlags.intersection(.deviceIndependentFlagsMask) == .command, |
| let key = event.charactersIgnoringModifiers?.lowercased() { |
| switch key { |