From cd61eca88fca6adcb049cd005b012c31453e589a Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Fri, 27 Mar 2026 17:13:29 -0700 Subject: [PATCH 1/2] feat: default useKey to true for layout-independent hotkeys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change useKey default from false to true in parseHotkey and Options type - Normalize space key (' ' → 'space') in useKey matching path - Update tests to reflect new default With useKey defaulting to true, hotkeys match the produced character (event.key) instead of the physical key code (event.code). This makes hotkeys work correctly on non-QWERTY layouts (Dvorak, AZERTY, Colemak) without requiring callers to pass useKey: true on every call. Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 3 +++ .../src/lib/parseHotkeys.ts | 2 +- packages/react-hotkeys-hook/src/lib/types.ts | 2 +- .../react-hotkeys-hook/src/lib/validators.ts | 4 ++- .../src/test/useHotkeys.test.tsx | 25 ++++++++++--------- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index e9bf164a..18ee6e68 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,6 @@ typings/ # next.js build output .next + +# Riptide artifacts (cloud-synced) +.humanlayer/tasks/ diff --git a/packages/react-hotkeys-hook/src/lib/parseHotkeys.ts b/packages/react-hotkeys-hook/src/lib/parseHotkeys.ts index 14153116..66943bd8 100644 --- a/packages/react-hotkeys-hook/src/lib/parseHotkeys.ts +++ b/packages/react-hotkeys-hook/src/lib/parseHotkeys.ts @@ -37,7 +37,7 @@ export function parseHotkey( hotkey: string, splitKey = '+', sequenceSplitKey = '>', - useKey = false, + useKey = true, description?: string, metadata?: Record, ): Hotkey { diff --git a/packages/react-hotkeys-hook/src/lib/types.ts b/packages/react-hotkeys-hook/src/lib/types.ts index abfbddfa..e6e58b5f 100644 --- a/packages/react-hotkeys-hook/src/lib/types.ts +++ b/packages/react-hotkeys-hook/src/lib/types.ts @@ -81,7 +81,7 @@ export type Options = { ignoreModifiers?: boolean // Pass through event listener options. (Default: undefined) eventListenerOptions?: EventListenerOptions - // Listen to the produced key instead of the code. (Default: false) + // Listen to the produced key instead of the code. (Default: true) useKey?: boolean // The timeout to wait for the next key to be pressed. (Default: 1000ms) sequenceTimeoutMs?: number diff --git a/packages/react-hotkeys-hook/src/lib/validators.ts b/packages/react-hotkeys-hook/src/lib/validators.ts index 02f5c65c..bee7e473 100644 --- a/packages/react-hotkeys-hook/src/lib/validators.ts +++ b/packages/react-hotkeys-hook/src/lib/validators.ts @@ -127,8 +127,10 @@ export const isHotkeyMatchingKeyboardEvent = (e: KeyboardEvent, hotkey: Hotkey, // If useKey is set, match against the produced key value instead of the key code // When useKey is true, we ONLY match produced keys — never fall through to code-based matching if (useKey) { + // Normalize produced key: map ' ' to 'space' so hotkey string 'space' works with useKey + const normalizedKey = producedKey === ' ' ? 'space' : producedKey.toLowerCase() if (keys?.length === 1) { - return keys.includes(producedKey.toLowerCase()) + return keys.includes(normalizedKey) } if (keys && keys.length > 0) { return isHotkeyPressed(keys.map((k) => k.toLowerCase())) diff --git a/packages/react-hotkeys-hook/src/test/useHotkeys.test.tsx b/packages/react-hotkeys-hook/src/test/useHotkeys.test.tsx index ad61c020..e938e26a 100644 --- a/packages/react-hotkeys-hook/src/test/useHotkeys.test.tsx +++ b/packages/react-hotkeys-hook/src/test/useHotkeys.test.tsx @@ -376,7 +376,7 @@ test('should listen to multiple combinations with modifiers', async () => { alt: true, meta: false, mod: false, - useKey: false, + useKey: true, isSequence: false, hotkey: 'alt+b', }) @@ -1055,7 +1055,8 @@ test('should listen to function keys f1-f16', async () => { const user = userEvent.setup() const callback = vi.fn() - renderHook(() => useHotkeys('f1, f16', callback)) + // Function keys are physical keys — use code-based matching + renderHook(() => useHotkeys('f1, f16', callback, { useKey: false })) await user.keyboard('[F1]') await user.keyboard('[F16]') @@ -1128,7 +1129,7 @@ test('should pass keyboard event and hotkey object to callback', async () => { alt: false, meta: false, mod: false, - useKey: false, + useKey: true, isSequence: false, hotkey: 'a', }) @@ -1150,7 +1151,7 @@ test('should set shift to true in hotkey object if listening to shift', async () alt: false, meta: false, mod: false, - useKey: false, + useKey: true, isSequence: false, hotkey: 'shift+a', }) @@ -1172,7 +1173,7 @@ test('should set ctrl to true in hotkey object if listening to ctrl', async () = alt: false, meta: false, mod: false, - useKey: false, + useKey: true, isSequence: false, hotkey: 'ctrl+a', }) @@ -1194,7 +1195,7 @@ test('should set alt to true in hotkey object if listening to alt', async () => alt: true, meta: false, mod: false, - useKey: false, + useKey: true, isSequence: false, hotkey: 'alt+a', }) @@ -1216,7 +1217,7 @@ test('should set mod to true in hotkey object if listening to mod', async () => alt: false, meta: false, mod: true, - useKey: false, + useKey: true, isSequence: false, hotkey: 'mod+a', }) @@ -1238,7 +1239,7 @@ test('should set meta to true in hotkey object if listening to meta', async () = alt: false, meta: true, mod: false, - useKey: false, + useKey: true, isSequence: false, hotkey: 'meta+a', }) @@ -1260,7 +1261,7 @@ test('should set multiple modifiers to true in hotkey object if listening to mul ctrl: false, meta: false, mod: true, - useKey: false, + useKey: true, isSequence: false, hotkey: 'mod+shift+a', }) @@ -1362,7 +1363,7 @@ test('should call preventDefault option function with hotkey and keyboard event' ctrl: false, meta: false, mod: false, - useKey: false, + useKey: true, isSequence: false, hotkey: 'a', }) @@ -1597,11 +1598,11 @@ test('Should listen to produced key and not to code', async () => { expect(callback).toHaveBeenCalledTimes(1) }) -test('Should not check produced key if useKey is not set', async () => { +test('Should not check produced key if useKey is false', async () => { const user = userEvent.setup() const callback = vi.fn() - renderHook(() => useHotkeys(`=`, callback)) + renderHook(() => useHotkeys(`=`, callback, { useKey: false })) await user.keyboard(`=`) From 51c4a67c3b873bfc363049fe27d109562c0cc716 Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Sat, 28 Mar 2026 00:15:38 +0000 Subject: [PATCH 2/2] chore: bump @humanlayer/react-hotkeys-hook to 5.3.0 Co-Authored-By: Claude Sonnet 4.6 --- package.json | 2 +- packages/react-hotkeys-hook/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index bc83dd80..c28f2e1b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@humanlayer/react-hotkeys-hook", "description": "React hook for handling keyboard shortcuts (HumanLayer fork)", - "version": "5.2.6", + "version": "5.3.0", "sideEffects": false, "repository": { "type": "git", diff --git a/packages/react-hotkeys-hook/package.json b/packages/react-hotkeys-hook/package.json index cdad55c5..d0350706 100644 --- a/packages/react-hotkeys-hook/package.json +++ b/packages/react-hotkeys-hook/package.json @@ -1,6 +1,6 @@ { "name": "react-hotkeys-hook", - "version": "5.2.6", + "version": "5.3.0", "type": "module", "scripts": { "dev": "vite",