Skip to content

Feat/respect keyboard layout in code fallback#53

Merged
KevinVandy merged 4 commits intoTanStack:mainfrom
dmontagu:feat/respect-keyboard-layout-in-code-fallback
Mar 7, 2026
Merged

Feat/respect keyboard layout in code fallback#53
KevinVandy merged 4 commits intoTanStack:mainfrom
dmontagu:feat/respect-keyboard-layout-in-code-fallback

Conversation

@dmontagu
Copy link
Contributor

@dmontagu dmontagu commented Mar 7, 2026

🎯 Changes

Fixes #17

The matchesKeyboardEvent function falls back to event.code (physical key position) when event.key doesn't match the registered hotkey. This fallback exists to handle cases where modifier keys cause the OS to report a special character instead of the expected letter (e.g., macOS Option+T producing instead of T).

However, the fallback currently activates even when event.key is a standard ASCII letter — which means it matches based on physical key position rather than the keyboard layout's logical mapping. This breaks all non-QWERTY keyboard layouts (Dvorak, Colemak, AZERTY, etc.):

Example on Dvorak (macOS with "Use keyboard layout for shortcuts" enabled):

  1. User presses Cmd + physical B key → OS reports event.key='x', event.code='KeyB'
  2. A hotkey registered as Mod+B checks: 'x' === 'b'? No.
  3. Fallback kicks in: event.code = 'KeyB''B' === 'B'? Yes → hotkey fires incorrectly
  4. The user's Cmd+X (cut) is hijacked by the sidebar toggle bound to Mod+B

The fix: When event.key is already a standard ASCII letter (a-z), trust the keyboard layout and return false immediately. The event.code fallback is still used when event.key produces a non-letter character (e.g., , ´, dead keys).

(To be clear, this "example" was a real issue in our Pydantic Logfire app, and is precisely how I noticed this bug, as I am a Dvorak user myself. I have applied the diff from this PR in our own frontend build via pnpm patches, and the problem is now fixed for me.)

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

dmontagu added 2 commits March 6, 2026 16:57
…youts

The `matchesKeyboardEvent` function falls back to `event.code` (physical
key position) when `event.key` doesn't match the hotkey. This fallback
exists to handle cases like macOS Option+T producing '†' instead of 'T'.

However, when `event.key` is already a standard ASCII letter, this
fallback incorrectly matches based on physical key position, breaking
all non-QWERTY keyboard layouts (Dvorak, Colemak, AZERTY, etc.).

For example, on Dvorak with macOS "Use keyboard layout for shortcuts":
- Pressing Cmd + physical B key produces event.key='x', event.code='KeyB'
- A hotkey registered as 'Mod+B' would incorrectly match via the code
  fallback, even though the user pressed Cmd+X in their layout

The fix: when event.key is a standard ASCII letter (a-z), trust the
keyboard layout mapping and skip the event.code fallback. The fallback
is still used when event.key is a non-letter character (like '†') or
a dead key.

Fixes TanStack#17
@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 7, 2026

Open in StackBlitz

@tanstack/hotkeys

npm i https://pkg.pr.new/@tanstack/hotkeys@53

@tanstack/hotkeys-devtools

npm i https://pkg.pr.new/@tanstack/hotkeys-devtools@53

@tanstack/preact-hotkeys

npm i https://pkg.pr.new/@tanstack/preact-hotkeys@53

@tanstack/preact-hotkeys-devtools

npm i https://pkg.pr.new/@tanstack/preact-hotkeys-devtools@53

@tanstack/react-hotkeys

npm i https://pkg.pr.new/@tanstack/react-hotkeys@53

@tanstack/react-hotkeys-devtools

npm i https://pkg.pr.new/@tanstack/react-hotkeys-devtools@53

@tanstack/solid-hotkeys

npm i https://pkg.pr.new/@tanstack/solid-hotkeys@53

@tanstack/solid-hotkeys-devtools

npm i https://pkg.pr.new/@tanstack/solid-hotkeys-devtools@53

commit: b81eb21

@KevinVandy KevinVandy force-pushed the feat/respect-keyboard-layout-in-code-fallback branch from df20bf1 to 467f93c Compare March 7, 2026 22:09
@KevinVandy
Copy link
Member

