TanStack Hotkeys version
v0.8.0 (via @tanstack/react-hotkeys v0.10.0)
Framework/Library version
React 18
Describe the bug and the steps to reproduce it
On a French AZERTY keyboard, the unshifted number row produces accented letters instead of digits for several keys: 2→é, 7→è, 9→ç (the rest, e.g. 1→&, 3→", 4→', produce punctuation). Because of this, useHotkey("Mod+2", callback) never fires — event.key is "é", not "2".
The library already handles this correctly for AZERTY digits that map to punctuation (Mod+1, Mod+3, ...): they fall through to the event.code "Digit" fallback in matchesKeyboardEvent (match.ts). But 2/7/9 map to event.key values that are themselves single Unicode letters (é/è/ç), and the letter-key branch returns early before that fallback is ever reached:
// match.ts
const eventKey = normalizeKeyName(event.key); // "é" -> "É"
const hotkeyKey = parsed.key; // "2"
if (eventKey !== "Dead" && eventKey.length === 1 && hotkeyKey.length === 1) {
if (eventKey.toUpperCase() === hotkeyKey.toUpperCase()) return true;
if (isSingleLetterKey(eventKey) && (/^[A-Za-z]$/.test(eventKey) || !event.altKey)) return false;
// ^ isSingleLetterKey uses /^\p{Letter}$/u, which matches "É" too — this
// returns false and NEVER reaches the event.code "Digit" fallback below.
}
if (event.code && (eventKey === "Dead" || eventKey.length === 1 && hotkeyKey.length === 1)) {
if (event.code.startsWith("Digit")) { /* this never runs for É/È/Ç */ }
...
}
Steps to reproduce:
- Switch the OS keyboard layout to French (AZERTY)
- Register
useHotkey("Mod+2", callback)
- Press Cmd+2 (physical digit-2 key, unshifted)
- Callback never fires —
event.key is "é"
For comparison, useHotkey("Mod+1", callback) works, because event.key on that key is "&" (punctuation, not a Unicode letter), so it skips the early-return branch and correctly falls through to the event.code check.
All AZERTY digit keys whose unshifted glyph is a letter are affected:
| Hotkey |
event.key on AZERTY |
event.code |
Matched? |
Mod+1 |
& |
Digit1 |
Yes |
Mod+2 |
é |
Digit2 |
No |
Mod+3 |
" |
Digit3 |
Yes |
Mod+4 |
' |
Digit4 |
Yes |
Mod+5 |
( |
Digit5 |
Yes |
Mod+6 |
- |
Digit6 |
Yes |
Mod+7 |
è |
Digit7 |
No |
Mod+8 |
_ |
Digit8 |
Yes |
Mod+9 |
ç |
Digit9 |
No |
Mod+0 |
à |
Digit0 |
No |
The fix is likely to make the letter-key early-return only fire when event.code doesn't itself resolve to a Digit*/Key* match — i.e. give the event.code fallback priority whenever it's available, rather than letting the event.key-based letter check short-circuit first. This is the same root cause as #63 (Alt+punctuation on macOS): a modifier can remap the unshifted glyph on a physical key to something that isn't the "expected" character class, and event.key-first matching keeps special-casing one remap (dead keys) without generalizing to others (AZERTY digit-row letters).
Do you intend to try to help solve this bug with your own PR?
None
Terms & Code of Conduct
TanStack Hotkeys version
v0.8.0 (via @tanstack/react-hotkeys v0.10.0)
Framework/Library version
React 18
Describe the bug and the steps to reproduce it
On a French AZERTY keyboard, the unshifted number row produces accented letters instead of digits for several keys:
2→é,7→è,9→ç(the rest, e.g.1→&,3→",4→', produce punctuation). Because of this,useHotkey("Mod+2", callback)never fires —event.keyis"é", not"2".The library already handles this correctly for AZERTY digits that map to punctuation (
Mod+1,Mod+3, ...): they fall through to theevent.code"Digit" fallback inmatchesKeyboardEvent(match.ts). But2/7/9map toevent.keyvalues that are themselves single Unicode letters (é/è/ç), and the letter-key branch returns early before that fallback is ever reached:Steps to reproduce:
useHotkey("Mod+2", callback)event.keyis"é"For comparison,
useHotkey("Mod+1", callback)works, becauseevent.keyon that key is"&"(punctuation, not a Unicode letter), so it skips the early-return branch and correctly falls through to theevent.codecheck.All AZERTY digit keys whose unshifted glyph is a letter are affected:
event.keyon AZERTYevent.codeMod+1&Digit1Mod+2éDigit2Mod+3"Digit3Mod+4'Digit4Mod+5(Digit5Mod+6-Digit6Mod+7èDigit7Mod+8_Digit8Mod+9çDigit9Mod+0àDigit0The fix is likely to make the letter-key early-return only fire when
event.codedoesn't itself resolve to aDigit*/Key*match — i.e. give theevent.codefallback priority whenever it's available, rather than letting theevent.key-based letter check short-circuit first. This is the same root cause as #63 (Alt+punctuation on macOS): a modifier can remap the unshifted glyph on a physical key to something that isn't the "expected" character class, andevent.key-first matching keeps special-casing one remap (dead keys) without generalizing to others (AZERTY digit-row letters).Do you intend to try to help solve this bug with your own PR?
None
Terms & Code of Conduct