+
+```
+
+## Validation
+
+```ts
+import { validateHotkey } from '@tanstack/ember-hotkeys';
+
+const result = validateHotkey('Alt+A');
+```
diff --git a/docs/framework/ember/guides/hotkey-recording.md b/docs/framework/ember/guides/hotkey-recording.md
new file mode 100644
index 00000000..b7c3e825
--- /dev/null
+++ b/docs/framework/ember/guides/hotkey-recording.md
@@ -0,0 +1,72 @@
+---
+title: Hotkey Recording Guide
+id: hotkey-recording
+---
+
+TanStack Hotkeys provides the `HotkeyRecorder` class for building shortcut customization UIs. The class is re-exported from `@tanstack/ember-hotkeys` and can be used directly in Ember components.
+
+## Basic Usage
+
+```gts
+import Component from '@glimmer/component';
+import { tracked } from '@glimmer/tracking';
+import { action } from '@ember/object';
+import { registerDestructor } from '@ember/destroyable';
+import { HotkeyRecorder, formatForDisplay } from '@tanstack/ember-hotkeys';
+import type { Hotkey } from '@tanstack/ember-hotkeys';
+
+export default class ShortcutRecorder extends Component {
+ @tracked recordedHotkey: Hotkey | null = null;
+ @tracked isRecording = false;
+
+ recorder = new HotkeyRecorder({
+ onRecord: (hotkey) => {
+ this.recordedHotkey = hotkey;
+ this.isRecording = false;
+ },
+ onCancel: () => {
+ this.isRecording = false;
+ },
+ });
+
+ constructor(owner: unknown, args: Record) {
+ super(owner, args);
+ registerDestructor(this, () => this.recorder.cancel());
+ }
+
+ @action startRecording() {
+ this.recorder.start();
+ this.isRecording = true;
+ }
+
+ @action cancelRecording() {
+ this.recorder.cancel();
+ }
+
+
+
+ {{#if this.isRecording}}
+
+ {{/if}}
+
+}
+```
+
+## `ignoreInputs`
+
+The `HotkeyRecorder` supports an `ignoreInputs` option (defaults to `true`). When `true`, the recorder will not intercept normal typing in text inputs, textareas, selects, or contentEditable elements — keystrokes pass through to the input as usual. Pressing **Escape** still cancels recording even when focused on an input.
+
+```ts
+const recorder = new HotkeyRecorder({
+ ignoreInputs: false, // record even from inside inputs
+ onRecord: (hotkey) => console.log(hotkey),
+});
+```
diff --git a/docs/framework/ember/guides/hotkeys.md b/docs/framework/ember/guides/hotkeys.md
new file mode 100644
index 00000000..5c9f1c99
--- /dev/null
+++ b/docs/framework/ember/guides/hotkeys.md
@@ -0,0 +1,166 @@
+---
+title: Hotkeys Guide
+id: hotkeys
+---
+
+The `{{onHotkey}}` helper is the primary way to register keyboard shortcuts in Ember applications. It wraps the singleton `HotkeyManager` with Ember's helper lifecycle: when the helper enters the template the key is registered, and when the template is torn down the listener is removed.
+
+## Basic Usage
+
+```gts
+import onHotkey from '@tanstack/ember-hotkeys/helpers/on-hotkey';
+
+const save = () => {
+ saveDocument();
+};
+
+
+ {{onHotkey "Mod+S" save}}
+
+```
+
+The callback receives the original `KeyboardEvent` as the first argument and a `HotkeyCallbackContext` as the second:
+
+```gts
+import onHotkey from '@tanstack/ember-hotkeys/helpers/on-hotkey';
+import type { HotkeyCallbackContext } from '@tanstack/ember-hotkeys';
+
+const save = (event: KeyboardEvent, context: HotkeyCallbackContext) => {
+ console.log(context.hotkey);
+ console.log(context.parsedHotkey);
+};
+
+
+ {{onHotkey "Mod+S" save}}
+
+```
+
+## Options
+
+All options are passed as named arguments in the template:
+
+```gts
+{{onHotkey "Mod+S" @onSave
+ preventDefault=true
+ stopPropagation=true
+ eventType="keydown"
+ enabled=true
+}}
+```
+
+### `enabled`
+
+When `enabled` is false, the hotkey **stays registered** (visible in devtools); only the callback is suppressed.
+
+```gts
+import onHotkey from '@tanstack/ember-hotkeys/helpers/on-hotkey';
+
+
+ {{onHotkey "Escape" @onClose enabled=@isOpen}}
+
+```
+
+### `target`
+
+Scope a hotkey to a specific DOM element instead of the entire document:
+
+```gts
+import Component from '@glimmer/component';
+import { action } from '@ember/object';
+import onHotkey from '@tanstack/ember-hotkeys/helpers/on-hotkey';
+
+export default class Panel extends Component {
+ panelEl: HTMLElement | null = null;
+
+ @action setRef(el: HTMLElement) {
+ this.panelEl = el;
+ }
+
+
+
+
+}
+```
+
+### `preventDefault`
+
+Prevent the browser default action (e.g., browser save dialog for `Mod+S`). This is `true` by default.
+
+```gts
+{{onHotkey "Mod+S" @onSave preventDefault=true}}
+```
+
+### `stopPropagation`
+
+Stop the event from propagating to parent elements:
+
+```gts
+{{onHotkey "Escape" @onClose stopPropagation=true}}
+```
+
+### `eventType`
+
+Listen for `keyup` events instead of the default `keydown`:
+
+```gts
+{{onHotkey "Escape" @onClose eventType="keyup"}}
+```
+
+### `requireReset`
+
+When `true`, the hotkey fires at most once per key-hold. The user must release the key and press again to fire the callback a second time.
+
+```gts
+{{onHotkey "Escape" @onClose requireReset=true}}
+```
+
+### `ignoreInputs`
+
+By default, hotkeys are suppressed when an input element is focused. Set `ignoreInputs` to `false` to fire the callback even when an input, textarea, or contenteditable element has focus:
+
+```gts
+{{onHotkey "Enter" @onSubmit ignoreInputs=false}}
+```
+
+### `conflictBehavior`
+
+Control what happens when the same hotkey is registered twice:
+
+```gts
+{{onHotkey "Mod+S" @onSave conflictBehavior="replace"}}
+```
+
+### `platform`
+
+Force a specific platform for `Mod` resolution:
+
+```gts
+{{onHotkey "Mod+S" @onSave platform="mac"}}
+```
+
+## Metadata (name & description)
+
+Every hotkey registration can carry a `meta` object with a `name` and `description`. This metadata is informational only -- it does not affect hotkey behavior -- but it flows through to registrations and devtools, making it easy to build shortcut palettes and help screens.
+
+```gts
+{{onHotkey "Mod+S" @onSave meta=(hash name="Save" description="Save the document")}}
+```
+
+## Automatic Cleanup
+
+Registrations are cleaned up automatically when the helper is destroyed -- for example, when the component is removed from the DOM or when an `{{#if}}` block becomes falsy.
+
+## The Hotkey Manager
+
+You can access the underlying manager directly when needed:
+
+```ts
+import { getHotkeyManager } from '@tanstack/ember-hotkeys';
+
+const manager = getHotkeyManager();
+manager.isRegistered('Mod+S');
+manager.getRegistrationCount();
+```
diff --git a/docs/framework/ember/guides/key-state-tracking.md b/docs/framework/ember/guides/key-state-tracking.md
new file mode 100644
index 00000000..4253d062
--- /dev/null
+++ b/docs/framework/ember/guides/key-state-tracking.md
@@ -0,0 +1,106 @@
+---
+title: Key State Tracking Guide
+id: key-state-tracking
+---
+
+TanStack Hotkeys provides `KeyStateTracker` for tracking live keyboard state. The tracker is a singleton re-exported from `@tanstack/ember-hotkeys` that automatically manages `keydown`/`keyup`/`blur` listeners and exposes reactive state via a TanStack Store.
+
+## `KeyStateTracker`
+
+```ts
+import { getKeyStateTracker } from '@tanstack/ember-hotkeys';
+
+const tracker = getKeyStateTracker();
+
+tracker.getHeldKeys(); // ['Control', 'Shift']
+tracker.isKeyHeld('Shift'); // true
+```
+
+## Common Patterns
+
+### Hold-to-Reveal UI
+
+```gts
+import Component from '@glimmer/component';
+import { tracked } from '@glimmer/tracking';
+import { registerDestructor } from '@ember/destroyable';
+import { getKeyStateTracker } from '@tanstack/ember-hotkeys';
+
+export default class FileActions extends Component {
+ @tracked isShiftHeld = false;
+
+ constructor(owner: unknown, args: Record) {
+ super(owner, args);
+
+ const tracker = getKeyStateTracker();
+
+ const unsubscribe = tracker.store.subscribe(() => {
+ this.isShiftHeld = tracker.isKeyHeld('Shift');
+ });
+
+ registerDestructor(this, unsubscribe);
+ }
+
+
+ {{#if this.isShiftHeld}}
+
+ {{else}}
+
+ {{/if}}
+
+}
+```
+
+### Displaying Held Keys
+
+```gts
+import Component from '@glimmer/component';
+import { tracked } from '@glimmer/tracking';
+import { registerDestructor } from '@ember/destroyable';
+import { getKeyStateTracker } from '@tanstack/ember-hotkeys';
+
+export default class KeyDebugger extends Component {
+ @tracked heldKeys: Array = [];
+
+ constructor(owner: unknown, args: Record) {
+ super(owner, args);
+
+ const tracker = getKeyStateTracker();
+
+ const unsubscribe = tracker.store.subscribe(() => {
+ this.heldKeys = tracker.getHeldKeys();
+ });
+
+ registerDestructor(this, unsubscribe);
+ }
+
+
+
+ {{#if this.heldKeys.length}}
+ Keys: {{this.heldKeys}}
+ {{else}}
+ No keys held
+ {{/if}}
+
+
+}
+```
+
+## Under the Hood
+
+The `KeyStateTracker` is a singleton that manages its own `keydown`, `keyup`, and `blur` listeners. It uses a TanStack Store for reactive state, so you can subscribe to changes and read current state:
+
+```ts
+import { getKeyStateTracker } from '@tanstack/ember-hotkeys';
+
+const tracker = getKeyStateTracker();
+
+// Read current state
+tracker.store.state.heldKeys; // ['Control', 'A']
+tracker.store.state.heldCodes; // { Control: 'ControlLeft', A: 'KeyA' }
+
+// Subscribe to changes
+const unsubscribe = tracker.store.subscribe(() => {
+ console.log('Currently held:', tracker.getHeldKeys());
+});
+```
diff --git a/docs/framework/ember/guides/sequence-recording.md b/docs/framework/ember/guides/sequence-recording.md
new file mode 100644
index 00000000..9e65ee87
--- /dev/null
+++ b/docs/framework/ember/guides/sequence-recording.md
@@ -0,0 +1,19 @@
+---
+title: Sequence Recording Guide
+id: sequence-recording
+---
+
+Use `HotkeySequenceRecorder` from `@tanstack/ember-hotkeys` for recording multi-key sequences. It exposes `start`, `stop`, `cancel`, and `commit` methods, along with state like `isRecording`, `steps`, and `recordedSequence`.
+
+```ts
+import { HotkeySequenceRecorder } from '@tanstack/ember-hotkeys';
+
+const recorder = new HotkeySequenceRecorder({
+ onRecord: (sequence) => console.log('Recorded:', sequence),
+ onCancel: () => console.log('Cancelled'),
+});
+```
+
+## `ignoreInputs`
+
+The `HotkeySequenceRecorder` supports an `ignoreInputs` option (defaults to `true`). When `true`, the recorder will not intercept normal typing in text inputs, textareas, selects, or contentEditable elements — keystrokes pass through to the input as usual. Pressing **Escape** still cancels recording even when focused on an input. Set `ignoreInputs: false` if you want the recorder to capture keys from within input elements.
diff --git a/docs/framework/ember/guides/sequences.md b/docs/framework/ember/guides/sequences.md
new file mode 100644
index 00000000..222cb780
--- /dev/null
+++ b/docs/framework/ember/guides/sequences.md
@@ -0,0 +1,105 @@
+---
+title: Sequences Guide
+id: sequences
+---
+
+TanStack Hotkeys supports multi-key sequences in Ember, where keys are pressed one after another rather than simultaneously.
+
+## Basic Usage
+
+```gts
+import onHotkeySequence from '@tanstack/ember-hotkeys/helpers/on-hotkey-sequence';
+import { array } from '@ember/helper';
+
+const scrollToTop = () => {
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+};
+
+
+ {{onHotkeySequence (array "G" "G") scrollToTop}}
+
+```
+
+## Sequence Options
+
+Options are passed as named arguments, just like `{{onHotkey}}`:
+
+```gts
+{{onHotkeySequence (array "G" "G") @onScrollToTop timeout=1000 enabled=true}}
+```
+
+### `timeout`
+
+The maximum time (in milliseconds) between key presses in a sequence. Defaults to 1000ms.
+
+```gts
+{{onHotkeySequence (array "D" "I" "W") @onDeleteWord timeout=500}}
+```
+
+### Reactive `enabled`
+
+When disabled, the sequence **stays registered** (visible in devtools); only execution is suppressed.
+
+```gts
+{{onHotkeySequence (array "G" "G") @onScrollToTop enabled=@isVimMode}}
+```
+
+### `meta`
+
+Sequences support the same `meta` option as hotkeys, allowing you to attach a `name` and `description` for use in shortcut palettes and devtools.
+
+```gts
+{{onHotkeySequence (array "G" "G") @onScrollToTop
+ meta=(hash name="Go to Top" description="Scroll to the top of the page")
+}}
+```
+
+See the [Hotkeys Guide](./hotkeys#metadata-name--description) for more details on metadata.
+
+## Chained Modifier Chords
+
+You can use modifiers within sequence steps:
+
+```gts
+{{onHotkeySequence (array "Shift+R" "Shift+T") @onAction}}
+```
+
+While a sequence is in progress, **modifier-only** keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored: they do not advance the sequence and do not reset progress.
+
+## Common Patterns
+
+### Vim-Style Navigation
+
+```gts
+import onHotkeySequence from '@tanstack/ember-hotkeys/helpers/on-hotkey-sequence';
+import { array } from '@ember/helper';
+
+
+ {{onHotkeySequence (array "G" "G") @onScrollToTop}}
+ {{onHotkeySequence (array "G" "Shift+G") @onScrollToBottom}}
+ {{onHotkeySequence (array "D" "D") @onDeleteLine}}
+ {{onHotkeySequence (array "D" "W") @onDeleteWord}}
+ {{onHotkeySequence (array "C" "I" "W") @onChangeInnerWord}}
+
+```
+
+### Konami Code
+
+```gts
+{{onHotkeySequence
+ (array "ArrowUp" "ArrowUp" "ArrowDown" "ArrowDown" "B" "A")
+ @onEasterEgg
+ timeout=2000
+}}
+```
+
+## Under the Hood
+
+`{{onHotkeySequence}}` uses the singleton `SequenceManager`. You can also access it directly:
+
+```ts
+import { createSequenceMatcher, getSequenceManager } from '@tanstack/ember-hotkeys';
+
+const manager = getSequenceManager();
+const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 });
+```
diff --git a/docs/framework/ember/quick-start.md b/docs/framework/ember/quick-start.md
new file mode 100644
index 00000000..d062691d
--- /dev/null
+++ b/docs/framework/ember/quick-start.md
@@ -0,0 +1,112 @@
+---
+title: Quick Start
+id: quick-start
+---
+
+## Installation
+
+Don't have TanStack Hotkeys installed yet? See the [Installation](../../installation) page for instructions.
+
+## Your First Hotkey
+
+The `{{onHotkey}}` helper is the primary way to register keyboard shortcuts in Ember:
+
+```gts
+import onHotkey from '@tanstack/ember-hotkeys/helpers/on-hotkey';
+
+const save = () => {
+ saveDocument();
+};
+
+
+ {{onHotkey "Mod+S" save}}
+
Press Cmd+S (Mac) or Ctrl+S (Windows) to save
+
+```
+
+The `Mod` modifier automatically resolves to `Meta` (Command) on macOS and `Control` on Windows/Linux, so your shortcuts work across platforms without extra logic.
+
+## Common Patterns
+
+### Multiple Hotkeys
+
+```gts
+import onHotkey from '@tanstack/ember-hotkeys/helpers/on-hotkey';
+
+
+ {{onHotkey "Mod+S" @onSave}}
+ {{onHotkey "Mod+Z" @onUndo}}
+ {{onHotkey "Mod+Shift+Z" @onRedo}}
+ {{onHotkey "Mod+F" @onSearch}}
+ {{onHotkey "Escape" @onDismiss}}
+
+```
+
+### Conditional Hotkeys
+
+Enable or disable hotkeys based on application state via the `enabled` option:
+
+```gts
+import onHotkey from '@tanstack/ember-hotkeys/helpers/on-hotkey';
+
+
+ {{onHotkey "Escape" @onClose enabled=@isOpen}}
+
+```
+
+### Multi-Key Sequences
+
+Register Vim-style key sequences with the `{{onHotkeySequence}}` helper:
+
+```gts
+import onHotkeySequence from '@tanstack/ember-hotkeys/helpers/on-hotkey-sequence';
+import { array } from '@ember/helper';
+
+
+ {{onHotkeySequence (array "G" "G") @onScrollToTop}}
+ {{onHotkeySequence (array "G" "Shift+G") @onScrollToBottom}}
+
+```
+
+### Displaying Hotkeys in the UI
+
+Format hotkeys for platform-aware display:
+
+```gts
+import { formatForDisplay } from '@tanstack/ember-hotkeys';
+import onHotkey from '@tanstack/ember-hotkeys/helpers/on-hotkey';
+
+
+ {{onHotkey "Mod+S" @onSave}}
+
+
+```
+
+## Test Helpers
+
+The package ships with test utilities for writing integration tests:
+
+```ts
+import { triggerKeyPress, triggerKeyRelease } from '@tanstack/ember-hotkeys/test-support';
+
+test('it saves on Mod+S', async function (assert) {
+ assert.expect(1);
+
+ const onSave = () => assert.ok(true, 'save was called');
+
+ await render(
+
+ {{onHotkey "Mod+S" onSave}}
+ ,
+ );
+
+ await triggerKeyPress('Mod+S');
+});
+```
+
+## Next Steps
+
+- [Hotkeys Guide](./guides/hotkeys)
+- [Sequences Guide](./guides/sequences)
diff --git a/docs/framework/ember/reference/index.md b/docs/framework/ember/reference/index.md
new file mode 100644
index 00000000..b6ac793a
--- /dev/null
+++ b/docs/framework/ember/reference/index.md
@@ -0,0 +1,41 @@
+---
+id: "@tanstack/ember-hotkeys"
+title: "@tanstack/ember-hotkeys"
+---
+
+# @tanstack/ember-hotkeys
+
+## Helpers
+
+- [onHotkey](../guides/hotkeys) — Register a single keyboard shortcut
+- [onHotkeySequence](../guides/sequences) — Register a multi-key sequence (Vim-style)
+
+## Test Support
+
+- `triggerKeyPress(combo)` — Dispatch a `keydown` event for the given hotkey combo and settle
+- `triggerKeyRelease(combo)` — Dispatch a `keyup` event for the given hotkey combo and settle
+
+Import from `@tanstack/ember-hotkeys/test-support`.
+
+## Interfaces
+
+- `OnHotkeySignature` — Glint signature for the `onHotkey` helper
+- `OnHotkeyOptions` — Named arguments accepted by `onHotkey`
+- `OnHotkeySequenceSignature` — Glint signature for the `onHotkeySequence` helper
+- `OnHotkeySequenceOptions` — Named arguments accepted by `onHotkeySequence`
+
+## Template Registry
+
+Import `Registry` from `@tanstack/ember-hotkeys/template-registry` and merge it into your app's registry for Glint strict-mode type checking:
+
+```ts
+import type { Registry as EmberHotkeysRegistry } from '@tanstack/ember-hotkeys/template-registry';
+
+declare module '@glint/environment-ember-loose/registry' {
+ export default interface Registry extends EmberHotkeysRegistry {}
+}
+```
+
+## Re-exports
+
+This package re-exports everything from the core [`@tanstack/hotkeys`](../../../reference/index) package, so there is no need to install the core package separately.
diff --git a/docs/installation.md b/docs/installation.md
index f92e3995..899e9f5d 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -8,6 +8,7 @@ TanStack Hotkeys is compatible with various front-end frameworks. Install the co
angular: @tanstack/angular-hotkeys
+ember: @tanstack/ember-hotkeys
lit: @tanstack/lit-hotkeys
preact: @tanstack/preact-hotkeys
react: @tanstack/react-hotkeys
@@ -84,6 +85,16 @@ Lit currently ships the hotkeys adapter only, so no dedicated Lit devtools packa
+
+
+### Ember
+
+Start with the [Quick Start](./framework/ember/quick-start) guide and the Ember-specific [guides](./framework/ember/guides/hotkeys).
+
+Ember currently ships the hotkeys adapter only, so no dedicated Ember devtools package is required.
+
+
+
preact: @tanstack/preact-devtools
diff --git a/docs/overview.md b/docs/overview.md
index 061d8b89..330d123c 100644
--- a/docs/overview.md
+++ b/docs/overview.md
@@ -10,7 +10,7 @@ TanStack Hotkeys is a **type-safe**, **framework-agnostic** library for handling
## Motivation
-On the surface, keyboard shortcuts are a simple concept, and you would think that it should just take a couple of lines of code to implement them. And sometimes, it can be that simple. However, there are enough small "gotchas" that can eventually add up to an annoying amount of complexity when you need to consider multiple keyboard layouts, operating systems, custom shortcuts, conflicting hotkey scopes, properly ignoring input elements, and more.
+On the surface, keyboard shortcuts are a simple concept, and you would think that it should just take a couple of lines of code to implement them. And sometimes, it can be that simple. However, there are enough small "gotchas" that can eventually add up to an annoying amount of complexity when you need to consider multiple keyboard layouts, operating systems, custom shortcuts, conflicting hotkey scopes, properly ignoring input elements, and more.
Surprisingly, in our experience, even AI often struggles to get hotkey management fully correct. We believe that providing a library that brings type-safety and well thought out cross-platform compatibility to hotkey management is a valuable contribution to the community.
@@ -46,9 +46,9 @@ Surprisingly, in our experience, even AI often struggles to get hotkey managemen
- Platform-aware formatting (e.g., `⌘⇧S` on Mac vs `Ctrl+Shift+S` on Windows) for cheatsheet UIs
- **Framework Adapters**
- - React and Preact hooks, Solid primitives, Angular inject APIs, Vue composables, and Lit controllers/decorators
+ - React and Preact hooks, Solid primitives, Angular inject APIs, Vue composables, Lit controllers/decorators, Svelte actions, and Ember helpers
- **Awesome Devtools!**
- See all currently registered hotkeys, held keys, and more in real-time.
-For a complete walkthrough, see the [React Quick Start](framework/react/quick-start), [Angular Quick Start](framework/angular/quick-start), [Vue Quick Start](framework/vue/quick-start) or [Lit Quick Start](framework/lit/quick-start).
+For a complete walkthrough, see the [React Quick Start](framework/react/quick-start), [Angular Quick Start](framework/angular/quick-start), [Vue Quick Start](framework/vue/quick-start), [Lit Quick Start](framework/lit/quick-start) or [Ember Quick Start](framework/ember/quick-start).
diff --git a/knip.json b/knip.json
index 51546bad..e88469f2 100644
--- a/knip.json
+++ b/knip.json
@@ -1,7 +1,7 @@
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"ignoreDependencies": ["@faker-js/faker"],
- "ignoreWorkspaces": ["examples/**"],
+ "ignoreWorkspaces": ["examples/**", "packages/ember-hotkeys"],
"workspaces": {
"packages/react-hotkeys": {
"ignore": []
diff --git a/package.json b/package.json
index ac469b34..cacfcd7e 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,7 @@
"clean": "find . -name 'dist' -type d -prune -exec rm -rf {} +",
"clean:node_modules": "find . -name 'node_modules' -type d -prune -exec rm -rf {} +",
"clean:all": "pnpm run clean && pnpm run clean:node_modules",
- "copy:readme": "cp README.md packages/hotkeys/README.md && cp README.md packages/hotkeys-devtools/README.md && cp README.md packages/angular-hotkeys/README.md && cp README.md packages/react-hotkeys/README.md && cp README.md packages/react-hotkeys-devtools/README.md && cp README.md packages/preact-hotkeys/README.md && cp README.md packages/preact-hotkeys-devtools/README.md && cp README.md packages/solid-hotkeys/README.md && cp README.md packages/solid-hotkeys-devtools/README.md && cp README.md packages/vue-hotkeys/README.md && cp README.md packages/vue-hotkeys-devtools/README.md && cp README.md packages/lit-hotkeys/README.md && cp README.md packages/svelte-hotkeys/README.md",
+ "copy:readme": "cp README.md packages/hotkeys/README.md && cp README.md packages/hotkeys-devtools/README.md && cp README.md packages/angular-hotkeys/README.md && cp README.md packages/react-hotkeys/README.md && cp README.md packages/react-hotkeys-devtools/README.md && cp README.md packages/preact-hotkeys/README.md && cp README.md packages/preact-hotkeys-devtools/README.md && cp README.md packages/solid-hotkeys/README.md && cp README.md packages/solid-hotkeys-devtools/README.md && cp README.md packages/vue-hotkeys/README.md && cp README.md packages/vue-hotkeys-devtools/README.md && cp README.md packages/lit-hotkeys/README.md && cp README.md packages/svelte-hotkeys/README.md && cp README.md packages/ember-hotkeys/README.md",
"dev": "pnpm run watch",
"format": "prettier --experimental-cli --ignore-unknown '**/*' --write",
"generate-docs": "node scripts/generate-docs.ts && pnpm run copy:readme",
@@ -83,6 +83,7 @@
},
"overrides": {
"@tanstack/angular-hotkeys": "workspace:*",
+ "@tanstack/ember-hotkeys": "workspace:*",
"@tanstack/hotkeys": "workspace:*",
"@tanstack/hotkeys-devtools": "workspace:*",
"@tanstack/lit-hotkeys": "workspace:*",
diff --git a/packages/ember-hotkeys/.env.development b/packages/ember-hotkeys/.env.development
new file mode 100644
index 00000000..8380c3a6
--- /dev/null
+++ b/packages/ember-hotkeys/.env.development
@@ -0,0 +1,8 @@
+# This file is committed to git and should not contain any secrets.
+#
+# Vite recommends using .env.local or .env.[mode].local if you need to manage secrets
+# SEE: https://vite.dev/guide/env-and-mode.html#env-files for more information.
+
+
+# Default NODE_ENV with vite build --mode=test is production
+NODE_ENV=development
diff --git a/packages/ember-hotkeys/.gitignore b/packages/ember-hotkeys/.gitignore
new file mode 100644
index 00000000..1d025719
--- /dev/null
+++ b/packages/ember-hotkeys/.gitignore
@@ -0,0 +1,21 @@
+# compiled output
+dist/
+dist-tests/
+declarations/
+
+# from scenarios
+tmp/
+config/optional-features.json
+ember-cli-build.cjs
+
+# npm/pnpm/yarn pack output
+*.tgz
+
+# deps & caches
+node_modules/
+.eslintcache
+.prettiercache
+
+# potentially containing secrets
+*.local
+
diff --git a/packages/ember-hotkeys/.template-lintrc.mjs b/packages/ember-hotkeys/.template-lintrc.mjs
new file mode 100644
index 00000000..8b6625cd
--- /dev/null
+++ b/packages/ember-hotkeys/.template-lintrc.mjs
@@ -0,0 +1,4 @@
+export default {
+ extends: 'recommended',
+ checkHbsTemplateLiterals: false,
+};
diff --git a/packages/ember-hotkeys/README.md b/packages/ember-hotkeys/README.md
new file mode 100644
index 00000000..cb919754
--- /dev/null
+++ b/packages/ember-hotkeys/README.md
@@ -0,0 +1,124 @@
+
+
+### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
+
+
+
+# TanStack Hotkeys
+
+> [!NOTE]
+> TanStack Hotkeys is alpha. We are actively developing the library and are open to feedback and contributions.
+
+Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objects, a cross-platform `Mod` key, a singleton Hotkey Manager, and utilities for cheatsheet UIs—built to stay SSR-friendly.
+
+- Type-safe bindings — template strings (`Mod+Shift+S`, `Escape`) or parsed objects for full control
+- Flexible options — `keydown`/`keyup`, `preventDefault`, `stopPropagation`, conditional enabled, `requireReset`
+- Cross-platform Mod — maps to Cmd on macOS and Ctrl on Windows/Linux
+- Batteries included — validation + matching, sequences (Vim-style), key-state tracking, recorder UI helpers, framework adapters, and devtools
+
+## Read the docs →
+
+
+
+> [!NOTE]
+> You may know **TanStack Hotkeys** by our adapter names, too!
+>
+> - [**React Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/react/react-hotkeys)
+> - [**Preact Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/preact/preact-hotkeys)
+> - [**Solid Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/solid/reference)
+> - [**Angular Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/angular/reference)
+> - [**Vue Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/vue/reference)
+> - [**Lit Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/lit/reference)
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
+> - [**Ember Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/ember/reference)
+
+## Get Involved
+
+- We welcome issues and pull requests!
+- Participate in [GitHub discussions](https://github.com/TanStack/hotkeys/discussions)
+- Chat with the community on [Discord](https://discord.com/invite/WrRKjPJ)
+- See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions
+
+## Partners
+
+
+We're looking for TanStack Hotkeys Partners to join our mission! Partner with us to push the boundaries of TanStack Hotkeys and build amazing things together.
+