Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
23 changes: 23 additions & 0 deletions .changeset/case-sensitive-key-names.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
'@tanstack/hotkeys': minor
'@tanstack/react-hotkeys': minor
'@tanstack/preact-hotkeys': minor
'@tanstack/solid-hotkeys': minor
'@tanstack/vue-hotkeys': minor
'@tanstack/svelte-hotkeys': minor
'@tanstack/angular-hotkeys': minor
'@tanstack/lit-hotkeys': minor
'@tanstack/alpine-hotkeys': minor
'@tanstack/octane-hotkeys': minor
'@tanstack/ember-hotkeys': minor
'@tanstack/hotkeys-devtools': minor
---

Key names are case-sensitive, and key aliases are no longer resolved, which removes the key-name lookup tables from every bundle. Typed hotkeys, recording, matching, and display behave as before.

- Spellings that the hotkey types don't allow, such as `escape`, `Esc`, `f1`, `Plus`, or `pgdn`, now stay as written and don't match. Use the canonical names: `Escape`, `F1`, `+`, `PageDown`. Letters still match in any case, and modifier aliases such as `Ctrl`, `Cmd`, and `Option` still work.
- `validateHotkey` and `checkHotkey` warn about keys that aren't written like a known key, such as `escape`, instead of checking a list of key names. In development, registering such a hotkey logs a warning that it may never fire.
- `formatForDisplay` accepts hotkeys only, including single keys and modifiers on their own such as `'Shift'`, instead of any string. Pass a key from a `KeyboardEvent`, such as a held key from `useHeldKeys`, as `{ key }`.
- `HotkeyManager.triggerRegistration` moves into the devtools package, which was its only user.
- The recorder rejects whitespace characters, such as the non-breaking space from Option+Space on macOS, with the message "Whitespace keys cannot be recorded".
- Saved custom hotkeys and display labels ignore names inherited from `Object.prototype`, such as `toString`. Previously, saved custom hotkeys could keep those ids, and `formatForDisplay` could show function source text for them.
5 changes: 5 additions & 0 deletions .changeset/dev-only-warnings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/hotkeys': minor
---

Console warnings and errors are logged only in development. Production builds, where `process.env.NODE_ENV` is `'production'`, skip them and drop the warning code. `checkHotkey` still returns whether a hotkey is valid.
13 changes: 13 additions & 0 deletions .changeset/devtools-host-peers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@tanstack/hotkeys-devtools': minor
'@tanstack/react-hotkeys-devtools': minor
'@tanstack/preact-hotkeys-devtools': minor
'@tanstack/solid-hotkeys-devtools': minor
'@tanstack/vue-hotkeys-devtools': minor
'@tanstack/svelte-hotkeys-devtools': minor
'@tanstack/angular-hotkeys-devtools': minor
---

Each devtools adapter now lists its TanStack Devtools host as an optional peer dependency at the current release, such as `@tanstack/react-devtools` 0.10.14. Older hosts pass the plugin a theme string instead of props, so the panel ignored the theme, and TypeScript rejects the React plugin since `@tanstack/react-hotkeys-devtools` 0.12.0. Package managers now warn about an old host at install time.