@dmontagu I think I was able to solve non-ascii too by using regex /^\p{Letter}$/. Your tests still pass.

@KevinVandy KevinVandy merged commit 029f473 into TanStack:main Mar 7, 2026
3 checks passed
@github-actions github-actions bot mentioned this pull request Mar 7, 2026
@dmontagu
Copy link
Contributor Author

dmontagu commented Mar 9, 2026

@KevinVandy I can see why you might like this behavior if you are typically typing custom characters, but I believe this change may be too broad and may have broken a number of reasonable keyboard shortcuts involving the option key on MacOS. The characters in the tables below all match \p{Letter}, meaning use of these keyboard shortcuts would be broken by the change here.

I can see how if you meant to type these characters it might be annoying, but my suspicion is that it will be more common to want to use some of these combinations as a shortcut than it will be for them to be intentionally typed. I'm not sure where to draw the line but you might want to be aware of this, in particular I would expect some issues to be reported as a result of this change given it will break so many previously-working shortcuts.

I think in the case of an ASCII letter being typed it's less controversial to not fall back to key code, as basically the only way to get an ASCII letter as the key but not the key code is to have a difference of keyboard layout. But for the characters, I think the probably they were intended to be typed when there is a compatible keyboard shortcut is fairly low, and handling this properly is perhaps better handled for most users by disabling global hotkeys (or at least the expanded set of letters?) when entering a textbox through some API for managing the hotkeys, rather than making these unusable.

Note that the below key combinations are on QWERTY, they will be register differently on other keyboard layouts (as it is based on the layout-determined letter, not the physical key, but the physical key won't change), so it may be hard to just whitelist based on key combinations.

I would suggest if you want to be conservative to go with my original suggestion of just skipping the fallback on ASCII letters to have less impact on existing hotkeys.

  ┌──────┬───────┐                                                                
  │ Char │  Key  │                                                                                                                                                                                                                                      
  ├──────┼───────┤                                                                
  │ æ    │ opt+' │                                                                                                                                                                                                                                      
  ├──────┼───────┤                                                                                                                                                                                                                                      
  │ π    │ opt+p │                                                                                                                                                                                                                                      
  ├──────┼───────┤
  │ å    │ opt+a │
  ├──────┼───────┤
  │ ø    │ opt+o │
  ├──────┼───────┤
  │ ˆ    │ opt+i │
  ├──────┼───────┤
  │ œ    │ opt+q │
  ├──────┼───────┤
  │ ƒ    │ opt+f │
  ├──────┼───────┤
  │ ç    │ opt+c │
  ├──────┼───────┤
  │ ß    │ opt+s │
  ├──────┼───────┤
  │ µ    │ opt+m │
  └──────┴───────┘

  ┌──────┬─────────────┐
  │ Char │     Key     │
  ├──────┼─────────────┤
  │ Æ    │ opt+shift+' │
  ├──────┼─────────────┤
  │ Á    │ opt+shift+y │
  ├──────┼─────────────┤
  │ Ï    │ opt+shift+f │
  ├──────┼─────────────┤
  │ Ç    │ opt+shift+c │
  ├──────┼─────────────┤
  │ Ò    │ opt+shift+l │
  ├──────┼─────────────┤
  │ Å    │ opt+shift+a │
  ├──────┼─────────────┤
  │ Ø    │ opt+shift+o │
  ├──────┼─────────────┤
  │ ˆ    │ opt+shift+i │
  ├──────┼─────────────┤
  │ Î    │ opt+shift+d │
  ├──────┼─────────────┤
  │ Ó    │ opt+shift+h │
  ├──────┼─────────────┤
  │ Í    │ opt+shift+s │
  ├──────┼─────────────┤
  │ Ú    │ opt+shift+; │
  ├──────┼─────────────┤
  │ Œ    │ opt+shift+q │
  ├──────┼─────────────┤
  │ Ô    │ opt+shift+j │
  ├──────┼─────────────┤
  │ ı    │ opt+shift+b │
  ├──────┼─────────────┤
  │ Â    │ opt+shift+m │
  └──────┴─────────────┘

@KevinVandy
Copy link
Member

Hmm, ok, we may need to patch again then

@KevinVandy
Copy link
Member

Would you want to follow up with another pr?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hotkey registering both for the key and code fields for normal characters

2 participants