Skip to content

fix(shortcuts): match single-letter hotkeys by layout-aware key#3525

Open
haacked wants to merge 3 commits into
mainfrom
posthog-code/fix-hotkeys-layout-aware
Open

fix(shortcuts): match single-letter hotkeys by layout-aware key#3525
haacked wants to merge 3 commits into
mainfrom
posthog-code/fix-hotkeys-layout-aware

Conversation

@haacked

@haacked haacked commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Problem

On non-QWERTY keyboard layouts (Dvorak, AZERTY, …), single-letter shortcuts fire based on the physical key position rather than the letter the user actually typed. The reported case: selecting text in the chat composer and pressing Cmd+C navigates to the Inbox instead of copying. On Dvorak the key labelled "C" sits at the physical QWERTY-"I" slot, and the Inbox shortcut is mod+i.

Root cause: react-hotkeys-hook@4.6.2 matches a hotkey's key against both the layout-aware event.key and the physical event.code (mapKey(code)). For mod+i it fires whenever event.code === "KeyI", regardless of the logical letter. Every single-letter mod+<letter> shortcut (mod+k, mod+n, mod+b, mod+w, mod+o, mod+r, mod+s, mod+f, mod+j, …) has the same problem. The library version in use has no option to disable code-based matching (useKey only exists in v5).

Closes #1797

Changes

Patch the react-hotkeys-hook matcher (patches/react-hotkeys-hook.patch, wired via pnpm patchedDependencies) to derive the pressed key from the layout-aware event.key. It falls back to the physical event.code only when a modifier (e.g. macOS Option) turns event.key into a non-alphanumeric glyph or dead key — so special keys (arrows, escape, space) and Option shortcuts keep working, while single-letter shortcuts now match the printed letter on every layout.

No call sites change; the fix applies to all current and future useHotkeys usages.

How did you test this?

Added packages/ui/src/features/command/hotkeysLayout.test.ts, a Vitest regression guard that renders useHotkeys("mod+i", …) and dispatches keydown events:

  • QWERTY Cmd+I (key: "i", code: "KeyI") → fires.
  • Dvorak Cmd+C (key: "c", code: "KeyI") → does not fire (the bug that hijacked copy).

Manually tested by switching my system keyboard layout to Dvorak and confirming Cmd+C now copies selected text instead of navigating to the Inbox. Also re-checked other mod+<letter> shortcuts on QWERTY to confirm no regression.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Created with PostHog from a Slack thread

@trunk-io

trunk-io Bot commented Jul 16, 2026

Copy link
Copy Markdown

✨ Submitted to Merge by @haacked. It will be added to the merge queue once all branch protection rules pass, there are no merge conflicts with the target branch, and impacted targets for the current PR head commit have been uploaded. See more details here.

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit d0b6475.

@haacked
haacked marked this pull request as ready for review July 16, 2026 20:43
@haacked
haacked requested a review from a team July 16, 2026 20:43
@greptile-apps

greptile-apps Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "fix(shortcuts): match single-letter hotk..." | Re-trigger Greptile

Comment thread patches/react-hotkeys-hook.patch
haacked added a commit that referenced this pull request Jul 17, 2026
Biome ci flagged the test file as unformatted (lines exceeded print
width). Fixes #3525
haacked and others added 3 commits July 17, 2026 14:08
react-hotkeys-hook 4.6.2 matches a hotkey's letter against both the
layout-aware event.key and the physical event.code. For a single-letter
shortcut like "mod+i" (Inbox), that means any key at the physical QWERTY-"I"
position triggers it — so on a Dvorak layout Cmd+C (whose key sits at that
physical slot) navigates to the inbox instead of copying. Every single-letter
mod+<letter> shortcut has the same problem on non-QWERTY layouts.

Patch the library matcher to derive the key from event.key, falling back to
the physical event.code only when a modifier (e.g. macOS Option) turns
event.key into a non-alphanumeric glyph. Special keys (arrows, escape, space)
and Option shortcuts keep working via the fallback.

Generated-By: PostHog Code
Task-Id: 570b6a35-794b-45bb-933f-7b54751b014a
The layout-aware matcher fix only patched dist/react-hotkeys-hook.esm.js.
Consumers that resolve the package's main field (Vitest's SSR module
resolution, Node/Electron require(), Metro/React Native) load
dist/index.js, which delegates to the untouched CJS bundles and keeps
the physical-code matching bug. Apply the same matcher fix to
dist/react-hotkeys-hook.cjs.development.js and
dist/react-hotkeys-hook.cjs.production.min.js so every resolution path
gets layout-aware matching. The regression test added alongside the
original fix was failing in CI for exactly this reason; it now passes.

Also fix an unrelated pre-existing quality-check failure: type the
codex app-server model/list response as RawModel[] instead of any[].
Biome ci flagged the test file as unformatted (lines exceeded print
width). Fixes #3525
@haacked
haacked force-pushed the posthog-code/fix-hotkeys-layout-aware branch from 43eb832 to d0b6475 Compare July 17, 2026 21:08
@haacked

haacked commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

/trunk merge

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.

Keyboard shortcuts fire based on physical key position, breaking non-QWERTY layouts

2 participants