| author | |
| committer | |
| log | 71a90ae70e3ff880396363809bd9bfcfd520f30c |
| tree | f6d90ccb957ee376a9cd24797c725666e12ad23c |
| parent | 921cb3c8bc772309ccab4c422b091d0a91497a49 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>4 files changed, 32 insertions(+), 5 deletions(-)
apps/web/src/components/settings/KeybindingsSettings.logic.test.ts+25| ... | ... | @@ -113,6 +113,31 @@ describe("KeybindingsSettings.logic", () => { |
| 113 | 113 | ).toBe("mod+shift+k"); |
| 114 | 114 | }); |
| 115 | 115 | |
| 116 | it.each([ | |
| 117 | ["k", "KeyK", "k"], | |
| 118 | ["Tab", "Tab", "tab"], | |
| 119 | ["F5", "F5", "f5"], | |
| 120 | ])("captures %s without a modifier", (key, code, expected) => { | |
| 121 | const noModifiers = { metaKey: false, ctrlKey: false, altKey: false, shiftKey: false }; | |
| 122 | expect(keybindingFromKeyboardEvent({ key, code, ...noModifiers }, "MacIntel")).toBe(expected); | |
| 123 | }); | |
| 124 | ||
| 125 | it("waits for a key when only a modifier is pressed", () => { | |
| 126 | expect( | |
| 127 | keybindingFromKeyboardEvent( | |
| 128 | { | |
| 129 | key: "Meta", | |
| 130 | code: "MetaLeft", | |
| 131 | metaKey: true, | |
| 132 | ctrlKey: false, | |
| 133 | altKey: false, | |
| 134 | shiftKey: false, | |
| 135 | }, | |
| 136 | "MacIntel", | |
| 137 | ), | |
| 138 | ).toBeNull(); | |
| 139 | }); | |
| 140 | ||
| 116 | 141 | it.each([ |
| 117 | 142 | ["@", "Digit2", "mod+shift+2"], |
| 118 | 143 | ['"', "Digit2", "mod+shift+2"], |
apps/web/src/components/settings/KeybindingsSettings.logic.ts+1-3| ... | ... | @@ -358,6 +358,7 @@ function normalizeShortcutKeyToken(key: string): string | null { |
| 358 | 358 | return null; |
| 359 | 359 | } |
| 360 | 360 | |
| 361 | /** Turns a keydown into a binding such as `mod+shift+k` or `tab`. Null for modifier-only presses. */ | |
| 361 | 362 | export function keybindingFromKeyboardEvent( |
| 362 | 363 | event: Pick<KeyboardEvent, "key" | "code" | "metaKey" | "ctrlKey" | "altKey" | "shiftKey">, |
| 363 | 364 | platform: string, |
| ... | ... | @@ -375,9 +376,6 @@ export function keybindingFromKeyboardEvent( |
| 375 | 376 | } |
| 376 | 377 | if (event.altKey) parts.push("alt"); |
| 377 | 378 | if (event.shiftKey) parts.push("shift"); |
| 378 | if (parts.length === 0) { | |
| 379 | return null; | |
| 380 | } | |
| 381 | 379 | parts.push(keyToken); |
| 382 | 380 | return parts.join("+"); |
| 383 | 381 | } |
apps/web/src/components/settings/KeybindingsSettings.tsx+4-2| ... | ... | @@ -768,7 +768,8 @@ function useKeybindingRowEditor({ |
| 768 | 768 | }; |
| 769 | 769 | |
| 770 | 770 | const captureKeybinding = (event: KeyboardEvent<HTMLInputElement>) => { |
| 771 | if (event.key === "Tab") return; | |
| 771 | // Tab is recorded like any key while recording; after that it moves focus on. | |
| 772 | if (event.key === "Tab" && !isRecording) return; | |
| 772 | 773 | event.preventDefault(); |
| 773 | 774 | if (event.key === "Escape") { |
| 774 | 775 | setDraft({ keyDraft: row.key, isRecording: false }); |
| ... | ... | @@ -1083,7 +1084,8 @@ function useNewKeybindingDraft({ |
| 1083 | 1084 | }; |
| 1084 | 1085 | |
| 1085 | 1086 | const captureKeybinding = (event: KeyboardEvent<HTMLInputElement>) => { |
| 1086 | if (event.key === "Tab") return; | |
| 1087 | // Tab is recorded like any key while recording; after that it moves focus on. | |
| 1088 | if (event.key === "Tab" && !isRecording) return; | |
| 1087 | 1089 | event.preventDefault(); |
| 1088 | 1090 | if (event.key === "Escape") { |
| 1089 | 1091 | setDraft({ keyDraft: "", isRecording: false }); |
apps/web/src/components/settings/useSnapShotShortcutRecorder.tsx+2| ... | ... | @@ -106,6 +106,8 @@ export function useSnapShotShortcutRecorder({ |
| 106 | 106 | } |
| 107 | 107 | return; |
| 108 | 108 | } |
| 109 | // A global shortcut without a modifier would take that key from every app. | |
| 110 | if (!event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey) return; | |
| 109 | 111 | const input = keybindingFromKeyboardEvent(event, navigator.platform); |
| 110 | 112 | if (!input) return; |
| 111 | 113 | const next = parseKeybindingShortcut(input); |