fix: add event.code fallback for Alt+punctuation keys on macOS#64
fix: add event.code fallback for Alt+punctuation keys on macOS#64danyi1212 wants to merge 2 commits intoTanStack:mainfrom
Conversation
On macOS, the Option (Alt) key acts as a character composer for punctuation keys (e.g., Option+- → en-dash '–'), causing event.key to differ from the expected character. This prevented Alt+punctuation hotkeys from matching. Add a PUNCTUATION_CODE_MAP that maps event.code values (Minus, Equal, Slash, BracketLeft, etc.) back to their canonical characters, following the same fallback pattern already used for letter (Key*) and digit (Digit*) codes.
🦋 Changeset detectedLatest commit: bb786bc The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@tanstack/angular-hotkeys
@tanstack/hotkeys
@tanstack/hotkeys-devtools
@tanstack/preact-hotkeys
@tanstack/preact-hotkeys-devtools
@tanstack/react-hotkeys
@tanstack/react-hotkeys-devtools
@tanstack/solid-hotkeys
@tanstack/solid-hotkeys-devtools
@tanstack/svelte-hotkeys
@tanstack/vue-hotkeys
@tanstack/vue-hotkeys-devtools
commit: |
📝 WalkthroughWalkthroughThis pull request introduces support for handling macOS Alt (Option) key punctuation transformations in the Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
.changeset/fix-alt-punctuation-macos.md (1)
7-7: Minor grammar fix in release note wording.Use “an
event.codefallback” for readability.✏️ Suggested edit
-On macOS, the Option (Alt) key acts as a character composer for punctuation keys (e.g., Option+- produces an en-dash '–'), causing `event.key` to differ from the expected character. Added a `event.code` fallback for punctuation keys (Minus, Equal, Slash, BracketLeft, BracketRight, Backslash, Comma, Period, Backquote, Semicolon), matching the existing fallback pattern for letter and digit keys. +On macOS, the Option (Alt) key acts as a character composer for punctuation keys (e.g., Option+- produces an en-dash '–'), causing `event.key` to differ from the expected character. Added an `event.code` fallback for punctuation keys (Minus, Equal, Slash, BracketLeft, BracketRight, Backslash, Comma, Period, Backquote, Semicolon), matching the existing fallback pattern for letter and digit keys.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.changeset/fix-alt-punctuation-macos.md at line 7, Update the release note sentence to use "an `event.code` fallback" instead of "a `event.code` fallback" for correct grammar; locate the sentence mentioning the new fallback for punctuation keys (Minus, Equal, Slash, BracketLeft, BracketRight, Backslash, Comma, Period, Backquote, Semicolon) and replace "a `event.code` fallback" with "an `event.code` fallback".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/hotkeys/src/constants.ts`:
- Around line 312-323: The fallback map PUNCTUATION_CODE_MAP contains a
Semicolon: ';' entry that is inconsistent with the canonical set
PUNCTUATION_KEYS and the PunctuationKey type; pick one consistent fix: either
remove the Semicolon entry from PUNCTUATION_CODE_MAP, or add ';' (and the
corresponding "Semicolon" key) to PUNCTUATION_KEYS and update the PunctuationKey
type so the canonical key set and the fallback map match; update only the
symbols PUNCTUATION_CODE_MAP, PUNCTUATION_KEYS, and PunctuationKey accordingly
to keep them in sync.
In `@packages/hotkeys/tests/match.test.ts`:
- Around line 657-679: Add the missing Semicolon entry to the punctuation test
matrix so the suite title matches coverage: update the cases array in
packages/hotkeys/tests/match.test.ts (the test using createKeyboardEvent and
matchesKeyboardEvent) to include the Semicolon mapping (composed character
produced with Alt/Option + ; on relevant layouts and the code 'Semicolon' with
expectedKey ';'), then ensure the loop still asserts matchesKeyboardEvent(event,
`Alt+;`) is true.
---
Nitpick comments:
In @.changeset/fix-alt-punctuation-macos.md:
- Line 7: Update the release note sentence to use "an `event.code` fallback"
instead of "a `event.code` fallback" for correct grammar; locate the sentence
mentioning the new fallback for punctuation keys (Minus, Equal, Slash,
BracketLeft, BracketRight, Backslash, Comma, Period, Backquote, Semicolon) and
replace "a `event.code` fallback" with "an `event.code` fallback".
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 982e4e76-72f1-4915-89b4-d56e2986b8f3
📒 Files selected for processing (16)
.changeset/fix-alt-punctuation-macos.mddocs/reference/functions/createHotkeyHandler.mddocs/reference/functions/createMultiHotkeyHandler.mddocs/reference/functions/isSingleLetterKey.mddocs/reference/functions/matchesKeyboardEvent.mddocs/reference/functions/normalizeKeyName.mddocs/reference/index.mddocs/reference/interfaces/CreateHotkeyHandlerOptions.mddocs/reference/variables/ALL_KEYS.mddocs/reference/variables/KEY_DISPLAY_SYMBOLS.mddocs/reference/variables/MAC_MODIFIER_SYMBOLS.mddocs/reference/variables/PUNCTUATION_CODE_MAP.mddocs/reference/variables/STANDARD_MODIFIER_LABELS.mdpackages/hotkeys/src/constants.tspackages/hotkeys/src/match.tspackages/hotkeys/tests/match.test.ts
| export const PUNCTUATION_CODE_MAP: Record<string, string> = { | ||
| Minus: '-', | ||
| Equal: '=', | ||
| Slash: '/', | ||
| BracketLeft: '[', | ||
| BracketRight: ']', | ||
| Backslash: '\\', | ||
| Comma: ',', | ||
| Period: '.', | ||
| Backquote: '`', | ||
| Semicolon: ';', | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Verify whether semicolon is part of canonical punctuation types/sets and test coverage.
rg -n -C2 'type PunctuationKey|PUNCTUATION_KEYS|PUNCTUATION_CODE_MAP|Semicolon|Alt\+;|;\x27' \
packages/hotkeys/src \
packages/hotkeys/testsRepository: TanStack/hotkeys
Length of output: 2554
🏁 Script executed:
cat -n packages/hotkeys/src/hotkey.ts | sed -n '117,135p'Repository: TanStack/hotkeys
Length of output: 653
🏁 Script executed:
rg -n 'Semicolon|";"' packages/hotkeys/tests --type ts --type jsRepository: TanStack/hotkeys
Length of output: 42
🏁 Script executed:
sed -n '291,323p' packages/hotkeys/src/constants.tsRepository: TanStack/hotkeys
Length of output: 928
Semicolon entry in fallback map is inconsistent with canonical punctuation key set.
The PUNCTUATION_CODE_MAP includes Semicolon: ';', but ; is not part of PUNCTUATION_KEYS (lines 291-301) or the PunctuationKey type. Either add ; to the canonical punctuation keys/types or remove the Semicolon entry from the map.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/hotkeys/src/constants.ts` around lines 312 - 323, The fallback map
PUNCTUATION_CODE_MAP contains a Semicolon: ';' entry that is inconsistent with
the canonical set PUNCTUATION_KEYS and the PunctuationKey type; pick one
consistent fix: either remove the Semicolon entry from PUNCTUATION_CODE_MAP, or
add ';' (and the corresponding "Semicolon" key) to PUNCTUATION_KEYS and update
the PunctuationKey type so the canonical key set and the fallback map match;
update only the symbols PUNCTUATION_CODE_MAP, PUNCTUATION_KEYS, and
PunctuationKey accordingly to keep them in sync.
| it('should fallback to event.code for all punctuation keys with Alt', () => { | ||
| const cases: Array<[string, string, string]> = [ | ||
| ['–', 'Minus', '-'], // Option+- → en-dash | ||
| ['≠', 'Equal', '='], // Option+= → ≠ | ||
| ['÷', 'Slash', '/'], // Option+/ → ÷ | ||
| ['\u201c', 'BracketLeft', '['], // Option+[ → " | ||
| ['\u2018', 'BracketRight', ']'], // Option+] → ' | ||
| ['«', 'Backslash', '\\'], // Option+\ → « | ||
| ['≤', 'Comma', ','], // Option+, → ≤ | ||
| ['≥', 'Period', '.'], // Option+. → ≥ | ||
| ['`', 'Backquote', '`'], // Option+` → ` (dead key on some layouts) | ||
| ] | ||
|
|
||
| for (const [composedChar, code, expectedKey] of cases) { | ||
| const event = createKeyboardEvent(composedChar, { | ||
| altKey: true, | ||
| code, | ||
| }) | ||
| expect( | ||
| matchesKeyboardEvent(event, `Alt+${expectedKey}` as Hotkey), | ||
| ).toBe(true) | ||
| } | ||
| }) |
There was a problem hiding this comment.
Include Semicolon in the “all punctuation keys” matrix.
The case list is close, but it skips Semicolon, so the suite title currently overstates coverage.
✅ Suggested test addition
const cases: Array<[string, string, string]> = [
['–', 'Minus', '-'], // Option+- → en-dash
['≠', 'Equal', '='], // Option+= → ≠
['÷', 'Slash', '/'], // Option+/ → ÷
['\u201c', 'BracketLeft', '['], // Option+[ → "
['\u2018', 'BracketRight', ']'], // Option+] → '
['«', 'Backslash', '\\'], // Option+\ → «
['≤', 'Comma', ','], // Option+, → ≤
['≥', 'Period', '.'], // Option+. → ≥
+ ['…', 'Semicolon', ';'], // Option+; → …
['`', 'Backquote', '`'], // Option+` → ` (dead key on some layouts)
]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| it('should fallback to event.code for all punctuation keys with Alt', () => { | |
| const cases: Array<[string, string, string]> = [ | |
| ['–', 'Minus', '-'], // Option+- → en-dash | |
| ['≠', 'Equal', '='], // Option+= → ≠ | |
| ['÷', 'Slash', '/'], // Option+/ → ÷ | |
| ['\u201c', 'BracketLeft', '['], // Option+[ → " | |
| ['\u2018', 'BracketRight', ']'], // Option+] → ' | |
| ['«', 'Backslash', '\\'], // Option+\ → « | |
| ['≤', 'Comma', ','], // Option+, → ≤ | |
| ['≥', 'Period', '.'], // Option+. → ≥ | |
| ['`', 'Backquote', '`'], // Option+` → ` (dead key on some layouts) | |
| ] | |
| for (const [composedChar, code, expectedKey] of cases) { | |
| const event = createKeyboardEvent(composedChar, { | |
| altKey: true, | |
| code, | |
| }) | |
| expect( | |
| matchesKeyboardEvent(event, `Alt+${expectedKey}` as Hotkey), | |
| ).toBe(true) | |
| } | |
| }) | |
| it('should fallback to event.code for all punctuation keys with Alt', () => { | |
| const cases: Array<[string, string, string]> = [ | |
| ['–', 'Minus', '-'], // Option+- → en-dash | |
| ['≠', 'Equal', '='], // Option+= → ≠ | |
| ['÷', 'Slash', '/'], // Option+/ → ÷ | |
| ['\u201c', 'BracketLeft', '['], // Option+[ → " | |
| ['\u2018', 'BracketRight', ']'], // Option+] → ' | |
| ['«', 'Backslash', '\\'], // Option+\ → « | |
| ['≤', 'Comma', ','], // Option+, → ≤ | |
| ['≥', 'Period', '.'], // Option+. → ≥ | |
| ['…', 'Semicolon', ';'], // Option+; → … | |
| ['`', 'Backquote', '`'], // Option+` → ` (dead key on some layouts) | |
| ] | |
| for (const [composedChar, code, expectedKey] of cases) { | |
| const event = createKeyboardEvent(composedChar, { | |
| altKey: true, | |
| code, | |
| }) | |
| expect( | |
| matchesKeyboardEvent(event, `Alt+${expectedKey}` as Hotkey), | |
| ).toBe(true) | |
| } | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/hotkeys/tests/match.test.ts` around lines 657 - 679, Add the missing
Semicolon entry to the punctuation test matrix so the suite title matches
coverage: update the cases array in packages/hotkeys/tests/match.test.ts (the
test using createKeyboardEvent and matchesKeyboardEvent) to include the
Semicolon mapping (composed character produced with Alt/Option + ; on relevant
layouts and the code 'Semicolon' with expectedKey ';'), then ensure the loop
still asserts matchesKeyboardEvent(event, `Alt+;`) is true.
Summary
Option+-produces an en-dash–), causingevent.keyto differ from the expected character. This preventedAlt+punctuationhotkeys likeAlt+-,Alt+/,Alt+[, etc. from matching.PUNCTUATION_CODE_MAPinconstants.tsthat mapsevent.codevalues (Minus,Equal,Slash,BracketLeft,BracketRight,Backslash,Comma,Period,Backquote,Semicolon) to their canonical characters, following the same fallback pattern already used for letter (Key*) and digit (Digit*) codes.Alt+[whenevent.keyisDead).Fixes the issue described in the bug report where all
PUNCTUATION_KEYScombined withAlton macOS failed to match.Test plan
Alt+-,Alt+=,Alt+/,Alt+[,Alt+],Alt+\,Alt+,,Alt+., `Alt+``)Mod+Alt+-on macOS)event.codeedge casepnpm testpasses (all targets: eslint, types, lib, docs, build)Summary by CodeRabbit
Release Notes
Bug Fixes
Documentation