fix: keyboard shortcuts on non-English layouts (#6009)

Co-authored-by: sahilkhan09k <181699039+sahilkhan09k@users.noreply.github.com>
Co-authored-by: 04cb <111667698+04cb@users.noreply.github.com>
Co-authored-by: James George <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
Shreyas 2026-03-26 19:06:00 +05:30 committed by GitHub
parent 8aac537c7a
commit ce0fdeccfb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 50 additions and 27 deletions

View file

@ -307,36 +307,64 @@ function generateKeybindingString(ev: KeyboardEvent): ShortcutKey | null {
}
function getPressedKey(ev: KeyboardEvent): Key | null {
// Sometimes the property code is not available on the KeyboardEvent object
const key = (ev.key ?? "").toLowerCase()
const code = ev.code ?? ""
// Check arrow keys
// Use event.code for letters and digits so shortcuts work regardless of
// the active keyboard layout (Cyrillic, CJK, Dvorak, etc). event.key
// returns the character produced by the layout, event.code returns the
// physical key position.
//
// TODO: Several component-level keydown handlers still use event.key
// (spotlight, EnvInput, SchemaSearch, AI modals). Those need the
// same migration but are lower priority since they only check
// arrow/Enter/Escape which are layout-stable.
// Letter keys (KeyAKeyZ)
if (code.startsWith("Key") && code.length === 4) {
return code[3].toLowerCase() as Key
}
// ev.code can be empty in synthetic events or older environments. Fall back
// to ev.key for ASCII letters so shortcuts don't silently stop working.
// This reintroduces layout-dependence for that edge case, but that's better
// than dropping the shortcut entirely.
if (!code && key.length === 1 && key >= "a" && key <= "z") return key as Key
// Arrow keys (ArrowUp → up, etc)
if (key.startsWith("arrow")) {
return key.slice(5) as Key
}
// Check for Tab key
if (key === "tab") return "tab"
// Check for Delete key
if (key === "delete") return "delete"
if (key === "backspace") return "backspace"
// Check letter keys
const isLetter = key.length === 1 && key >= "a" && key <= "z"
if (isLetter) return key as Key
// Shift+/ produces "?" on most layouts but the shortcut is registered as "/"
if (key === "?") return "/"
// Check if number keys
const isDigit = key.length === 1 && key >= "0" && key <= "9"
if (isDigit) return key as Key
// Check if slash, period or enter
// Punctuation and special keys checked before digit codes because some
// layouts produce these characters from physical digit keys (e.g. AZERTY
// produces [ via AltGr+5 which has code "Digit5").
if (key === "/" || key === "." || key === "enter") return key
if (key === "[" || key === "]") return key
// If no other cases match, this is not a valid key
// Digit keys (Digit0Digit9)
if (code.startsWith("Digit") && code.length === 6) {
return code[5] as Key
}
// Numpad digits (Numpad0Numpad9), only when NumLock is on.
// When NumLock is off the physical keys act as navigation (Home, End, etc)
// but event.code still returns Numpad0-Numpad9.
if (
code.startsWith("Numpad") &&
code.length === 7 &&
ev.getModifierState("NumLock")
) {
return code.slice(6) as Key
}
return null
}

View file

@ -277,12 +277,7 @@ async function initApp() {
const isCtrlOrCmd = e.ctrlKey || e.metaKey
let shortcutEvent: string | null = null
if (
isCtrlOrCmd &&
!e.shiftKey &&
!e.altKey &&
e.key.toLowerCase() === "q"
) {
if (isCtrlOrCmd && !e.shiftKey && !e.altKey && e.code === "KeyQ") {
// Ctrl/Cmd + Q - Quit Application
e.preventDefault()
e.stopPropagation()
@ -292,7 +287,7 @@ async function initApp() {
isCtrlOrCmd &&
!e.shiftKey &&
!e.altKey &&
e.key.toLowerCase() === "t"
e.code === "KeyT"
) {
// Ctrl/Cmd + T - New Tab
e.preventDefault()
@ -303,7 +298,7 @@ async function initApp() {
isCtrlOrCmd &&
!e.shiftKey &&
!e.altKey &&
e.key.toLowerCase() === "w"
e.code === "KeyW"
) {
// Ctrl/Cmd + W - Close Tab
e.preventDefault()
@ -314,7 +309,7 @@ async function initApp() {
isCtrlOrCmd &&
e.shiftKey &&
!e.altKey &&
e.key.toLowerCase() === "t"
e.code === "KeyT"
) {
// Ctrl/Cmd + Shift + T - Reopen Tab
e.preventDefault()
@ -347,7 +342,7 @@ async function initApp() {
isCtrlOrCmd &&
!e.shiftKey &&
e.altKey &&
(e.key === "9" || e.code === "Digit9")
e.code === "Digit9"
) {
// Ctrl/Cmd + Alt + 9 - First Tab
e.preventDefault()
@ -358,7 +353,7 @@ async function initApp() {
isCtrlOrCmd &&
!e.shiftKey &&
e.altKey &&
(e.key === "0" || e.code === "Digit0")
e.code === "Digit0"
) {
// Ctrl/Cmd + Alt + 0 - Last Tab
e.preventDefault()