fix(keymap): match remaining letter shortcuts by typed character (#292)

#291 named letter keys by the typed character (e.key) when Alt is not held, so AZERTY Ctrl+Z is no longer Ctrl+W. Hard-coded handlers still compared the physical KeyboardEvent.code. A shared letterOf(e) in platform/keymap.ts now backs strokeOf and those handlers:

- terminal Ctrl+Shift+C / V / T (copy, paste, new tab) and Ctrl+letter control codes
- Run/Debug Configurations Ctrl/Cmd+N
- browser shortcut guards (blocked reload/print etc.)

Alt combos keep the physical key (Option / AltGr type symbols).
Цей коміт міститься в:
l0ng-ai authored and GitHub committed 2026-10-29 23:05:26 +08:00
1 джерело d1a597898d
коміт aeec0e6759
8 files changed
+64 -18

No files matched your search

+14 -4
Переглянути файл
@@ -253,15 +253,25 @@ const CODE_NAMES: Record<string, string> = {
NumpadDivide: "NumpadDivide",
};
/**
* The uppercase ASCII letter of a key event: the character it types when Alt
* is not held (on AZERTY the Z key sits on the physical KeyW, so Ctrl+Z must
* not read as Ctrl+W), or the physical `KeyX` when Alt is held (⌥ on macOS and
* AltGr may type a symbol) or `e.key` is not an ASCII letter.
*/
export function letterOf(e: Pick<KeyboardEvent, "code" | "key"> & { altKey?: boolean }): string | null {
if (/^[a-zA-Z]$/.test(e.key) && !e.altKey) return e.key.toUpperCase();
if (/^Key[A-Z]$/.test(e.code)) return e.code.slice(3);
return null;
}
/** Converts a keydown event into a canonical stroke, or null for bare modifiers. */
export function strokeOf(
e: Pick<KeyboardEvent, "code" | "key" | "metaKey" | "ctrlKey" | "altKey" | "shiftKey">,
): string | null {
let key: string | undefined;
// A letter is named by the character it types: on AZERTY the Z key sits on the physical KeyW,
// so Ctrl+Z must not read as Ctrl+W. Alt combos may type a symbol (⌥ on macOS, AltGr), so they keep the physical key.
if (/^[a-zA-Z]$/.test(e.key) && !e.altKey) key = e.key.toUpperCase();
else if (/^Key[A-Z]$/.test(e.code)) key = e.code.slice(3);
const letter = letterOf(e);
if (letter) key = letter;
else if (/^Digit\d$/.test(e.code)) key = e.code.slice(5);
else if (/^Numpad\d$/.test(e.code)) key = e.code.slice(6);
else if (/^F\d{1,2}$/.test(e.code)) key = e.code;