Skip to content

fix(hotkey): migrate useHotkeys to event.key|code#114192

Merged
natemoo-re merged 8 commits intomasterfrom
nm/hotkey-i18n
Apr 29, 2026
Merged

fix(hotkey): migrate useHotkeys to event.key|code#114192
natemoo-re merged 8 commits intomasterfrom
nm/hotkey-i18n

Conversation

@natemoo-re
Copy link
Copy Markdown
Member

@natemoo-re natemoo-re commented Apr 28, 2026

the keyboard detection logic for useHotkeys was using keyCode which is deprecated and MDN actively discourages because it is tightly coupled to keyboard layout.

this pr migrates our implementation to the modern replacements, which are code (baseline limited availability due to Android compat issues) and key (baseline widely available).

by relying on these KeyboardEvent properties, we get both the value of the key and the physical location of the key in a layout-agnostic manner.

this should fix command+/ handling on german keyboard layouts as well as a host of other keyboard problems it did not (but #114198 does!) but this is generally more robust so we should merge anyway

@github-actions github-actions Bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Apr 28, 2026
@natemoo-re natemoo-re changed the title fix(hotkey): support layout-independent keybindings fix(hotkey): migrate useHotkeys to event.key|code Apr 28, 2026
useHotkeys([
{
match: 'Escape',
enabled: isDrawerOpen,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Big refactoring win! Added an enabled option to useHotkeys because the migration to key uncovered a latent bug here that we couldn't detect before.

33452cf migrated the hook from event.keyCode to event.key. GlobalDrawer had been registering an unconditional useHotkeys for Escape—this previously failed silently in tests because userEvent.keyboard('{Escape}') doesn't set keyCode in jsdom, but now it fires (and calls preventDefault) on every Escape—even when no drawer was open. The modal's escape handler bails on e.defaultPrevented (added in PR #109556 to let react-aria child overlays absorb Escape), so the global modal stopped closing entirely.

@natemoo-re natemoo-re marked this pull request as ready for review April 29, 2026 15:03
@natemoo-re natemoo-re requested review from a team as code owners April 29, 2026 15:03
natemoo-re added a commit that referenced this pull request Apr 29, 2026
Seer currently accepts `Command+/` as a shortcut for opening the drawer.
One problem: the `/` key is a secondary key on all European keyboards,
and we didn't support those.

After a [quite
(#114198)](#114198) [productive
(#114192)](#114192) deep-dive on
our hotkey primitives to fix edge cases, I've circled back to the most
robust fix for non-QWERTY keyboards being the most straightforward
one—we should just hard-code keybindings for a known set of layouts.

This PR adds support for `command+shift+7` for QWERTZ and
`command+shift+.` for AZERTY which should cover where the majority of
our users see `/` on their keyboards. Regrettably Eurocentric, but
that's best effort.

### Alternatives Considered

The Chrome-only
[`navigator.keyboard`](https://developer.mozilla.org/en-US/docs/Web/API/Keyboard)
interface seemed promising, but after quite a bit of experimentation,
proved to be a dead-end here. Given that the API is `async`, only
supported in secure environments, and seems unlikely to advance further
in the spec process, I decided to go with a solution that worked
everywhere.
navigationContext()
);

fireEvent.keyDown(document, {keyCode: 66 /* b */, ctrlKey: true});
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Great test change 🙏🏼

@natemoo-re natemoo-re merged commit 3193cc4 into master Apr 29, 2026
66 checks passed
@natemoo-re natemoo-re deleted the nm/hotkey-i18n branch April 29, 2026 16:14
cleptric pushed a commit that referenced this pull request May 5, 2026
Seer currently accepts `Command+/` as a shortcut for opening the drawer.
One problem: the `/` key is a secondary key on all European keyboards,
and we didn't support those.

After a [quite
(#114198)](#114198) [productive
(#114192)](#114192) deep-dive on
our hotkey primitives to fix edge cases, I've circled back to the most
robust fix for non-QWERTY keyboards being the most straightforward
one—we should just hard-code keybindings for a known set of layouts.

This PR adds support for `command+shift+7` for QWERTZ and
`command+shift+.` for AZERTY which should cover where the majority of
our users see `/` on their keyboards. Regrettably Eurocentric, but
that's best effort.

### Alternatives Considered

The Chrome-only
[`navigator.keyboard`](https://developer.mozilla.org/en-US/docs/Web/API/Keyboard)
interface seemed promising, but after quite a bit of experimentation,
proved to be a dead-end here. Given that the API is `async`, only
supported in secure environments, and seems unlikely to advance further
in the spec process, I decided to go with a solution that worked
everywhere.
cleptric pushed a commit that referenced this pull request May 5, 2026
the keyboard detection logic for `useHotkeys` was using
[`keyCode`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode)
which is deprecated and MDN actively discourages because it is tightly
coupled to keyboard layout.

this pr migrates our implementation to the modern replacements, which
are
[`code`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code)
(baseline limited availability due to Android compat issues) and
[`key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key)
(baseline widely available).

by relying on these `KeyboardEvent` properties, we get both the
**value** of the key and the **physical location** of the key in a
layout-agnostic manner.

~~this _should_ fix `command+/` handling on german keyboard layouts as
well as a host of other keyboard problems~~ it did not ([but #114198
does!](#114198)) but this is
generally more robust so we should merge anyway
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants