From fbb7d8f74e171ab124f116f192ccbfbd313044d7 Mon Sep 17 00:00:00 2001 From: anthhub Date: Fri, 27 Mar 2026 12:25:54 +0800 Subject: [PATCH] fix: prevent Cmd+Shift+U escape sequence leak in kitty protocol mode (#2198) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the kitty keyboard protocol is active, charactersIgnoringModifiers may carry a synthetic value that does not match the shortcut key. The previous code checked this guard before attempting the keyCode-based layout translation, causing matchShortcut to return false for letter shortcuts (like Cmd+Shift+U) even though the keyCode and modifiers were correct. The event then fell through to the terminal, producing the escape sequence [12629;10u. Fix by moving the layout-character match (shortcutLayoutCharacterProvider) before the early-return guard. The keyCode→layout translation reliably identifies the physical key regardless of keyboard protocol mode. The guard is preserved after both character sources have been tried, so the cross-layout collision protection for letter shortcuts remains intact. Co-Authored-By: Claude Sonnet 4.6 --- Sources/AppDelegate.swift | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index cd623863cb13..c6d4a87b0d28 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -10671,24 +10671,15 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent return true } - // For command-based shortcuts, trust AppKit's layout-aware characters when present. - // Keep this strict for letter shortcuts to avoid physical-key collisions across layouts, - // while still allowing keyCode fallback for digit/punctuation shortcuts on non-US layouts. - // When a non-Latin input source is active (Russian, Korean, Chinese, Japanese, etc.), - // charactersIgnoringModifiers returns non-ASCII characters that can never match - // a Latin shortcut key — skip this guard and fall through to layout-based matching. let hasEventChars = !(eventCharsIgnoringModifiers?.isEmpty ?? true) let eventCharsAreASCII = eventCharsIgnoringModifiers?.allSatisfy(\.isASCII) ?? true - if hasEventChars, - eventCharsAreASCII, - flags.contains(.command), - !flags.contains(.control), - shouldRequireCharacterMatchForCommandShortcut(shortcutKey: shortcutKey) { - return false - } // Match using the current keyboard layout so Command shortcuts stay character-based // across layouts (QWERTY, Dvorak, etc.) instead of being tied to ANSI physical keys. + // This also provides a reliable fallback when the kitty keyboard protocol is active: + // in that mode, charactersIgnoringModifiers may be empty or carry a synthetic value + // that does not match the shortcut key, but the keyCode-based layout translation + // still correctly identifies the physical key (e.g. keyCode 32 → "u"). let layoutCharacter = shortcutLayoutCharacterProvider(event.keyCode, event.modifierFlags) if shortcutCharacterMatches( eventCharacter: layoutCharacter, @@ -10699,6 +10690,18 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent return true } + // For command-based shortcuts, once both character sources have been tried, block + // further keyCode fallback for letter shortcuts to avoid physical-key collisions + // across layouts. Non-ASCII characters (Russian, Korean, CJK, etc.) cannot match + // a Latin shortcut key, so always allow keyCode fallback in that case. + if hasEventChars, + eventCharsAreASCII, + flags.contains(.command), + !flags.contains(.control), + shouldRequireCharacterMatchForCommandShortcut(shortcutKey: shortcutKey) { + return false + } + // Control-key combos can surface as ASCII control characters (e.g. Ctrl+H => backspace), // so keep ANSI keyCode fallback for control-modified shortcuts. Also allow fallback for // command punctuation shortcuts, since some non-US layouts report different characters @@ -10709,7 +10712,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent // physical key code is the definitive identifier for the intended shortcut. // For empty-character events (synthetic/browser key equivalents), preserve the original // behavior: only fall back when the layout translation also failed. - let hasUsableEventChars = hasEventChars && eventCharsAreASCII let allowANSIKeyCodeFallback = flags.contains(.control) || (flags.contains(.command) && !flags.contains(.control)