Skip to content

fix: add event.code fallback for Alt+punctuation keys on macOS#64

Open
danyi1212 wants to merge 2 commits intoTanStack:mainfrom
danyi1212:feat-fix-alt-punctuation-macos
Open

fix: add event.code fallback for Alt+punctuation keys on macOS#64
danyi1212 wants to merge 2 commits intoTanStack:mainfrom
danyi1212:feat-fix-alt-punctuation-macos

Conversation

@danyi1212
Copy link

@danyi1212 danyi1212 commented Mar 12, 2026

Summary

  • 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. This prevented Alt+punctuation hotkeys like Alt+-, Alt+/, Alt+[, etc. from matching.
  • Added a PUNCTUATION_CODE_MAP in constants.ts that maps event.code values (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.
  • This also fixes dead-key + punctuation code matching (e.g., Alt+[ when event.key is Dead).

Fixes the issue described in the bug report where all PUNCTUATION_KEYS combined with Alt on macOS failed to match.

Test plan

  • Added tests for all punctuation keys with Alt modifier (Alt+-, Alt+=, Alt+/, Alt+[, Alt+], Alt+\, Alt+,, Alt+., `Alt+``)
  • Added test for multi-modifier combinations (Mod+Alt+- on macOS)
  • Added test for direct punctuation matching without fallback (no regression)
  • Added test for missing event.code edge case
  • Added test for dead key + punctuation code fallback
  • Updated existing dead-key test to expect correct behavior
  • pnpm test passes (all targets: eslint, types, lib, docs, build)

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Fixed macOS Alt (Option) key combinations with punctuation keys (e.g., Option+Minus, Option+Slash) not triggering hotkeys correctly. The system now properly recognizes the intended punctuation key regardless of how modifier combinations transform the character.
  • Documentation

    • Updated reference documentation to reflect improvements to punctuation key handling and fallback behavior across platforms.

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-bot
Copy link

changeset-bot bot commented Mar 12, 2026

🦋 Changeset detected

Latest commit: bb786bc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@tanstack/hotkeys Patch
@tanstack/angular-hotkeys Patch
@tanstack/hotkeys-devtools Patch
@tanstack/preact-hotkeys Patch
@tanstack/react-hotkeys Patch
@tanstack/solid-hotkeys Patch
@tanstack/svelte-hotkeys Patch
@tanstack/vue-hotkeys Patch
@tanstack/preact-hotkeys-devtools Patch
@tanstack/react-hotkeys-devtools Patch
@tanstack/solid-hotkeys-devtools Patch
@tanstack/vue-hotkeys-devtools Patch

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

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 15, 2026

Open in StackBlitz

@tanstack/angular-hotkeys

npm i https://pkg.pr.new/@tanstack/angular-hotkeys@64

@tanstack/hotkeys

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

@tanstack/hotkeys-devtools

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

@tanstack/preact-hotkeys

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

@tanstack/preact-hotkeys-devtools

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

@tanstack/react-hotkeys

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

@tanstack/react-hotkeys-devtools

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

@tanstack/solid-hotkeys

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

@tanstack/solid-hotkeys-devtools

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

@tanstack/svelte-hotkeys

npm i https://pkg.pr.new/@tanstack/svelte-hotkeys@64

@tanstack/vue-hotkeys

npm i https://pkg.pr.new/@tanstack/vue-hotkeys@64

@tanstack/vue-hotkeys-devtools

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

commit: bb786bc

@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

📝 Walkthrough

Walkthrough

This pull request introduces support for handling macOS Alt (Option) key punctuation transformations in the @tanstack/hotkeys library. A new constant PUNCTUATION_CODE_MAP maps physical keyboard codes to canonical punctuation characters, enabling fallback matching when Alt key compositions alter event.key values. The matching logic, constants, tests, and documentation are updated accordingly.

Changes

Cohort / File(s) Summary
Changelog Entry
.changeset/fix-alt-punctuation-macos.md
Documents patch release fix for macOS Alt/Option key punctuation handling via event.code fallback for punctuation keys.
Documentation Reference Updates
docs/reference/functions/createHotkeyHandler.md, docs/reference/functions/createMultiHotkeyHandler.md, docs/reference/functions/isSingleLetterKey.md, docs/reference/functions/normalizeKeyName.md, docs/reference/interfaces/CreateHotkeyHandlerOptions.md, docs/reference/variables/ALL_KEYS.md, docs/reference/variables/KEY_DISPLAY_SYMBOLS.md, docs/reference/variables/MAC_MODIFIER_SYMBOLS.md, docs/reference/variables/STANDARD_MODIFIER_LABELS.md
Updates definition location references (line numbers) in documentation due to source file changes; no functional or API changes.
New Punctuation Map Documentation
docs/reference/index.md, docs/reference/variables/PUNCTUATION_CODE_MAP.md
Adds documentation for new PUNCTUATION_CODE_MAP constant, explaining its role as a fallback for punctuation keys when Alt/Option modifies event.key values.
Punctuation Constants
packages/hotkeys/src/constants.ts
Exports new PUNCTUATION_CODE_MAP constant mapping KeyboardEvent.code values (Minus, Equal, Slash, BracketLeft, BracketRight, Backslash, Comma, Period, Backquote, Semicolon) to canonical character equivalents.
Hotkey Matching Logic
packages/hotkeys/src/match.ts
Extends matchesKeyboardEvent fallback path to check PUNCTUATION_CODE_MAP when event.code indicates a punctuation key, enabling matching when Alt transformations affect event.key.
Test Coverage
packages/hotkeys/tests/match.test.ts
Adds comprehensive test suites for dead-key and punctuation fallback scenarios, including macOS Option+punctuation combinations, multiple modifiers, and edge cases across platforms.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 When Option pressed dash became en-,
The rabbit checked code instead of key!
Now punctuation falls back just right,
With PUNCTUATION_CODE_MAP's light—
Alt+dash works day and night! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main fix: adding event.code fallback for Alt+punctuation keys on macOS, which directly corresponds to the primary change in the changeset.
Description check ✅ Passed The description includes a detailed summary of changes, test plan with checkmarks, and references to the bug report. However, the required checklist items from the template are not explicitly marked/filled (Contributing guide and pnpm test confirmation are missing acknowledgment).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.code fallback” 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

📥 Commits

Reviewing files that changed from the base of the PR and between 75ce7c8 and bb786bc.

📒 Files selected for processing (16)
  • .changeset/fix-alt-punctuation-macos.md
  • docs/reference/functions/createHotkeyHandler.md
  • docs/reference/functions/createMultiHotkeyHandler.md
  • docs/reference/functions/isSingleLetterKey.md
  • docs/reference/functions/matchesKeyboardEvent.md
  • docs/reference/functions/normalizeKeyName.md
  • docs/reference/index.md
  • docs/reference/interfaces/CreateHotkeyHandlerOptions.md
  • docs/reference/variables/ALL_KEYS.md
  • docs/reference/variables/KEY_DISPLAY_SYMBOLS.md
  • docs/reference/variables/MAC_MODIFIER_SYMBOLS.md
  • docs/reference/variables/PUNCTUATION_CODE_MAP.md
  • docs/reference/variables/STANDARD_MODIFIER_LABELS.md
  • packages/hotkeys/src/constants.ts
  • packages/hotkeys/src/match.ts
  • packages/hotkeys/tests/match.test.ts

Comment on lines +312 to +323
export const PUNCTUATION_CODE_MAP: Record<string, string> = {
Minus: '-',
Equal: '=',
Slash: '/',
BracketLeft: '[',
BracketRight: ']',
Backslash: '\\',
Comma: ',',
Period: '.',
Backquote: '`',
Semicolon: ';',
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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/tests

Repository: 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 js

Repository: TanStack/hotkeys

Length of output: 42


🏁 Script executed:

sed -n '291,323p' packages/hotkeys/src/constants.ts

Repository: 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.

Comment on lines +657 to +679
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)
}
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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.

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.

1 participant