| author | |
| committer | |
| log | 664499c92e838f36a0ff8187bc2cd95d7b8ef9d5 |
| tree | bbc5764f1f50dcf4eb346093a7bf7069e98a70b8 |
| parent | 931e91527c285c49d8482328877c93c8c684591a |
| signature | Signed by PGP key B5690EEEBB952194 |
2 files changed, 37 insertions(+), 2 deletions(-)
apps/web/src/keybindings.test.ts+29| ... | @@ -728,6 +728,35 @@ describe("resolveShortcutCommand", () => { | ... | @@ -728,6 +728,35 @@ describe("resolveShortcutCommand", () => { |
| 728 | "rightPanel.toggle", | 728 | "rightPanel.toggle", |
| 729 | ); | 729 | ); |
| 730 | }); | 730 | }); |
| 731 | |||
| 732 | it("matches non-Latin layout letters using the physical key code", () => { | ||
| 733 | const keybindings = compile([{ shortcut: modShortcut("d"), command: "diff.toggle" }]); | ||
| 734 | |||
| 735 | assert.strictEqual( | ||
| 736 | resolveShortcutCommand(event({ key: "в", code: "KeyD", metaKey: true }), keybindings, { | ||
| 737 | platform: "MacIntel", | ||
| 738 | }), | ||
| 739 | "diff.toggle", | ||
| 740 | ); | ||
| 741 | }); | ||
| 742 | |||
| 743 | it("ignores the physical key code when the layout types a different Latin letter", () => { | ||
| 744 | const keybindings = compile([{ shortcut: modShortcut("d"), command: "diff.toggle" }]); | ||
| 745 | |||
| 746 | // On a remapped layout the physical D key types "a"; only the physical | ||
| 747 | // key whose layout output is "d" may trigger the shortcut. | ||
| 748 | assert.isNull( | ||
| 749 | resolveShortcutCommand(event({ key: "a", code: "KeyD", metaKey: true }), keybindings, { | ||
| 750 | platform: "MacIntel", | ||
| 751 | }), | ||
| 752 | ); | ||
| 753 | assert.strictEqual( | ||
| 754 | resolveShortcutCommand(event({ key: "d", code: "KeyL", metaKey: true }), keybindings, { | ||
| 755 | platform: "MacIntel", | ||
| 756 | }), | ||
| 757 | "diff.toggle", | ||
| 758 | ); | ||
| 759 | }); | ||
| 731 | }); | 760 | }); |
| 732 | 761 | ||
| 733 | describe("formatShortcutLabel", () => { | 762 | describe("formatShortcutLabel", () => { |
apps/web/src/keybindings.ts+8-2| ... | @@ -71,9 +71,15 @@ function normalizeEventKey(key: string): string { | ... | @@ -71,9 +71,15 @@ function normalizeEventKey(key: string): string { |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | function resolveEventKeys(event: ShortcutEventLike): Set<string> { | 73 | function resolveEventKeys(event: ShortcutEventLike): Set<string> { |
| 74 | const keys = new Set([normalizeEventKey(event.key)]); | 74 | const layoutKey = normalizeEventKey(event.key); |
| 75 | const keys = new Set([layoutKey]); | ||
| 76 | // The physical-position fallback exists for layouts that type non-Latin | ||
| 77 | // letters (Cyrillic, Greek) and for Option-modified symbols on macOS. | ||
| 78 | // When the layout already produces a Latin letter, match on it alone; | ||
| 79 | // otherwise a remapped physical key triggers shortcuts for two different | ||
| 80 | // letters at once and shadows system shortcuts on non-QWERTY layouts. | ||
| 75 | const letterCode = event.code?.match(/^Key([A-Z])$/)?.[1]; | 81 | const letterCode = event.code?.match(/^Key([A-Z])$/)?.[1]; |
| 76 | if (letterCode) { | 82 | if (letterCode && !/^[a-z]$/.test(layoutKey)) { |
| 77 | keys.add(letterCode.toLowerCase()); | 83 | keys.add(letterCode.toLowerCase()); |
| 78 | } | 84 | } |
| 79 | const aliases = event.code ? EVENT_CODE_KEY_ALIASES[event.code] : undefined; | 85 | const aliases = event.code ? EVENT_CODE_KEY_ALIASES[event.code] : undefined; |