Update `@tanstack/devtools-utils` and `@tanstack/devtools-ui` to 0.7.2. The Preact panel now unmounts its core on Preact 11 (TanStack/devtools#551).
17 changes: 17 additions & 0 deletions .changeset/drop-tanstack-store.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@tanstack/hotkeys': minor
'@tanstack/react-hotkeys': minor
'@tanstack/preact-hotkeys': minor
'@tanstack/solid-hotkeys': minor
'@tanstack/vue-hotkeys': minor
'@tanstack/angular-hotkeys': minor
'@tanstack/octane-hotkeys': minor
'@tanstack/alpine-hotkeys': minor
'@tanstack/ember-hotkeys': minor
'@tanstack/svelte-hotkeys': minor
'@tanstack/lit-hotkeys': minor
---

TanStack Hotkeys no longer depends on `@tanstack/store` or the framework store adapters. Its stores, such as `manager.registrations` and `recorder.store`, keep the same `state`, `get`, `setState`, and `subscribe` API and still work with TanStack Store's framework adapters. Bundles shrink by about 1.3 kB (brotli) in core and about 2.4 kB in the React adapter.

The stores are no longer `Store` instances from `@tanstack/store`, so `instanceof Store` checks and other TanStack Store features, such as `actions`, no longer apply to them.
12 changes: 12 additions & 0 deletions .changeset/lazy-app-hotkeys-store.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@tanstack/hotkeys': minor
'@tanstack/react-hotkeys': minor
'@tanstack/preact-hotkeys': minor
'@tanstack/solid-hotkeys': minor
'@tanstack/vue-hotkeys': minor
'@tanstack/svelte-hotkeys': minor
'@tanstack/angular-hotkeys': minor
'@tanstack/octane-hotkeys': minor
---

Providers no longer bundle app hotkey support unless the app uses `defineAppHotkeys`. A React app with `HotkeysProvider` and no app hotkeys is about 1 kB smaller (brotli).
4,035 changes: 3,337 additions & 698 deletions .intent/review-state.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions docs/devtools.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ The Hotkeys devtools panel lets you:

Install the TanStack Devtools host package and the Hotkeys devtools adapter for your framework.

The adapters need a current TanStack Devtools host, such as `@tanstack/react-devtools` 0.10.14 or newer. Older hosts pass the plugin a theme string instead of props, so the panel ignores the theme and TypeScript rejects the plugin. Both libraries are in beta, so upgrade them together.

<!-- ::start:framework -->

# React
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/alpine/guides/app-hotkeys.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ provideAppHotkeys(appHotkeys, {
```

- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them.
- `provideAppHotkeys` reads `customHotkeys` once, and the shared store owns the state afterwards. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning.
- `provideAppHotkeys` reads `customHotkeys` once, and the shared store owns the state afterwards. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning in development.
- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile.

The [hotkey recorder](./custom-hotkey-recording) records `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others.
Expand Down
2 changes: 2 additions & 0 deletions docs/framework/alpine/guides/formatting-display.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ The same label can stand for different hotkeys. `Mod+S` follows the character S,

Omit `platform` to use detection. On macOS, the label joins modifier symbols with spaces. Windows and Linux use names joined with `+`. macOS orders modifiers as Control, Option, Shift, Command, so `Mod+Shift+S` displays as `⇧ ⌘ S`, while the hotkey itself stays `Mod+Shift+S`.

`formatForDisplay` accepts hotkeys only, including single keys such as `'Escape'` and modifiers on their own such as `'Shift'`. A key from a `KeyboardEvent`, such as a held key, can be outside the key types: pass it as `{ key }`.

## Render individual keycaps

Set `parts: true` to get one label per key instead of a joined string:
Expand Down
2 changes: 2 additions & 0 deletions docs/framework/alpine/guides/held-key-state-helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Alpine.data('keyDisplay', () => {
})
```

To label each held key the way hotkeys are labeled, pass it to `formatForDisplay` as `{ key }`: `Shift` becomes `⇧` on macOS and `Escape` becomes `Esc`. Held keys come from the browser and can be outside the key types, which the `{ key }` form accepts.

```html
<div x-data="keyDisplay" x-text="label"></div>
```
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/alpine/guides/hotkey-manager.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ manager.isRegistered('Escape', panelElement) // Whether one matches on that targ

`isRegistered` compares hotkeys the way [duplicate detection](./hotkeys#handle-duplicate-hotkeys) does, so `Meta+S` matches a `Mod+S` registration on macOS.

`manager.registrations` is a [TanStack Store](https://tanstack.com/store) that holds every registration in a `Map`, keyed by id. Subscribe to it to run code when registrations change:
`manager.registrations` is a store that holds every registration in a `Map`, keyed by id. It reads and subscribes like a [TanStack Store](https://tanstack.com/store), so it also works with TanStack Store's framework adapters. Subscribe to it to run code when registrations change:

```ts
const subscription = manager.registrations.subscribe((registrations) => {
Expand Down
4 changes: 2 additions & 2 deletions docs/framework/alpine/guides/hotkeys.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,9 @@ Use a target for hotkeys that belong to a focusable element, such as a list or a

## Handle duplicate hotkeys

When you register a hotkey that is already registered on the same target and in the same scope, TanStack Hotkeys logs a warning, and both callbacks run. Set `conflictBehavior` to change that:
When you register a hotkey that is already registered on the same target and in the same scope, TanStack Hotkeys logs a warning in development, and both callbacks run. Set `conflictBehavior` to change that:

- `'warn'` (default) logs a warning and keeps both registrations.
- `'warn'` (default) logs a warning in development and keeps both registrations.
- `'error'` throws an error.
- `'replace'` unregisters the existing registration.
- `'allow'` keeps both without a warning.
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/angular/guides/app-hotkeys.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ bootstrapApplication(AppComponent, {
```

- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them.
- `provideHotkeys` reads `customHotkeys` once and owns the state afterwards. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning.
- `provideHotkeys` reads `customHotkeys` once and owns the state afterwards. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning in development.
- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile.

The [hotkey recorder](./custom-hotkey-recording) records `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others.
Expand Down
2 changes: 2 additions & 0 deletions docs/framework/angular/guides/formatting-display.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ The same label can stand for different hotkeys. `Mod+S` follows the character S,

Omit `platform` to use detection. On macOS, the label joins modifier symbols with spaces. Windows and Linux use names joined with `+`. macOS orders modifiers as Control, Option, Shift, Command, so `Mod+Shift+S` displays as `⇧ ⌘ S`, while the hotkey itself stays `Mod+Shift+S`.

`formatForDisplay` accepts hotkeys only, including single keys such as `'Escape'` and modifiers on their own such as `'Shift'`. A key from a `KeyboardEvent`, such as a held key, can be outside the key types: pass it as `{ key }`.

## Render individual keycaps

Set `parts: true` to get one label per key instead of a joined string:
Expand Down
2 changes: 2 additions & 0 deletions docs/framework/angular/guides/held-key-state-helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export class KeyDisplayComponent {
}
```

To label each held key the way hotkeys are labeled, pass it to `formatForDisplay` as `{ key }`: `Shift` becomes `⇧` on macOS and `Escape` becomes `Esc`. Held keys come from the browser and can be outside the key types, which the `{ key }` form accepts.

## Check whether a key is held

`injectKeyHold` returns whether one key is held. It updates only when that key is pressed or released, and accepts a getter for a key that changes:
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/angular/guides/hotkey-manager.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ manager.isRegistered('Escape', panelElement) // Whether one matches on that targ

`isRegistered` compares hotkeys the way [duplicate detection](./hotkeys#handle-duplicate-hotkeys) does, so `Meta+S` matches a `Mod+S` registration on macOS.

`manager.registrations` is a [TanStack Store](https://tanstack.com/store) that holds every registration in a `Map`, keyed by id. Subscribe to it to run code when registrations change:
`manager.registrations` is a store that holds every registration in a `Map`, keyed by id. It reads and subscribes like a [TanStack Store](https://tanstack.com/store), so it also works with TanStack Store's framework adapters. Subscribe to it to run code when registrations change:

```ts
const subscription = manager.registrations.subscribe((registrations) => {
Expand Down
4 changes: 2 additions & 2 deletions docs/framework/angular/guides/hotkeys.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,9 +384,9 @@ Use a target for hotkeys that belong to a focusable element, such as a list or a

## Handle duplicate hotkeys

When you register a hotkey that is already registered on the same target and in the same scope, TanStack Hotkeys logs a warning, and both callbacks run. Set `conflictBehavior` to change that:
When you register a hotkey that is already registered on the same target and in the same scope, TanStack Hotkeys logs a warning in development, and both callbacks run. Set `conflictBehavior` to change that:

- `'warn'` (default) logs a warning and keeps both registrations.
- `'warn'` (default) logs a warning in development and keeps both registrations.
- `'error'` throws an error.
- `'replace'` unregisters the existing registration.
- `'allow'` keeps both without a warning.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ title: injectDefaultHotkeysOptions
function injectDefaultHotkeysOptions(): HotkeysProviderOptions;
```

Defined in: [hotkeys-provider.ts:242](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L242)
Defined in: [hotkeys-provider.ts:244](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L244)

## Returns

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ title: injectHotkeysContext
function injectHotkeysContext(): HotkeysContextValue;
```

Defined in: [hotkeys-provider.ts:238](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L238)
Defined in: [hotkeys-provider.ts:240](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L240)

## Returns

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ title: provideHotkeys
function provideHotkeys(options?): Provider;
```

Defined in: [hotkeys-provider.ts:231](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L231)
Defined in: [hotkeys-provider.ts:233](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L233)

Provides default options, app hotkeys, and the user's custom hotkeys to an
injector, such as the application or a component.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ id: HotkeysContextValue
title: HotkeysContextValue
---

Defined in: [hotkeys-provider.ts:72](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L72)
Defined in: [hotkeys-provider.ts:74](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L74)

## Properties

Expand All @@ -15,7 +15,7 @@ readonly appHotkeysState: Signal<
| null>;
```

Defined in: [hotkeys-provider.ts:79](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L79)
Defined in: [hotkeys-provider.ts:81](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L81)

Tracks app hotkeys and custom hotkeys so signals and effects re-run.

Expand All @@ -29,7 +29,7 @@ readonly appHotkeysStore:
| null;
```

Defined in: [hotkeys-provider.ts:77](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L77)
Defined in: [hotkeys-provider.ts:79](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L79)

***

Expand All @@ -39,7 +39,7 @@ Defined in: [hotkeys-provider.ts:77](https://github.com/TanStack/hotkeys/blob/ma
readonly defaultOptions: HotkeysProviderOptions;
```

Defined in: [hotkeys-provider.ts:74](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L74)
Defined in: [hotkeys-provider.ts:76](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L76)

Merged with every ancestor provider's defaults, including `hotkey.scope`.

Expand All @@ -51,6 +51,6 @@ Merged with every ancestor provider's defaults, including `hotkey.scope`.
readonly enabled: Signal<boolean>;
```

Defined in: [hotkeys-provider.ts:76](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L76)
Defined in: [hotkeys-provider.ts:78](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L78)

Whether this provider and every ancestor are enabled.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ id: HotkeysProviderOptions
title: HotkeysProviderOptions
---

Defined in: [hotkeys-provider.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L28)
Defined in: [hotkeys-provider.ts:30](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L30)

## Properties

Expand All @@ -13,7 +13,7 @@ Defined in: [hotkeys-provider.ts:28](https://github.com/TanStack/hotkeys/blob/ma
optional hotkey?: InjectHotkeyCommonOptions;
```

Defined in: [hotkeys-provider.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L33)
Defined in: [hotkeys-provider.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L35)

Defaults for every hotkey registered by `injectHotkey` and `injectHotkeys`.
`requireReset` applies to chords and `timeout` applies to sequences.
Expand All @@ -26,6 +26,6 @@ Defaults for every hotkey registered by `injectHotkey` and `injectHotkeys`.
optional hotkeyRecorder?: Partial<Omit<HotkeyChordRecorderOptions & HotkeySequenceRecorderOptions, "maxSteps" | "validate" | "onRecord">>;
```

Defined in: [hotkeys-provider.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L35)
Defined in: [hotkeys-provider.ts:37](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L37)

Defaults for every `injectHotkeyRecorder`.
Loading
Loading