diff --git a/README.md b/README.md
index 1c80cd6..8c67395 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,7 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
> - [**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)
-> - Svelte Hotkeys – needs a contributor!
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
## Get Involved
diff --git a/docs/config.json b/docs/config.json
index 544aa64..bf75e84 100644
--- a/docs/config.json
+++ b/docs/config.json
@@ -67,6 +67,15 @@
"to": "framework/vue/quick-start"
}
]
+ },
+ {
+ "label": "svelte",
+ "children": [
+ {
+ "label": "Quick Start",
+ "to": "framework/svelte/quick-start"
+ }
+ ]
}
]
},
@@ -198,6 +207,31 @@
"to": "framework/vue/guides/formatting-display"
}
]
+ },
+ {
+ "label": "svelte",
+ "children": [
+ {
+ "label": "Hotkeys",
+ "to": "framework/svelte/guides/hotkeys"
+ },
+ {
+ "label": "Sequences",
+ "to": "framework/svelte/guides/sequences"
+ },
+ {
+ "label": "Hotkey Recording",
+ "to": "framework/svelte/guides/hotkey-recording"
+ },
+ {
+ "label": "Key State Tracking",
+ "to": "framework/svelte/guides/key-state-tracking"
+ },
+ {
+ "label": "Formatting & Display",
+ "to": "framework/svelte/guides/formatting-display"
+ }
+ ]
}
]
},
@@ -254,6 +288,15 @@
"to": "framework/vue/reference/index"
}
]
+ },
+ {
+ "label": "svelte",
+ "children": [
+ {
+ "label": "Svelte APIs",
+ "to": "framework/svelte/reference/index"
+ }
+ ]
}
]
},
@@ -376,6 +419,51 @@
"to": "framework/vue/reference/interfaces/HotkeysProviderOptions"
}
]
+ },
+ {
+ "label": "svelte",
+ "children": [
+ {
+ "label": "createHotkey",
+ "to": "framework/svelte/reference/functions/createHotkey"
+ },
+ {
+ "label": "createHotkeyAttachment",
+ "to": "framework/svelte/reference/functions/createHotkeyAttachment"
+ },
+ {
+ "label": "CreateHotkeyOptions",
+ "to": "framework/svelte/reference/interfaces/CreateHotkeyOptions"
+ },
+ {
+ "label": "HotkeysProvider",
+ "to": "framework/svelte/reference/variables/HotkeysProvider"
+ },
+ {
+ "label": "HotkeysProviderOptions",
+ "to": "framework/svelte/reference/interfaces/HotkeysProviderOptions"
+ },
+ {
+ "label": "HotkeysProviderProps",
+ "to": "framework/svelte/reference/interfaces/HotkeysProviderProps"
+ },
+ {
+ "label": "DEFAULT_OPTIONS",
+ "to": "framework/svelte/reference/variables/DEFAULT_OPTIONS"
+ },
+ {
+ "label": "getDefaultHotkeysOptions",
+ "to": "framework/svelte/reference/functions/getDefaultHotkeysOptions"
+ },
+ {
+ "label": "getHotkeysContext",
+ "to": "framework/svelte/reference/functions/getHotkeysContext"
+ },
+ {
+ "label": "setHotkeysContext",
+ "to": "framework/svelte/reference/functions/setHotkeysContext"
+ }
+ ]
}
]
},
@@ -470,6 +558,23 @@
"to": "framework/vue/reference/interfaces/UseHotkeySequenceOptions"
}
]
+ },
+ {
+ "label": "svelte",
+ "children": [
+ {
+ "label": "createHotkeySequence",
+ "to": "framework/svelte/reference/functions/createHotkeySequence"
+ },
+ {
+ "label": "createHotkeySequenceAttachment",
+ "to": "framework/svelte/reference/functions/createHotkeySequenceAttachment"
+ },
+ {
+ "label": "CreateHotkeySequenceOptions",
+ "to": "framework/svelte/reference/interfaces/CreateHotkeySequenceOptions"
+ }
+ ]
}
]
},
@@ -540,6 +645,19 @@
"to": "framework/vue/reference/functions/useKeyHold"
}
]
+ },
+ {
+ "label": "svelte",
+ "children": [
+ {
+ "label": "getIsKeyHeld",
+ "to": "framework/svelte/reference/functions/getIsKeyHeld"
+ },
+ {
+ "label": "SvelteHeldKeyState",
+ "to": "framework/svelte/reference/interfaces/SvelteHeldKeyState"
+ }
+ ]
}
]
},
@@ -630,6 +748,27 @@
"to": "framework/vue/reference/functions/useHeldKeyCodes"
}
]
+ },
+ {
+ "label": "svelte",
+ "children": [
+ {
+ "label": "getHeldKeys",
+ "to": "framework/svelte/reference/functions/getHeldKeys"
+ },
+ {
+ "label": "SvelteHeldKeys",
+ "to": "framework/svelte/reference/interfaces/SvelteHeldKeys"
+ },
+ {
+ "label": "getHeldKeyCodesMap",
+ "to": "framework/svelte/reference/functions/getHeldKeyCodesMap"
+ },
+ {
+ "label": "SvelteHeldKeyCodesMap",
+ "to": "framework/svelte/reference/interfaces/SvelteHeldKeyCodesMap"
+ }
+ ]
}
]
},
@@ -716,6 +855,19 @@
"to": "framework/vue/reference/interfaces/VueHotkeyRecorder"
}
]
+ },
+ {
+ "label": "svelte",
+ "children": [
+ {
+ "label": "createHotkeyRecorder",
+ "to": "framework/svelte/reference/functions/createHotkeyRecorder"
+ },
+ {
+ "label": "SvelteHotkeyRecorder",
+ "to": "framework/svelte/reference/interfaces/SvelteHotkeyRecorder"
+ }
+ ]
}
]
},
@@ -906,6 +1058,31 @@
"to": "framework/vue/examples/useKeyhold"
}
]
+ },
+ {
+ "label": "svelte",
+ "children": [
+ {
+ "label": "createHotkey",
+ "to": "framework/svelte/examples/create-hotkey"
+ },
+ {
+ "label": "createHotkeySequence",
+ "to": "framework/svelte/examples/create-hotkey-sequence"
+ },
+ {
+ "label": "createHotkeyRecorder",
+ "to": "framework/svelte/examples/create-hotkey-recorder"
+ },
+ {
+ "label": "getHeldKeys",
+ "to": "framework/svelte/examples/get-held-keys"
+ },
+ {
+ "label": "getIsKeyHeld",
+ "to": "framework/svelte/examples/get-is-key-held"
+ }
+ ]
}
]
}
diff --git a/docs/framework/svelte/guides/formatting-display.md b/docs/framework/svelte/guides/formatting-display.md
new file mode 100644
index 0000000..f13d857
--- /dev/null
+++ b/docs/framework/svelte/guides/formatting-display.md
@@ -0,0 +1,70 @@
+---
+title: Formatting & Display Guide
+id: formatting-display
+---
+
+TanStack Hotkeys includes utilities for turning hotkey strings into display-friendly labels. These utilities are framework-agnostic, but they pair naturally with Svelte templates and reactive UI.
+
+## `formatForDisplay`
+
+```ts
+import { formatForDisplay } from '@tanstack/svelte-hotkeys'
+
+formatForDisplay('Mod+S')
+formatForDisplay('Mod+Shift+Z')
+```
+
+## `formatWithLabels`
+
+```ts
+import { formatWithLabels } from '@tanstack/svelte-hotkeys'
+
+formatWithLabels('Mod+S')
+formatWithLabels('Mod+Shift+Z')
+```
+
+## `formatKeyForDebuggingDisplay`
+
+```ts
+import { formatKeyForDebuggingDisplay } from '@tanstack/svelte-hotkeys'
+
+formatKeyForDebuggingDisplay('Meta')
+formatKeyForDebuggingDisplay('Shift')
+```
+
+## Using Formatted Hotkeys in Svelte
+
+### Keyboard Shortcut Badges
+
+```svelte
+
+
+{formatForDisplay(hotkey)}
+```
+
+### Menu Items with Hotkeys
+
+```svelte
+
+
+
+```
+
+## Validation
+
+```ts
+import { validateHotkey } from '@tanstack/svelte-hotkeys'
+
+const result = validateHotkey('Alt+A')
+```
diff --git a/docs/framework/svelte/guides/hotkey-recording.md b/docs/framework/svelte/guides/hotkey-recording.md
new file mode 100644
index 0000000..a143859
--- /dev/null
+++ b/docs/framework/svelte/guides/hotkey-recording.md
@@ -0,0 +1,78 @@
+---
+title: Hotkey Recording Guide
+id: hotkey-recording
+---
+
+TanStack Hotkeys provides the `createHotkeyRecorder` function for building shortcut customization UIs in Svelte.
+
+## Basic Usage
+
+```svelte
+
+
+
+
+ {recorder.isRecording
+ ? 'Press a key combination...'
+ : recorder.recordedHotkey
+ ? formatForDisplay(recorder.recordedHotkey)
+ : 'Click to record'}
+
+ {#if recorder.isRecording}
+ recorder.cancelRecording()}>Cancel
+ {/if}
+
+```
+
+## Return Value
+
+- `isRecording`: whether recording is active
+- `recordedHotkey`: the most recently recorded hotkey
+- `startRecording()`: start listening for key presses
+- `stopRecording()`: stop listening and keep the current recording
+- `cancelRecording()`: stop listening and discard the in-progress recording
+
+## Options
+
+```ts
+createHotkeyRecorder({
+ onRecord: (hotkey) => {},
+ onCancel: () => {},
+ onClear: () => {},
+})
+```
+
+Options can also be reactive:
+
+```svelte
+
+```
+
+## Recording Behavior
+
+- Modifier-only presses do not complete a recording.
+- Modifier plus key combinations record the full shortcut.
+- Escape cancels recording.
+- Backspace and Delete clear the shortcut.
+- Recorded values are normalized to portable `Mod` format.
diff --git a/docs/framework/svelte/guides/hotkeys.md b/docs/framework/svelte/guides/hotkeys.md
new file mode 100644
index 0000000..88c4a7c
--- /dev/null
+++ b/docs/framework/svelte/guides/hotkeys.md
@@ -0,0 +1,138 @@
+---
+title: Hotkeys Guide
+id: hotkeys
+---
+
+Use `createHotkey` for global shortcuts and `createHotkeyAttachment` for element-scoped shortcuts. This keeps the common global case simple while making scoped behavior feel native to Svelte 5.
+
+## Global hotkeys
+
+```svelte
+
+```
+
+The callback receives the original `KeyboardEvent` as the first argument and a `HotkeyCallbackContext` as the second:
+
+```ts
+createHotkey('Mod+S', (event, context) => {
+ console.log(context.hotkey)
+ console.log(context.parsedHotkey)
+})
+```
+
+## Scoped hotkeys
+
+Use attachments instead of capturing an element ref just to pass it back into the API.
+
+```svelte
+
+
+Panel content
+```
+
+## Reactive inputs
+
+Hotkeys can take plain values for static registrations or getter functions when the hotkey or options depend on reactive state.
+
+### Reactive `enabled`
+
+```svelte
+
+```
+
+### Reactive hotkey values
+
+```svelte
+
+```
+
+## Default options
+
+Set defaults explicitly with `setHotkeysContext` when a subtree needs shared behavior:
+
+```svelte
+
+```
+
+## Common Options
+
+### `requireReset`
+
+```ts
+createHotkey('Escape', () => closePanel(), { requireReset: true })
+```
+
+### `ignoreInputs`
+
+```ts
+createHotkey('K', () => openSearch())
+createHotkey('Enter', () => submit(), { ignoreInputs: false })
+```
+
+### `conflictBehavior`
+
+```ts
+createHotkey('Mod+S', () => save(), { conflictBehavior: 'replace' })
+```
+
+### `platform`
+
+```ts
+createHotkey('Mod+S', () => save(), { platform: 'mac' })
+```
+
+## Automatic Cleanup
+
+Global hotkeys are automatically unregistered when the owning component unmounts. Attachment-based hotkeys clean themselves up when the attached element is removed or when reactive inputs change.
+
+## The Hotkey Manager
+
+You can always reach for the underlying manager directly:
+
+```ts
+import { getHotkeyManager } from '@tanstack/svelte-hotkeys'
+
+const manager = getHotkeyManager()
+manager.isRegistered('Mod+S')
+manager.getRegistrationCount()
+```
diff --git a/docs/framework/svelte/guides/key-state-tracking.md b/docs/framework/svelte/guides/key-state-tracking.md
new file mode 100644
index 0000000..0aedcc0
--- /dev/null
+++ b/docs/framework/svelte/guides/key-state-tracking.md
@@ -0,0 +1,98 @@
+---
+title: Key State Tracking Guide
+id: key-state-tracking
+---
+
+TanStack Hotkeys provides three Svelte functions for tracking live keyboard state: `getHeldKeys`, `getHeldKeyCodesMap`, and `getIsKeyHeld`.
+
+## `getHeldKeys`
+
+```svelte
+
+
+{heldKeys.keys.length > 0 ? heldKeys.keys.join(' + ') : 'No keys held'}
+```
+
+## `getHeldKeyCodesMap`
+
+```svelte
+
+
+{JSON.stringify(heldCodes.codes, null, 2)}
+```
+
+## `getIsKeyHeld`
+
+```svelte
+
+
+Shift
+```
+
+## Common Patterns
+
+### Hold-to-Reveal UI
+
+```svelte
+
+
+{#if isShiftHeld.held}
+ Permanently Delete
+{:else}
+ Move to Trash
+{/if}
+```
+
+### Debugging Key Display
+
+```svelte
+
+
+
+ {#each heldKeys.keys as key}
+
+ {formatKeyForDebuggingDisplay(key)}:
+ {heldCodes.codes[key]
+ ? formatKeyForDebuggingDisplay(heldCodes.codes[key], { source: 'code' })
+ : 'unknown'}
+
+ {/each}
+
+```
+
+## Under the Hood
+
+All three functions subscribe to the singleton `KeyStateTracker`:
+
+```ts
+import { getKeyStateTracker } from '@tanstack/svelte-hotkeys'
+
+const tracker = getKeyStateTracker()
+tracker.getHeldKeys()
+tracker.isKeyHeld('Shift')
+```
diff --git a/docs/framework/svelte/guides/sequences.md b/docs/framework/svelte/guides/sequences.md
new file mode 100644
index 0000000..2211644
--- /dev/null
+++ b/docs/framework/svelte/guides/sequences.md
@@ -0,0 +1,109 @@
+---
+title: Sequences Guide
+id: sequences
+---
+
+TanStack Hotkeys supports multi-key sequences in Svelte, where keys are pressed one after another rather than simultaneously.
+
+## Global sequences
+
+```svelte
+
+```
+
+## Scoped sequences
+
+Use `createHotkeySequenceAttachment` when a sequence should only be active while a specific element owns focus.
+
+```svelte
+
+
+
+ Focus here, then press g then g
+
+```
+
+## Sequence options
+
+```ts
+createHotkeySequence(['G', 'G'], callback, {
+ timeout: 1000,
+ enabled: true,
+})
+```
+
+### Reactive `enabled`
+
+```svelte
+
+```
+
+## Default options
+
+```svelte
+
+```
+
+## Common Patterns
+
+### Vim-Style Navigation
+
+```ts
+createHotkeySequence(['G', 'G'], () => scrollToTop())
+createHotkeySequence(['G', 'Shift+G'], () => scrollToBottom())
+createHotkeySequence(['D', 'D'], () => deleteLine())
+createHotkeySequence(['D', 'W'], () => deleteWord())
+createHotkeySequence(['C', 'I', 'W'], () => changeInnerWord())
+```
+
+### Konami Code
+
+```ts
+createHotkeySequence(
+ ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'B', 'A'],
+ () => enableEasterEgg(),
+ { timeout: 2000 },
+)
+```
+
+## Under the Hood
+
+`createHotkeySequence` uses the singleton `SequenceManager`. You can also access it directly:
+
+```ts
+import {
+ createSequenceMatcher,
+ getSequenceManager,
+} from '@tanstack/svelte-hotkeys'
+
+const manager = getSequenceManager()
+const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 })
+```
diff --git a/docs/framework/svelte/quick-start.md b/docs/framework/svelte/quick-start.md
new file mode 100644
index 0000000..44290bc
--- /dev/null
+++ b/docs/framework/svelte/quick-start.md
@@ -0,0 +1,170 @@
+---
+title: Quick Start
+id: quick-start
+---
+
+## Installation
+
+Don't have TanStack Hotkeys installed yet? See the [Installation](../../installation) page for instructions.
+
+## Your First Hotkey
+
+Use `createHotkey` for global shortcuts and attachments for element-scoped shortcuts.
+
+```svelte
+
+
+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 global hotkeys
+
+```svelte
+
+```
+
+### Scoped hotkeys with attachments
+
+```svelte
+
+
+
+
Press Escape while focused here to close
+
+```
+
+### Reactive options
+
+```svelte
+
+```
+
+### Scoped sequences
+
+```svelte
+
+
+
+ Focus here, then press g then g
+
+```
+
+### Tracking held keys
+
+```svelte
+
+
+
+ {#if isShiftHeld.held}Shift mode active {/if}
+ {#if heldKeys.keys.length > 0}
+ Keys: {heldKeys.keys.join('+')}
+ {/if}
+
+```
+
+### Recording shortcuts
+
+```svelte
+
+
+
+ {recorder.recordedHotkey
+ ? formatForDisplay(recorder.recordedHotkey)
+ : 'Click to record'}
+
+```
+
+### Displaying hotkeys in the UI
+
+```svelte
+
+
+
+ Save {formatForDisplay('Mod+S')}
+
+```
+
+## Default options
+
+Use `setHotkeysContext` when you want defaults for a subtree. This is an advanced API and usually belongs near the root of the part of the app that owns the hotkeys.
+
+```svelte
+
+```
+
+## Next Steps
+
+- [Hotkeys Guide](./guides/hotkeys)
+- [Sequences Guide](./guides/sequences)
+- [Hotkey Recording Guide](./guides/hotkey-recording)
+- [Key State Tracking Guide](./guides/key-state-tracking)
+- [Formatting & Display Guide](./guides/formatting-display)
diff --git a/docs/framework/svelte/reference/functions/createHotkey.md b/docs/framework/svelte/reference/functions/createHotkey.md
new file mode 100644
index 0000000..323583c
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/createHotkey.md
@@ -0,0 +1,47 @@
+---
+id: createHotkey
+title: createHotkey
+---
+
+# Function: createHotkey()
+
+```ts
+function createHotkey(
+ hotkey,
+ callback,
+ options): void;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:70](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L70)
+
+Register a global hotkey for the current component.
+
+## Parameters
+
+### hotkey
+
+`MaybeGetter`\<`RegisterableHotkey`\>
+
+### callback
+
+`HotkeyCallback`
+
+### options
+
+`MaybeGetter`\<[`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md)\> = `{}`
+
+## Returns
+
+`void`
+
+## Example
+
+```svelte
+
+```
diff --git a/docs/framework/svelte/reference/functions/createHotkeyAttachment.md b/docs/framework/svelte/reference/functions/createHotkeyAttachment.md
new file mode 100644
index 0000000..2b27ca3
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/createHotkeyAttachment.md
@@ -0,0 +1,53 @@
+---
+id: createHotkeyAttachment
+title: createHotkeyAttachment
+---
+
+# Function: createHotkeyAttachment()
+
+```ts
+function createHotkeyAttachment(
+ hotkey,
+ callback,
+options): Attachment;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:108](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L108)
+
+Create an attachment for element-scoped hotkeys.
+
+## Parameters
+
+### hotkey
+
+`MaybeGetter`\<`RegisterableHotkey`\>
+
+### callback
+
+`HotkeyCallback`
+
+### options
+
+`MaybeGetter`\<[`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md)\> = `{}`
+
+## Returns
+
+`Attachment`\<`HTMLElement`\>
+
+## Example
+
+```svelte
+
+
+
+ Count: {count}
+
+```
diff --git a/docs/framework/svelte/reference/functions/createHotkeyRecorder.md b/docs/framework/svelte/reference/functions/createHotkeyRecorder.md
new file mode 100644
index 0000000..41e3db2
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/createHotkeyRecorder.md
@@ -0,0 +1,22 @@
+---
+id: createHotkeyRecorder
+title: createHotkeyRecorder
+---
+
+# Function: createHotkeyRecorder()
+
+```ts
+function createHotkeyRecorder(options): SvelteHotkeyRecorder;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:98](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L98)
+
+## Parameters
+
+### options
+
+`MaybeGetter`\<`HotkeyRecorderOptions`\>
+
+## Returns
+
+[`SvelteHotkeyRecorder`](../interfaces/SvelteHotkeyRecorder.md)
diff --git a/docs/framework/svelte/reference/functions/createHotkeySequence.md b/docs/framework/svelte/reference/functions/createHotkeySequence.md
new file mode 100644
index 0000000..0fb3c55
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/createHotkeySequence.md
@@ -0,0 +1,62 @@
+---
+id: createHotkeySequence
+title: createHotkeySequence
+---
+
+# Function: createHotkeySequence()
+
+```ts
+function createHotkeySequence(
+ sequence,
+ callback,
+ options): void;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts:67](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts#L67)
+
+Register a global keyboard shortcut sequence for the current component.
+
+## Parameters
+
+### sequence
+
+`MaybeGetter`\<`HotkeySequence`\>
+
+### callback
+
+`HotkeyCallback`
+
+### options
+
+`MaybeGetter`\<[`CreateHotkeySequenceOptions`](../interfaces/CreateHotkeySequenceOptions.md)\> = `{}`
+
+## Returns
+
+`void`
+
+## Example
+
+```svelte
+
+
+
+ ....
+
+```
diff --git a/docs/framework/svelte/reference/functions/createHotkeySequenceAttachment.md b/docs/framework/svelte/reference/functions/createHotkeySequenceAttachment.md
new file mode 100644
index 0000000..69a28c8
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/createHotkeySequenceAttachment.md
@@ -0,0 +1,51 @@
+---
+id: createHotkeySequenceAttachment
+title: createHotkeySequenceAttachment
+---
+
+# Function: createHotkeySequenceAttachment()
+
+```ts
+function createHotkeySequenceAttachment(
+ sequence,
+ callback,
+options): Attachment;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts:113](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts#L113)
+
+Create an attachment for element-scoped keyboard sequences.
+
+## Parameters
+
+### sequence
+
+`MaybeGetter`\<`HotkeySequence`\>
+
+### callback
+
+`HotkeyCallback`
+
+### options
+
+`MaybeGetter`\<[`CreateHotkeySequenceOptions`](../interfaces/CreateHotkeySequenceOptions.md)\> = `{}`
+
+## Returns
+
+`Attachment`\<`HTMLElement`\>
+
+## Example
+
+```svelte
+
+
+
+ Focus here and press g then g
+
+```
diff --git a/docs/framework/svelte/reference/functions/getDefaultHotkeysOptions.md b/docs/framework/svelte/reference/functions/getDefaultHotkeysOptions.md
new file mode 100644
index 0000000..e3db423
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/getDefaultHotkeysOptions.md
@@ -0,0 +1,16 @@
+---
+id: getDefaultHotkeysOptions
+title: getDefaultHotkeysOptions
+---
+
+# Function: getDefaultHotkeysOptions()
+
+```ts
+function getDefaultHotkeysOptions(): HotkeysProviderOptions;
+```
+
+Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L55)
+
+## Returns
+
+[`HotkeysProviderOptions`](../interfaces/HotkeysProviderOptions.md)
diff --git a/docs/framework/svelte/reference/functions/getHeldKeyCodesMap.md b/docs/framework/svelte/reference/functions/getHeldKeyCodesMap.md
new file mode 100644
index 0000000..11767fc
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/getHeldKeyCodesMap.md
@@ -0,0 +1,39 @@
+---
+id: getHeldKeyCodesMap
+title: getHeldKeyCodesMap
+---
+
+# Function: getHeldKeyCodesMap()
+
+```ts
+function getHeldKeyCodesMap(): SvelteHeldKeyCodesMap;
+```
+
+Defined in: [packages/svelte-hotkeys/src/getHeldKeyCodesMap.svelte.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHeldKeyCodesMap.svelte.ts#L42)
+
+Svelte function that returns reactive access to the map of currently held key names
+to their physical `event.code` values.
+
+This is useful for debugging which physical key was pressed (e.g. distinguishing
+left vs right Shift via "ShiftLeft" / "ShiftRight").
+
+## Returns
+
+[`SvelteHeldKeyCodesMap`](../interfaces/SvelteHeldKeyCodesMap.md)
+
+Object with a reactive `codes` property
+
+```svelte
+
+
+
+ {#each Object.entries(heldKeyCodesMap.codes) as [key, code]}
+
+ {key} {code}
+
+ {/each}
+
+```
diff --git a/docs/framework/svelte/reference/functions/getHeldKeys.md b/docs/framework/svelte/reference/functions/getHeldKeys.md
new file mode 100644
index 0000000..a50cd94
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/getHeldKeys.md
@@ -0,0 +1,36 @@
+---
+id: getHeldKeys
+title: getHeldKeys
+---
+
+# Function: getHeldKeys()
+
+```ts
+function getHeldKeys(): SvelteHeldKeys;
+```
+
+Defined in: [packages/svelte-hotkeys/src/getHeldKeys.svelte.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHeldKeys.svelte.ts#L38)
+
+Svelte function that returns reactive access to currently held keyboard keys.
+
+This function uses the global KeyStateTracker and updates whenever keys are pressed
+or released.
+
+## Returns
+
+[`SvelteHeldKeys`](../interfaces/SvelteHeldKeys.md)
+
+Object with a reactive `keys` property
+
+## Example
+
+```svelte
+
+
+ Currently pressed: {heldKeys.keys.join(' + ') || 'None'}
+
+```
diff --git a/docs/framework/svelte/reference/functions/getHotkeysContext.md b/docs/framework/svelte/reference/functions/getHotkeysContext.md
new file mode 100644
index 0000000..bcefd64
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/getHotkeysContext.md
@@ -0,0 +1,16 @@
+---
+id: getHotkeysContext
+title: getHotkeysContext
+---
+
+# Function: getHotkeysContext()
+
+```ts
+function getHotkeysContext(): HotkeysContextValue | null;
+```
+
+Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:47](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L47)
+
+## Returns
+
+`HotkeysContextValue` \| `null`
diff --git a/docs/framework/svelte/reference/functions/getIsKeyHeld.md b/docs/framework/svelte/reference/functions/getIsKeyHeld.md
new file mode 100644
index 0000000..5bf281a
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/getIsKeyHeld.md
@@ -0,0 +1,61 @@
+---
+id: getIsKeyHeld
+title: getIsKeyHeld
+---
+
+# Function: getIsKeyHeld()
+
+```ts
+function getIsKeyHeld(key): SvelteHeldKeyState;
+```
+
+Defined in: [packages/svelte-hotkeys/src/getIsKeyHeld.svelte.ts:66](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getIsKeyHeld.svelte.ts#L66)
+
+Svelte function that returns reactive access to whether a specific key is currently being held.
+
+This function uses the global KeyStateTracker and updates whenever keys are pressed
+or released.
+
+## Parameters
+
+### key
+
+`HeldKey`
+
+The key to check (e.g., 'Shift', 'Control', 'A')
+
+## Returns
+
+[`SvelteHeldKeyState`](../interfaces/SvelteHeldKeyState.md)
+
+Object with a reactive `held` property
+
+## Examples
+
+```svelte
+
+
+
+ {isShiftHeld.held ? 'Shift is pressed!' : 'Press Shift'}
+
+```
+
+```svelte
+
+
+
+ Ctrl
+ Shift
+ Alt
+
+```
diff --git a/docs/framework/svelte/reference/functions/setHotkeysContext.md b/docs/framework/svelte/reference/functions/setHotkeysContext.md
new file mode 100644
index 0000000..a778858
--- /dev/null
+++ b/docs/framework/svelte/reference/functions/setHotkeysContext.md
@@ -0,0 +1,22 @@
+---
+id: setHotkeysContext
+title: setHotkeysContext
+---
+
+# Function: setHotkeysContext()
+
+```ts
+function setHotkeysContext(defaultOptions): HotkeysContextValue;
+```
+
+Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L27)
+
+## Parameters
+
+### defaultOptions
+
+[`HotkeysProviderOptions`](../interfaces/HotkeysProviderOptions.md) = `DEFAULT_OPTIONS`
+
+## Returns
+
+`HotkeysContextValue`
diff --git a/docs/framework/svelte/reference/index.md b/docs/framework/svelte/reference/index.md
new file mode 100644
index 0000000..d9e09c8
--- /dev/null
+++ b/docs/framework/svelte/reference/index.md
@@ -0,0 +1,40 @@
+---
+id: "@tanstack/svelte-hotkeys"
+title: "@tanstack/svelte-hotkeys"
+---
+
+# @tanstack/svelte-hotkeys
+
+## Interfaces
+
+- [CreateHotkeyOptions](interfaces/CreateHotkeyOptions.md)
+- [CreateHotkeySequenceOptions](interfaces/CreateHotkeySequenceOptions.md)
+- [HotkeysProviderOptions](interfaces/HotkeysProviderOptions.md)
+- [HotkeysProviderProps](interfaces/HotkeysProviderProps.md)
+- [SvelteHeldKeyCodesMap](interfaces/SvelteHeldKeyCodesMap.md)
+- [SvelteHeldKeys](interfaces/SvelteHeldKeys.md)
+- [SvelteHeldKeyState](interfaces/SvelteHeldKeyState.md)
+- [SvelteHotkeyRecorder](interfaces/SvelteHotkeyRecorder.md)
+
+## Type Aliases
+
+- [HotkeysProvider](type-aliases/HotkeysProvider.md)
+
+## Variables
+
+- [DEFAULT\_OPTIONS](variables/DEFAULT_OPTIONS.md)
+- [HotkeysProvider](variables/HotkeysProvider.md)
+
+## Functions
+
+- [createHotkey](functions/createHotkey.md)
+- [createHotkeyAttachment](functions/createHotkeyAttachment.md)
+- [createHotkeyRecorder](functions/createHotkeyRecorder.md)
+- [createHotkeySequence](functions/createHotkeySequence.md)
+- [createHotkeySequenceAttachment](functions/createHotkeySequenceAttachment.md)
+- [getDefaultHotkeysOptions](functions/getDefaultHotkeysOptions.md)
+- [getHeldKeyCodesMap](functions/getHeldKeyCodesMap.md)
+- [getHeldKeys](functions/getHeldKeys.md)
+- [getHotkeysContext](functions/getHotkeysContext.md)
+- [getIsKeyHeld](functions/getIsKeyHeld.md)
+- [setHotkeysContext](functions/setHotkeysContext.md)
diff --git a/docs/framework/svelte/reference/interfaces/CreateHotkeyOptions.md b/docs/framework/svelte/reference/interfaces/CreateHotkeyOptions.md
new file mode 100644
index 0000000..142abc9
--- /dev/null
+++ b/docs/framework/svelte/reference/interfaces/CreateHotkeyOptions.md
@@ -0,0 +1,22 @@
+---
+id: CreateHotkeyOptions
+title: CreateHotkeyOptions
+---
+
+# Interface: CreateHotkeyOptions
+
+Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L18)
+
+## Extends
+
+- `Omit`\<`HotkeyOptions`, `"target"`\>
+
+## Properties
+
+### target?
+
+```ts
+optional target: Document | Window;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L19)
diff --git a/docs/framework/svelte/reference/interfaces/CreateHotkeySequenceOptions.md b/docs/framework/svelte/reference/interfaces/CreateHotkeySequenceOptions.md
new file mode 100644
index 0000000..e25631a
--- /dev/null
+++ b/docs/framework/svelte/reference/interfaces/CreateHotkeySequenceOptions.md
@@ -0,0 +1,22 @@
+---
+id: CreateHotkeySequenceOptions
+title: CreateHotkeySequenceOptions
+---
+
+# Interface: CreateHotkeySequenceOptions
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts#L12)
+
+## Extends
+
+- `Omit`\<`SequenceOptions`, `"target"`\>
+
+## Properties
+
+### target?
+
+```ts
+optional target: Document | Window;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts#L16)
diff --git a/docs/framework/svelte/reference/interfaces/HotkeysProviderOptions.md b/docs/framework/svelte/reference/interfaces/HotkeysProviderOptions.md
new file mode 100644
index 0000000..4716f04
--- /dev/null
+++ b/docs/framework/svelte/reference/interfaces/HotkeysProviderOptions.md
@@ -0,0 +1,38 @@
+---
+id: HotkeysProviderOptions
+title: HotkeysProviderOptions
+---
+
+# Interface: HotkeysProviderOptions
+
+Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:7](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L7)
+
+## Properties
+
+### hotkey?
+
+```ts
+optional hotkey: Partial;
+```
+
+Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:8](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L8)
+
+***
+
+### hotkeyRecorder?
+
+```ts
+optional hotkeyRecorder: Partial;
+```
+
+Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:9](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L9)
+
+***
+
+### hotkeySequence?
+
+```ts
+optional hotkeySequence: Partial;
+```
+
+Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L10)
diff --git a/docs/framework/svelte/reference/interfaces/HotkeysProviderProps.md b/docs/framework/svelte/reference/interfaces/HotkeysProviderProps.md
new file mode 100644
index 0000000..6a02782
--- /dev/null
+++ b/docs/framework/svelte/reference/interfaces/HotkeysProviderProps.md
@@ -0,0 +1,28 @@
+---
+id: HotkeysProviderProps
+title: HotkeysProviderProps
+---
+
+# Interface: HotkeysProviderProps
+
+Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L13)
+
+## Properties
+
+### children
+
+```ts
+children: Snippet;
+```
+
+Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L14)
+
+***
+
+### defaultOptions?
+
+```ts
+optional defaultOptions: HotkeysProviderOptions;
+```
+
+Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L15)
diff --git a/docs/framework/svelte/reference/interfaces/SvelteHeldKeyCodesMap.md b/docs/framework/svelte/reference/interfaces/SvelteHeldKeyCodesMap.md
new file mode 100644
index 0000000..dfa7564
--- /dev/null
+++ b/docs/framework/svelte/reference/interfaces/SvelteHeldKeyCodesMap.md
@@ -0,0 +1,18 @@
+---
+id: SvelteHeldKeyCodesMap
+title: SvelteHeldKeyCodesMap
+---
+
+# Interface: SvelteHeldKeyCodesMap
+
+Defined in: [packages/svelte-hotkeys/src/getHeldKeyCodesMap.svelte.ts:4](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHeldKeyCodesMap.svelte.ts#L4)
+
+## Properties
+
+### codes
+
+```ts
+readonly codes: Record;
+```
+
+Defined in: [packages/svelte-hotkeys/src/getHeldKeyCodesMap.svelte.ts:5](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHeldKeyCodesMap.svelte.ts#L5)
diff --git a/docs/framework/svelte/reference/interfaces/SvelteHeldKeyState.md b/docs/framework/svelte/reference/interfaces/SvelteHeldKeyState.md
new file mode 100644
index 0000000..31ecd19
--- /dev/null
+++ b/docs/framework/svelte/reference/interfaces/SvelteHeldKeyState.md
@@ -0,0 +1,18 @@
+---
+id: SvelteHeldKeyState
+title: SvelteHeldKeyState
+---
+
+# Interface: SvelteHeldKeyState
+
+Defined in: [packages/svelte-hotkeys/src/getIsKeyHeld.svelte.ts:5](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getIsKeyHeld.svelte.ts#L5)
+
+## Properties
+
+### held
+
+```ts
+readonly held: boolean;
+```
+
+Defined in: [packages/svelte-hotkeys/src/getIsKeyHeld.svelte.ts:6](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getIsKeyHeld.svelte.ts#L6)
diff --git a/docs/framework/svelte/reference/interfaces/SvelteHeldKeys.md b/docs/framework/svelte/reference/interfaces/SvelteHeldKeys.md
new file mode 100644
index 0000000..259e130
--- /dev/null
+++ b/docs/framework/svelte/reference/interfaces/SvelteHeldKeys.md
@@ -0,0 +1,18 @@
+---
+id: SvelteHeldKeys
+title: SvelteHeldKeys
+---
+
+# Interface: SvelteHeldKeys
+
+Defined in: [packages/svelte-hotkeys/src/getHeldKeys.svelte.ts:4](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHeldKeys.svelte.ts#L4)
+
+## Properties
+
+### keys
+
+```ts
+readonly keys: string[];
+```
+
+Defined in: [packages/svelte-hotkeys/src/getHeldKeys.svelte.ts:5](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHeldKeys.svelte.ts#L5)
diff --git a/docs/framework/svelte/reference/interfaces/SvelteHotkeyRecorder.md b/docs/framework/svelte/reference/interfaces/SvelteHotkeyRecorder.md
new file mode 100644
index 0000000..a2e7f44
--- /dev/null
+++ b/docs/framework/svelte/reference/interfaces/SvelteHotkeyRecorder.md
@@ -0,0 +1,80 @@
+---
+id: SvelteHotkeyRecorder
+title: SvelteHotkeyRecorder
+---
+
+# Interface: SvelteHotkeyRecorder
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:8](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L8)
+
+## Properties
+
+### cancelRecording()
+
+```ts
+cancelRecording: () => void;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L18)
+
+Cancel recording without saving
+
+#### Returns
+
+`void`
+
+***
+
+### isRecording
+
+```ts
+readonly isRecording: boolean;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L10)
+
+Whether recording is currently active
+
+***
+
+### recordedHotkey
+
+```ts
+readonly recordedHotkey: Hotkey | null;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L12)
+
+The currently recorded hotkey (for live preview)
+
+***
+
+### startRecording()
+
+```ts
+startRecording: () => void;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L14)
+
+Start recording a new hotkey
+
+#### Returns
+
+`void`
+
+***
+
+### stopRecording()
+
+```ts
+stopRecording: () => void;
+```
+
+Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L16)
+
+Stop recording (same as cancel)
+
+#### Returns
+
+`void`
diff --git a/docs/framework/svelte/reference/type-aliases/HotkeysProvider.md b/docs/framework/svelte/reference/type-aliases/HotkeysProvider.md
new file mode 100644
index 0000000..a35a88d
--- /dev/null
+++ b/docs/framework/svelte/reference/type-aliases/HotkeysProvider.md
@@ -0,0 +1,12 @@
+---
+id: HotkeysProvider
+title: HotkeysProvider
+---
+
+# Type Alias: HotkeysProvider
+
+```ts
+type HotkeysProvider = SvelteComponent;
+```
+
+Defined in: node\_modules/.pnpm/svelte@5.53.7/node\_modules/svelte/types/index.d.ts:3195
diff --git a/docs/framework/svelte/reference/variables/DEFAULT_OPTIONS.md b/docs/framework/svelte/reference/variables/DEFAULT_OPTIONS.md
new file mode 100644
index 0000000..0d1171c
--- /dev/null
+++ b/docs/framework/svelte/reference/variables/DEFAULT_OPTIONS.md
@@ -0,0 +1,12 @@
+---
+id: DEFAULT_OPTIONS
+title: DEFAULT_OPTIONS
+---
+
+# Variable: DEFAULT\_OPTIONS
+
+```ts
+const DEFAULT_OPTIONS: HotkeysProviderOptions = {};
+```
+
+Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L18)
diff --git a/docs/framework/svelte/reference/variables/HotkeysProvider.md b/docs/framework/svelte/reference/variables/HotkeysProvider.md
new file mode 100644
index 0000000..bc71bdd
--- /dev/null
+++ b/docs/framework/svelte/reference/variables/HotkeysProvider.md
@@ -0,0 +1,12 @@
+---
+id: HotkeysProvider
+title: HotkeysProvider
+---
+
+# Variable: HotkeysProvider
+
+```ts
+const HotkeysProvider: LegacyComponentType;
+```
+
+Defined in: node\_modules/.pnpm/svelte@5.53.7/node\_modules/svelte/types/index.d.ts:3195
diff --git a/examples/svelte/create-hotkey-recorder/.gitignore b/examples/svelte/create-hotkey-recorder/.gitignore
new file mode 100644
index 0000000..3b462cb
--- /dev/null
+++ b/examples/svelte/create-hotkey-recorder/.gitignore
@@ -0,0 +1,23 @@
+node_modules
+
+# Output
+.output
+.vercel
+.netlify
+.wrangler
+/.svelte-kit
+/build
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Env
+.env
+.env.*
+!.env.example
+!.env.test
+
+# Vite
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
diff --git a/examples/svelte/create-hotkey-recorder/.npmrc b/examples/svelte/create-hotkey-recorder/.npmrc
new file mode 100644
index 0000000..b6f27f1
--- /dev/null
+++ b/examples/svelte/create-hotkey-recorder/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/examples/svelte/create-hotkey-recorder/index.html b/examples/svelte/create-hotkey-recorder/index.html
new file mode 100644
index 0000000..1d26e1a
--- /dev/null
+++ b/examples/svelte/create-hotkey-recorder/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ createHotkeyRecorder - TanStack Hotkeys Svelte Example
+
+
+ You need to enable JavaScript to run this app.
+
+
+
+
diff --git a/examples/svelte/create-hotkey-recorder/package.json b/examples/svelte/create-hotkey-recorder/package.json
new file mode 100644
index 0000000..ce7942a
--- /dev/null
+++ b/examples/svelte/create-hotkey-recorder/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@tanstack/hotkeys-example-svelte-create-hotkey-recorder",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite --port=3069"
+ },
+ "dependencies": {
+ "@tanstack/svelte-hotkeys": "0.4.1",
+ "svelte": "^5.53.7"
+ },
+ "devDependencies": {
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
+ "typescript": "5.9.3",
+ "vite": "^7.3.1"
+ }
+}
diff --git a/examples/svelte/create-hotkey-recorder/src/App.svelte b/examples/svelte/create-hotkey-recorder/src/App.svelte
new file mode 100644
index 0000000..8c0d819
--- /dev/null
+++ b/examples/svelte/create-hotkey-recorder/src/App.svelte
@@ -0,0 +1,262 @@
+
+
+
+
+
+
+
+ Shortcuts
+
+ {#each Object.entries(DEFAULT_SHORTCUT_ACTIONS) as [actionId, action]}
+ handleEdit(actionId)}
+ onCancel={handleCancel}
+ />
+ {/each}
+
+
+
+
+ Demo Actions
+ Try your shortcuts! Actions will trigger when you press them.
+
+
+
Save
+
{saveCount}
+
{formatForDisplay(shortcuts.save || 'Mod+K')}
+
+
+
Open
+
{openCount}
+
{formatForDisplay(shortcuts.open || 'Mod+E')}
+
+
+
New
+
{newCount}
+
{formatForDisplay(shortcuts.new || 'Mod+G')}
+
+
+
Close
+
{closeCount}
+
{formatForDisplay(shortcuts.close || 'Mod+Shift+K')}
+
+
+
Undo
+
{undoCount}
+
{formatForDisplay(shortcuts.undo || 'Mod+Shift+E')}
+
+
+
Redo
+
{redoCount}
+
{formatForDisplay(shortcuts.redo || 'Mod+Shift+G')}
+
+
+
+
+ {#if recorder.isRecording}
+
+ Recording shortcut... Press any key combination or Escape
+ to cancel. Press Backspace/Delete to clear the shortcut.
+
+ {/if}
+
+
+ Usage
+ {`import {
+ createHotkey,
+ createHotkeyRecorder,
+ formatForDisplay,
+} from '@tanstack/svelte-hotkeys'
+
+let shortcuts = $state({
+ save: 'Mod+K',
+ open: 'Mod+E',
+})
+
+const recorder = createHotkeyRecorder({
+ onRecord: (hotkey) => {
+ shortcuts = { ...shortcuts, save: hotkey }
+ },
+})
+
+// Register shortcuts dynamically
+createHotkey(
+ () => shortcuts.save,
+ () => handleSave(),
+ () => ({ enabled: !recorder.isRecording })
+)
+
+// In template:
+// {formatForDisplay(shortcuts.save)} `}
+
+
+
diff --git a/examples/svelte/create-hotkey-recorder/src/Root.svelte b/examples/svelte/create-hotkey-recorder/src/Root.svelte
new file mode 100644
index 0000000..12e94e9
--- /dev/null
+++ b/examples/svelte/create-hotkey-recorder/src/Root.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/examples/svelte/create-hotkey-recorder/src/ShortcutListItem.svelte b/examples/svelte/create-hotkey-recorder/src/ShortcutListItem.svelte
new file mode 100644
index 0000000..c34268d
--- /dev/null
+++ b/examples/svelte/create-hotkey-recorder/src/ShortcutListItem.svelte
@@ -0,0 +1,51 @@
+
+
+
+
+
{actionName}
+
+ {#if isRecording}
+
+ {#if heldKeys.keys.length > 0}
+
+ {#each heldKeys.keys as key, index}
+ {#if index > 0}
+ +
+ {/if}
+ {key}
+ {/each}
+
+ {:else}
+
Press any key combination...
+ {/if}
+
+ {:else if hotkey}
+
{formatForDisplay(hotkey as Hotkey)}
+ {:else}
+
No shortcut
+ {/if}
+
+
+
+ {#if isRecording}
+ Cancel
+ {:else}
+ Edit
+ {/if}
+
+
diff --git a/examples/svelte/create-hotkey-recorder/src/index.css b/examples/svelte/create-hotkey-recorder/src/index.css
new file mode 100644
index 0000000..77dfb2f
--- /dev/null
+++ b/examples/svelte/create-hotkey-recorder/src/index.css
@@ -0,0 +1,256 @@
+* {
+ box-sizing: border-box;
+}
+body {
+ margin: 0;
+ font-family:
+ system-ui,
+ -apple-system,
+ sans-serif;
+ background: #f5f5f5;
+ color: #333;
+}
+.app {
+ max-width: 900px;
+ margin: 0 auto;
+ padding: 20px;
+}
+header {
+ text-align: center;
+ margin-bottom: 40px;
+}
+header h1 {
+ margin: 0 0 10px;
+ color: #0066cc;
+}
+header p {
+ color: #666;
+ margin: 0;
+ max-width: 600px;
+ margin: 0 auto;
+}
+.demo-section {
+ background: white;
+ border-radius: 12px;
+ padding: 24px;
+ margin-bottom: 24px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+.demo-section h2 {
+ margin: 0 0 16px;
+ font-size: 20px;
+}
+.demo-section p {
+ margin: 0 0 16px;
+}
+kbd {
+ background: linear-gradient(180deg, #f8f8f8 0%, #e8e8e8 100%);
+ border: 1px solid #ccc;
+ border-bottom-width: 2px;
+ border-radius: 4px;
+ padding: 2px 8px;
+ font-family: monospace;
+ font-size: 13px;
+}
+button {
+ background: #0066cc;
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 14px;
+ transition: background 0.2s;
+}
+button:hover {
+ background: #0052a3;
+}
+button:active {
+ background: #004080;
+}
+.cancel-button {
+ background: #dc3545;
+}
+.cancel-button:hover {
+ background: #c82333;
+}
+.edit-button {
+ background: #28a745;
+}
+.edit-button:hover {
+ background: #218838;
+}
+.code-block {
+ background: #1e1e1e;
+ color: #d4d4d4;
+ padding: 16px;
+ border-radius: 8px;
+ overflow-x: auto;
+ font-size: 13px;
+ line-height: 1.5;
+ margin-top: 16px;
+}
+.info-box {
+ background: #e3f2fd;
+ border-radius: 8px;
+ padding: 12px 16px;
+ margin: 20px 0;
+}
+.recording-notice {
+ background: #fff3cd;
+ border: 2px solid #ffc107;
+ animation: pulse 2s ease-in-out infinite;
+}
+@keyframes pulse {
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.8;
+ }
+}
+
+/* Shortcuts List */
+.shortcuts-list {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+.shortcut-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 16px;
+ background: #f8f9fa;
+ border: 2px solid transparent;
+ border-radius: 8px;
+ transition: all 0.2s;
+}
+.shortcut-item:hover {
+ background: #f0f0f0;
+}
+.shortcut-item.recording {
+ background: #fff3cd;
+ border-color: #ffc107;
+ box-shadow: 0 0 0 3px rgba(255, 193, 7, 0.2);
+ animation: recordingPulse 1.5s ease-in-out infinite;
+}
+@keyframes recordingPulse {
+ 0%,
+ 100% {
+ box-shadow: 0 0 0 3px rgba(255, 193, 7, 0.2);
+ }
+ 50% {
+ box-shadow: 0 0 0 6px rgba(255, 193, 7, 0.1);
+ }
+}
+.shortcut-item-content {
+ display: flex;
+ align-items: center;
+ gap: 24px;
+ flex: 1;
+}
+.shortcut-action {
+ font-weight: 500;
+ min-width: 80px;
+ font-size: 15px;
+}
+.shortcut-hotkey {
+ display: flex;
+ align-items: center;
+ min-height: 32px;
+}
+.shortcut-hotkey kbd {
+ font-size: 14px;
+}
+.no-shortcut {
+ color: #999;
+ font-style: italic;
+ font-size: 14px;
+}
+.shortcut-actions {
+ display: flex;
+ gap: 8px;
+}
+
+/* Recording Indicator */
+.recording-indicator {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+.recording-text {
+ color: #856404;
+ font-style: italic;
+ font-size: 14px;
+}
+.held-hotkeys {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+}
+.held-hotkeys .plus {
+ color: #856404;
+ font-size: 16px;
+ margin: 0 4px;
+}
+.held-hotkeys kbd {
+ background: #ffc107;
+ border-color: #ff9800;
+ color: #856404;
+ font-weight: 600;
+}
+
+/* Demo Stats */
+.demo-stats {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+ gap: 16px;
+ margin-top: 20px;
+}
+.stat-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 16px;
+ background: #f8f9fa;
+ border-radius: 8px;
+ gap: 8px;
+}
+.stat-label {
+ font-size: 13px;
+ color: #666;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+.stat-value {
+ font-size: 32px;
+ font-weight: bold;
+ color: #0066cc;
+}
+.stat-item kbd {
+ margin-top: 4px;
+}
+
+/* Responsive */
+@media (max-width: 600px) {
+ .shortcut-item {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 12px;
+ }
+ .shortcut-item-content {
+ width: 100%;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 8px;
+ }
+ .shortcut-actions {
+ width: 100%;
+ justify-content: flex-end;
+ }
+ .demo-stats {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}
diff --git a/examples/svelte/create-hotkey-recorder/src/main.ts b/examples/svelte/create-hotkey-recorder/src/main.ts
new file mode 100644
index 0000000..9357900
--- /dev/null
+++ b/examples/svelte/create-hotkey-recorder/src/main.ts
@@ -0,0 +1,5 @@
+import { mount } from 'svelte'
+import Root from './Root.svelte'
+import './index.css'
+
+mount(Root, { target: document.getElementById('app')! })
diff --git a/examples/svelte/create-hotkey-recorder/static/robots.txt b/examples/svelte/create-hotkey-recorder/static/robots.txt
new file mode 100644
index 0000000..b6dd667
--- /dev/null
+++ b/examples/svelte/create-hotkey-recorder/static/robots.txt
@@ -0,0 +1,3 @@
+# allow crawling everything by default
+User-agent: *
+Disallow:
diff --git a/examples/svelte/create-hotkey-recorder/svelte.config.js b/examples/svelte/create-hotkey-recorder/svelte.config.js
new file mode 100644
index 0000000..b30b657
--- /dev/null
+++ b/examples/svelte/create-hotkey-recorder/svelte.config.js
@@ -0,0 +1,11 @@
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
+
+/** @type {import('svelte').Config} */
+const config = {
+ preprocess: vitePreprocess(),
+ compilerOptions: {
+ runes: true,
+ },
+}
+
+export default config
diff --git a/examples/svelte/create-hotkey-recorder/tsconfig.json b/examples/svelte/create-hotkey-recorder/tsconfig.json
new file mode 100644
index 0000000..912a030
--- /dev/null
+++ b/examples/svelte/create-hotkey-recorder/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../../tsconfig.json",
+ "compilerOptions": {
+ "moduleResolution": "bundler"
+ },
+ "include": ["src"],
+ "exclude": ["eslint.config.js"]
+}
diff --git a/examples/svelte/create-hotkey-recorder/vite.config.ts b/examples/svelte/create-hotkey-recorder/vite.config.ts
new file mode 100644
index 0000000..951a9ba
--- /dev/null
+++ b/examples/svelte/create-hotkey-recorder/vite.config.ts
@@ -0,0 +1,6 @@
+import { defineConfig } from 'vite'
+import { svelte } from '@sveltejs/vite-plugin-svelte'
+
+export default defineConfig({
+ plugins: [svelte()],
+})
diff --git a/examples/svelte/create-hotkey-sequence/.gitignore b/examples/svelte/create-hotkey-sequence/.gitignore
new file mode 100644
index 0000000..3b462cb
--- /dev/null
+++ b/examples/svelte/create-hotkey-sequence/.gitignore
@@ -0,0 +1,23 @@
+node_modules
+
+# Output
+.output
+.vercel
+.netlify
+.wrangler
+/.svelte-kit
+/build
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Env
+.env
+.env.*
+!.env.example
+!.env.test
+
+# Vite
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
diff --git a/examples/svelte/create-hotkey-sequence/.npmrc b/examples/svelte/create-hotkey-sequence/.npmrc
new file mode 100644
index 0000000..b6f27f1
--- /dev/null
+++ b/examples/svelte/create-hotkey-sequence/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/examples/svelte/create-hotkey-sequence/README.md b/examples/svelte/create-hotkey-sequence/README.md
new file mode 100644
index 0000000..57b7713
--- /dev/null
+++ b/examples/svelte/create-hotkey-sequence/README.md
@@ -0,0 +1,42 @@
+# sv
+
+Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
+
+## Creating a project
+
+If you're seeing this, you've probably already done this step. Congrats!
+
+```sh
+# create a new project
+npx sv create my-app
+```
+
+To recreate this project with the same configuration:
+
+```sh
+# recreate this project
+pnpm dlx sv create --template minimal --types ts --install pnpm create-hotkey
+```
+
+## Developing
+
+Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
+
+```sh
+npm run dev
+
+# or start the server and open the app in a new browser tab
+npm run dev -- --open
+```
+
+## Building
+
+To create a production version of your app:
+
+```sh
+npm run build
+```
+
+You can preview the production build with `npm run preview`.
+
+> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
diff --git a/examples/svelte/create-hotkey-sequence/index.html b/examples/svelte/create-hotkey-sequence/index.html
new file mode 100644
index 0000000..3e93ebb
--- /dev/null
+++ b/examples/svelte/create-hotkey-sequence/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ createHotkeySequence - TanStack Hotkeys Svelte Example
+
+
+ You need to enable JavaScript to run this app.
+
+
+
+
diff --git a/examples/svelte/create-hotkey-sequence/package.json b/examples/svelte/create-hotkey-sequence/package.json
new file mode 100644
index 0000000..436c6bb
--- /dev/null
+++ b/examples/svelte/create-hotkey-sequence/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@tanstack/hotkeys-example-svelte-create-hotkey-sequence",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite --port=3069"
+ },
+ "dependencies": {
+ "@tanstack/svelte-hotkeys": "0.4.1",
+ "svelte": "^5.53.7"
+ },
+ "devDependencies": {
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
+ "typescript": "5.9.3",
+ "vite": "^7.3.1"
+ }
+}
diff --git a/examples/svelte/create-hotkey-sequence/src/App.svelte b/examples/svelte/create-hotkey-sequence/src/App.svelte
new file mode 100644
index 0000000..f28fcd1
--- /dev/null
+++ b/examples/svelte/create-hotkey-sequence/src/App.svelte
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+ Vim-Style Commands
+
+
+
+ Sequence
+ Action
+
+
+
+
+
+ g g
+
+ Go to top
+
+
+
+ G (Shift+G)
+
+ Go to bottom
+
+
+
+ d d
+
+ Delete line
+
+
+
+ y y
+
+ Yank (copy) line
+
+
+
+ d w
+
+ Delete word
+
+
+
+ c i w
+
+ Change inner word
+
+
+
+
+
+
+ Fun Sequences
+
+
+
Konami Code (Partial)
+
+ ↑ ↑ ↓ ↓
+
+
Use arrow keys within 1.5 seconds
+
+
+
Side to Side
+
+ ← → ← →
+
+
Arrow keys within 1.5 seconds
+
+
+
Spell It Out
+
+ h e l l o
+
+
Type "hello" quickly
+
+
+
+
+ {#if lastSequence}
+
+ Triggered:
+ {lastSequence}
+
+ {/if}
+
+
+
+
+ Usage
+ {`import { createHotkeySequence } from '@tanstack/svelte-hotkeys'
+
+`}
+
+
+ {#if history.length > 0}
+
+ History
+
+ {#each history as item}
+ {item}
+ {/each}
+
+ (history = [])}>Clear History
+
+ {/if}
+
+
+ Press Escape to clear history
+
+
+
diff --git a/examples/svelte/create-hotkey-sequence/src/Root.svelte b/examples/svelte/create-hotkey-sequence/src/Root.svelte
new file mode 100644
index 0000000..12e94e9
--- /dev/null
+++ b/examples/svelte/create-hotkey-sequence/src/Root.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/examples/svelte/create-hotkey-sequence/src/index.css b/examples/svelte/create-hotkey-sequence/src/index.css
new file mode 100644
index 0000000..8ee4387
--- /dev/null
+++ b/examples/svelte/create-hotkey-sequence/src/index.css
@@ -0,0 +1,162 @@
+* {
+ box-sizing: border-box;
+}
+body {
+ margin: 0;
+ font-family:
+ system-ui,
+ -apple-system,
+ sans-serif;
+ background: #f5f5f5;
+ color: #333;
+}
+.app {
+ max-width: 800px;
+ margin: 0 auto;
+ padding: 20px;
+}
+header {
+ text-align: center;
+ margin-bottom: 40px;
+}
+header h1 {
+ margin: 0 0 10px;
+ color: #0066cc;
+}
+header p {
+ color: #666;
+ margin: 0;
+ max-width: 500px;
+ margin: 0 auto;
+}
+
+.demo-section {
+ background: white;
+ border-radius: 12px;
+ padding: 24px;
+ margin-bottom: 24px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+.demo-section h2 {
+ margin: 0 0 16px;
+ font-size: 20px;
+}
+.demo-section p {
+ margin: 0 0 12px;
+}
+kbd {
+ background: linear-gradient(180deg, #f8f8f8 0%, #e8e8e8 100%);
+ border: 1px solid #ccc;
+ border-bottom-width: 2px;
+ border-radius: 4px;
+ padding: 2px 8px;
+ font-family: monospace;
+ font-size: 13px;
+ margin-right: 4px;
+}
+.sequence-table {
+ width: 100%;
+ border-collapse: collapse;
+}
+.sequence-table th,
+.sequence-table td {
+ padding: 12px;
+ text-align: left;
+ border-bottom: 1px solid #eee;
+}
+.sequence-table th {
+ font-weight: 600;
+ color: #666;
+ font-size: 14px;
+}
+.fun-sequences {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 16px;
+}
+.sequence-card {
+ background: #f8f9fa;
+ border-radius: 8px;
+ padding: 16px;
+ text-align: center;
+}
+.sequence-card h3 {
+ margin: 0 0 12px;
+ font-size: 16px;
+}
+.sequence-card p {
+ margin: 0 0 8px;
+}
+.hint {
+ font-size: 12px;
+ color: #888;
+ font-style: italic;
+}
+.info-box {
+ background: #e3f2fd;
+ border-radius: 8px;
+ padding: 16px 20px;
+ margin-bottom: 24px;
+ font-size: 18px;
+}
+.info-box.success {
+ background: #e8f5e9;
+ color: #2e7d32;
+}
+.code-block {
+ background: #1e1e1e;
+ color: #d4d4d4;
+ padding: 16px;
+ border-radius: 8px;
+ overflow-x: auto;
+ font-size: 13px;
+ line-height: 1.5;
+}
+.history-list {
+ list-style: none;
+ padding: 0;
+ margin: 0 0 16px;
+}
+.history-list li {
+ padding: 10px 14px;
+ background: #f0f0f0;
+ border-radius: 6px;
+ margin-bottom: 6px;
+ font-family: monospace;
+ font-size: 14px;
+}
+button {
+ background: #0066cc;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 14px;
+}
+button:hover {
+ background: #0052a3;
+}
+
+.counter {
+ font-size: 18px;
+ font-weight: bold;
+ color: #0066cc;
+ margin: 12px 0;
+}
+
+.demo-input {
+ width: 100%;
+ max-width: 400px;
+ padding: 12px 16px;
+ font-size: 14px;
+ border: 1px solid #ddd;
+ border-radius: 6px;
+ margin-top: 8px;
+}
+
+.demo-input:focus {
+ outline: 2px solid #0066cc;
+ outline-offset: 2px;
+ border-color: #0066cc;
+}
diff --git a/examples/svelte/create-hotkey-sequence/src/main.ts b/examples/svelte/create-hotkey-sequence/src/main.ts
new file mode 100644
index 0000000..9357900
--- /dev/null
+++ b/examples/svelte/create-hotkey-sequence/src/main.ts
@@ -0,0 +1,5 @@
+import { mount } from 'svelte'
+import Root from './Root.svelte'
+import './index.css'
+
+mount(Root, { target: document.getElementById('app')! })
diff --git a/examples/svelte/create-hotkey-sequence/static/robots.txt b/examples/svelte/create-hotkey-sequence/static/robots.txt
new file mode 100644
index 0000000..b6dd667
--- /dev/null
+++ b/examples/svelte/create-hotkey-sequence/static/robots.txt
@@ -0,0 +1,3 @@
+# allow crawling everything by default
+User-agent: *
+Disallow:
diff --git a/examples/svelte/create-hotkey-sequence/svelte.config.js b/examples/svelte/create-hotkey-sequence/svelte.config.js
new file mode 100644
index 0000000..b30b657
--- /dev/null
+++ b/examples/svelte/create-hotkey-sequence/svelte.config.js
@@ -0,0 +1,11 @@
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
+
+/** @type {import('svelte').Config} */
+const config = {
+ preprocess: vitePreprocess(),
+ compilerOptions: {
+ runes: true,
+ },
+}
+
+export default config
diff --git a/examples/svelte/create-hotkey-sequence/tsconfig.json b/examples/svelte/create-hotkey-sequence/tsconfig.json
new file mode 100644
index 0000000..912a030
--- /dev/null
+++ b/examples/svelte/create-hotkey-sequence/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../../tsconfig.json",
+ "compilerOptions": {
+ "moduleResolution": "bundler"
+ },
+ "include": ["src"],
+ "exclude": ["eslint.config.js"]
+}
diff --git a/examples/svelte/create-hotkey-sequence/vite.config.ts b/examples/svelte/create-hotkey-sequence/vite.config.ts
new file mode 100644
index 0000000..951a9ba
--- /dev/null
+++ b/examples/svelte/create-hotkey-sequence/vite.config.ts
@@ -0,0 +1,6 @@
+import { defineConfig } from 'vite'
+import { svelte } from '@sveltejs/vite-plugin-svelte'
+
+export default defineConfig({
+ plugins: [svelte()],
+})
diff --git a/examples/svelte/create-hotkey/.gitignore b/examples/svelte/create-hotkey/.gitignore
new file mode 100644
index 0000000..3b462cb
--- /dev/null
+++ b/examples/svelte/create-hotkey/.gitignore
@@ -0,0 +1,23 @@
+node_modules
+
+# Output
+.output
+.vercel
+.netlify
+.wrangler
+/.svelte-kit
+/build
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Env
+.env
+.env.*
+!.env.example
+!.env.test
+
+# Vite
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
diff --git a/examples/svelte/create-hotkey/.npmrc b/examples/svelte/create-hotkey/.npmrc
new file mode 100644
index 0000000..b6f27f1
--- /dev/null
+++ b/examples/svelte/create-hotkey/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/examples/svelte/create-hotkey/index.html b/examples/svelte/create-hotkey/index.html
new file mode 100644
index 0000000..42f0210
--- /dev/null
+++ b/examples/svelte/create-hotkey/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ createHotkey - TanStack Hotkeys Svelte Example
+
+
+ You need to enable JavaScript to run this app.
+
+
+
+
diff --git a/examples/svelte/create-hotkey/package.json b/examples/svelte/create-hotkey/package.json
new file mode 100644
index 0000000..9f52691
--- /dev/null
+++ b/examples/svelte/create-hotkey/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "@tanstack/hotkeys-example-svelte-create-hotkey",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite --port=3069",
+ "build": "vite build",
+ "preview": "vite preview",
+ "lint": "eslint .",
+ "lint:fix": "eslint . --fix",
+ "test:types": "tsc"
+ },
+ "dependencies": {
+ "@tanstack/svelte-hotkeys": "0.4.1",
+ "svelte": "^5.53.7"
+ },
+ "devDependencies": {
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
+ "typescript": "5.9.3",
+ "vite": "^7.3.1"
+ }
+}
diff --git a/examples/svelte/create-hotkey/src/App.svelte b/examples/svelte/create-hotkey/src/App.svelte
new file mode 100644
index 0000000..c31ccde
--- /dev/null
+++ b/examples/svelte/create-hotkey/src/App.svelte
@@ -0,0 +1,549 @@
+
+
+
+
+
+
+
+ Basic Hotkey
+
+ Press {formatForDisplay('Mod+S')} to trigger
+
+ Save triggered: {saveCount}x
+ {`createHotkey('Mod+S', (_event, { hotkey, parsedHotkey }) => {
+ console.log('Hotkey:', hotkey)
+ console.log('Parsed:', parsedHotkey)
+})`}
+
+
+
+ With requireReset
+
+ Hold {formatForDisplay('Mod+K')} — only increments once until you
+ release all keys
+
+ Increment: {incrementCount}
+
+ This prevents repeated triggering while holding the keys down. Release
+ all keys to allow re-triggering.
+
+ {`createHotkey(
+ 'Mod+K',
+ (event, { hotkey }) => {
+ count++
+ },
+ { requireReset: true }
+)`}
+
+
+
+ Conditional Hotkey
+
+ {formatForDisplay('Mod+E')} is currently
+ {enabled ? 'enabled' : 'disabled'}
+
+ (enabled = !enabled)}>
+ {enabled ? 'Disable' : 'Enable'} Hotkey
+
+ {`let enabled = $state(true)
+
+createHotkey(
+ 'Mod+E',
+ (event, { hotkey }) => {
+ alert('Triggered!')
+ },
+ () => ({ enabled })
+)`}
+
+
+
+ Number Key Combinations
+ Common for tab/section switching:
+
+
{formatForDisplay('Mod+1')} → Tab 1
+
{formatForDisplay('Mod+2')} → Tab 2
+
{formatForDisplay('Mod+3')} → Tab 3
+
{formatForDisplay('Mod+4')} → Tab 4
+
{formatForDisplay('Mod+5')} → Tab 5
+
+ Active Tab: {activeTab}
+ {`createHotkey('Mod+1', () => activeTab = 1)
+createHotkey('Mod+2', () => activeTab = 2)`}
+
+
+
+ Navigation Key Combinations
+ Selection and navigation shortcuts:
+
+
{formatForDisplay('Shift+ArrowUp')} — Select up
+
+ {formatForDisplay('Shift+ArrowDown')} — Select down
+
+
+ {formatForDisplay('Alt+ArrowLeft')} — Navigate back
+
+
+ {formatForDisplay('Alt+ArrowRight')} — Navigate forward
+
+
{formatForDisplay('Mod+Home')} — Go to start
+
{formatForDisplay('Mod+End')} — Go to end
+
+ {formatForDisplay('Control+PageUp')} — Previous page
+
+
+ {formatForDisplay('Control+PageDown')} — Next page
+
+
+ Navigation triggered: {navigationCount}x
+
+
+
+ Function Key Combinations
+ System and application shortcuts:
+
+
{formatForDisplay('Alt+F4')} — Close window
+
{formatForDisplay('Control+F5')} — Hard refresh
+
{formatForDisplay('Mod+F1')} — Help
+
{formatForDisplay('Shift+F10')} — Context menu
+
{formatForDisplay('F12')} — DevTools
+
+ Function keys triggered: {functionKeyCount}x
+
+
+
+ Multi-Modifier Combinations
+ Complex shortcuts with multiple modifiers:
+
+
{formatForDisplay('Mod+Shift+S')} — Save As
+
{formatForDisplay('Mod+Shift+Z')} — Redo
+
+ {formatForDisplay('Control+Alt+A')} — Special action
+
+
+ {formatForDisplay('Control+Shift+N')} — New incognito
+
+
{formatForDisplay('Mod+Alt+T')} — Toggle theme
+
+ {formatForDisplay('Control+Alt+Shift+X')} — Triple modifier
+
+
+
+ Multi-modifier triggered: {multiModifierCount}x
+
+
+
+
+ Editing Key Combinations
+ Text editing and form shortcuts:
+
+
{formatForDisplay('Mod+Enter')} — Submit form
+
{formatForDisplay('Shift+Enter')} — New line
+
+ {formatForDisplay('Mod+Backspace')} — Delete word
+
+
+ {formatForDisplay('Mod+Delete')} — Delete forward
+
+
{formatForDisplay('Control+Tab')} — Next tab
+
{formatForDisplay('Shift+Tab')} — Previous field
+
{formatForDisplay('Mod+Space')} — Toggle
+
+ Editing keys triggered: {editingKeyCount}x
+
+
+ {#if lastHotkey}
+
+ Last triggered:
+ {formatForDisplay(lastHotkey)}
+
+ {/if}
+
+
+ Press Escape to reset all counters
+
+
+
+ Scoped Keyboard Shortcuts
+
+ Shortcuts can be scoped to specific DOM elements using the
+ target option. This allows different shortcuts to work in different
+ parts of your application.
+
+
+
+
+
+
Sidebar (Scoped Area)
+
Click here to focus, then try:
+
+
+ {formatForDisplay('Mod+B')} — Trigger sidebar action
+
+
{formatForDisplay('Mod+N')} — New item
+
+
+ Sidebar shortcuts: {sidebarShortcutCount}x
+
+
+ These shortcuts only work when this sidebar area is focused or
+ contains focus.
+
+
+
+
+
Modal Dialog
+
(modalOpen = true)}>Open Modal
+ {#if modalOpen}
+
+
(modalOpen = false)}
+ role="button"
+ tabindex="-1"
+ >
+
+
e.stopPropagation()}
+ role="dialog"
+ {@attach closeModalHotkey}
+ {@attach submitModalHotkey}
+ >
+
Modal Dialog (Scoped)
+
Try these shortcuts while modal is open:
+
+
+ {formatForDisplay('Escape')} — Close modal
+
+
+ {formatForDisplay('Mod+Enter')} — Submit
+
+
+
+ Modal shortcuts: {modalShortcutCount}x
+
+
+ These shortcuts only work when the modal is open and focused.
+ The Escape key here won't conflict with the global Escape
+ handler.
+
+
(modalOpen = false)}>Close
+
+
+ {/if}
+
+
+
+
Text Editor (Scoped)
+
Focus the editor below and try:
+
+
+ {formatForDisplay('Mod+S')} — Save editor content
+
+
{formatForDisplay('Mod+/')} — Add comment
+
{formatForDisplay('Mod+K')} — Clear editor
+
+
+
Editor shortcuts: {editorShortcutCount}x
+
+ These shortcuts only work when the editor is focused. Notice that {formatForDisplay('Mod+S')}
+ here doesn't conflict with the global
+ {formatForDisplay('Mod+S')}
+ shortcut.
+
+
+
+
+ {`const sidebarHotkey = createHotkeyAttachment(
+ 'Mod+B',
+ () => console.log('Sidebar shortcut!'),
+)
+
+const modalEscape = createHotkeyAttachment(
+ 'Escape',
+ () => (modalOpen = false),
+ () => ({ enabled: modalOpen }),
+)
+
+const editorSave = createHotkeyAttachment('Mod+S', () => {
+ saveEditorContent()
+})`}
+
+
+
diff --git a/examples/svelte/create-hotkey/src/Root.svelte b/examples/svelte/create-hotkey/src/Root.svelte
new file mode 100644
index 0000000..12e94e9
--- /dev/null
+++ b/examples/svelte/create-hotkey/src/Root.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/examples/svelte/create-hotkey/src/index.css b/examples/svelte/create-hotkey/src/index.css
new file mode 100644
index 0000000..e9f3ca9
--- /dev/null
+++ b/examples/svelte/create-hotkey/src/index.css
@@ -0,0 +1,212 @@
+* {
+ box-sizing: border-box;
+}
+body {
+ margin: 0;
+ font-family:
+ system-ui,
+ -apple-system,
+ sans-serif;
+ background: #f5f5f5;
+ color: #333;
+}
+.app {
+ max-width: 800px;
+ margin: 0 auto;
+ padding: 20px;
+}
+header {
+ text-align: center;
+ margin-bottom: 40px;
+}
+header h1 {
+ margin: 0 0 10px;
+ color: #0066cc;
+}
+header p {
+ color: #666;
+ margin: 0;
+}
+.demo-section {
+ background: white;
+ border-radius: 12px;
+ padding: 24px;
+ margin-bottom: 24px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+.demo-section h2 {
+ margin: 0 0 12px;
+ font-size: 20px;
+}
+.demo-section p {
+ margin: 0 0 12px;
+}
+kbd {
+ background: linear-gradient(180deg, #f8f8f8 0%, #e8e8e8 100%);
+ border: 1px solid #ccc;
+ border-bottom-width: 2px;
+ border-radius: 4px;
+ padding: 2px 8px;
+ font-family: monospace;
+ font-size: 13px;
+}
+.counter {
+ font-size: 28px;
+ font-weight: bold;
+ color: #0066cc;
+ margin: 16px 0;
+}
+.hint {
+ font-size: 13px;
+ color: #888;
+ font-style: italic;
+}
+.info-box {
+ background: #e3f2fd;
+ border-radius: 8px;
+ padding: 12px 16px;
+ margin: 20px 0;
+}
+button {
+ background: #0066cc;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 14px;
+}
+button:hover {
+ background: #0052a3;
+}
+.code-block {
+ background: #1e1e1e;
+ color: #d4d4d4;
+ padding: 16px;
+ border-radius: 8px;
+ overflow-x: auto;
+ font-size: 13px;
+ line-height: 1.5;
+ margin-top: 16px;
+}
+.hotkey-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 12px;
+ margin: 16px 0;
+}
+.hotkey-grid > div {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 8px 12px;
+ background: #f8f9fa;
+ border-radius: 6px;
+ font-size: 14px;
+}
+.hotkey-grid kbd {
+ flex-shrink: 0;
+}
+
+/* Scoped shortcuts section */
+.scoped-section {
+ margin-top: 40px;
+}
+
+.scoped-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 24px;
+ margin: 24px 0;
+}
+
+.scoped-area {
+ background: #f8f9fa;
+ border: 2px dashed #0066cc;
+ border-radius: 8px;
+ padding: 20px;
+ position: relative;
+}
+
+.scoped-area:focus-within {
+ border-color: #0052a3;
+ border-style: solid;
+ background: #f0f7ff;
+ box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);
+}
+
+.scoped-area h3 {
+ margin: 0 0 12px;
+ font-size: 18px;
+ color: #0066cc;
+}
+
+.scoped-area .hotkey-list {
+ margin: 12px 0;
+}
+
+.scoped-area .hotkey-list > div {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 6px 0;
+ font-size: 14px;
+}
+
+.scoped-editor {
+ width: 100%;
+ margin: 12px 0;
+ padding: 12px;
+ border: 1px solid #ddd;
+ border-radius: 6px;
+ font-family: 'Courier New', monospace;
+ font-size: 14px;
+ resize: vertical;
+ min-height: 120px;
+}
+
+.scoped-editor:focus {
+ outline: 2px solid #0066cc;
+ outline-offset: 2px;
+ border-color: #0066cc;
+}
+
+/* Modal styles */
+.modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.5);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1000;
+}
+
+.modal-content {
+ background: white;
+ border-radius: 12px;
+ padding: 24px;
+ max-width: 500px;
+ width: 90%;
+ max-height: 80vh;
+ overflow-y: auto;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
+}
+
+.modal-content:focus {
+ outline: 3px solid #0066cc;
+ outline-offset: 2px;
+}
+
+.modal-content h3 {
+ margin: 0 0 16px;
+ font-size: 20px;
+ color: #0066cc;
+}
+
+.modal-content button {
+ margin-top: 16px;
+}
diff --git a/examples/svelte/create-hotkey/src/main.ts b/examples/svelte/create-hotkey/src/main.ts
new file mode 100644
index 0000000..9357900
--- /dev/null
+++ b/examples/svelte/create-hotkey/src/main.ts
@@ -0,0 +1,5 @@
+import { mount } from 'svelte'
+import Root from './Root.svelte'
+import './index.css'
+
+mount(Root, { target: document.getElementById('app')! })
diff --git a/examples/svelte/create-hotkey/static/robots.txt b/examples/svelte/create-hotkey/static/robots.txt
new file mode 100644
index 0000000..b6dd667
--- /dev/null
+++ b/examples/svelte/create-hotkey/static/robots.txt
@@ -0,0 +1,3 @@
+# allow crawling everything by default
+User-agent: *
+Disallow:
diff --git a/examples/svelte/create-hotkey/svelte.config.js b/examples/svelte/create-hotkey/svelte.config.js
new file mode 100644
index 0000000..b30b657
--- /dev/null
+++ b/examples/svelte/create-hotkey/svelte.config.js
@@ -0,0 +1,11 @@
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
+
+/** @type {import('svelte').Config} */
+const config = {
+ preprocess: vitePreprocess(),
+ compilerOptions: {
+ runes: true,
+ },
+}
+
+export default config
diff --git a/examples/svelte/create-hotkey/tsconfig.json b/examples/svelte/create-hotkey/tsconfig.json
new file mode 100644
index 0000000..912a030
--- /dev/null
+++ b/examples/svelte/create-hotkey/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../../tsconfig.json",
+ "compilerOptions": {
+ "moduleResolution": "bundler"
+ },
+ "include": ["src"],
+ "exclude": ["eslint.config.js"]
+}
diff --git a/examples/svelte/create-hotkey/vite.config.ts b/examples/svelte/create-hotkey/vite.config.ts
new file mode 100644
index 0000000..951a9ba
--- /dev/null
+++ b/examples/svelte/create-hotkey/vite.config.ts
@@ -0,0 +1,6 @@
+import { defineConfig } from 'vite'
+import { svelte } from '@sveltejs/vite-plugin-svelte'
+
+export default defineConfig({
+ plugins: [svelte()],
+})
diff --git a/examples/svelte/get-held-keys/.gitignore b/examples/svelte/get-held-keys/.gitignore
new file mode 100644
index 0000000..3b462cb
--- /dev/null
+++ b/examples/svelte/get-held-keys/.gitignore
@@ -0,0 +1,23 @@
+node_modules
+
+# Output
+.output
+.vercel
+.netlify
+.wrangler
+/.svelte-kit
+/build
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Env
+.env
+.env.*
+!.env.example
+!.env.test
+
+# Vite
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
diff --git a/examples/svelte/get-held-keys/.npmrc b/examples/svelte/get-held-keys/.npmrc
new file mode 100644
index 0000000..b6f27f1
--- /dev/null
+++ b/examples/svelte/get-held-keys/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/examples/svelte/get-held-keys/index.html b/examples/svelte/get-held-keys/index.html
new file mode 100644
index 0000000..de0f445
--- /dev/null
+++ b/examples/svelte/get-held-keys/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ getHeldKeys - TanStack Hotkeys Svelte Example
+
+
+ You need to enable JavaScript to run this app.
+
+
+
+
diff --git a/examples/svelte/get-held-keys/package.json b/examples/svelte/get-held-keys/package.json
new file mode 100644
index 0000000..e6c1329
--- /dev/null
+++ b/examples/svelte/get-held-keys/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@tanstack/hotkeys-example-svelte-get-held-keys",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite --port=3069"
+ },
+ "dependencies": {
+ "@tanstack/svelte-hotkeys": "0.4.1",
+ "svelte": "^5.53.7"
+ },
+ "devDependencies": {
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
+ "typescript": "5.9.3",
+ "vite": "^7.3.1"
+ }
+}
diff --git a/examples/svelte/get-held-keys/src/App.svelte b/examples/svelte/get-held-keys/src/App.svelte
new file mode 100644
index 0000000..5e2901e
--- /dev/null
+++ b/examples/svelte/get-held-keys/src/App.svelte
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+ Currently Held Keys
+
+ {#if heldKeys.keys.length > 0}
+ {#each heldKeys.keys as key, index}
+ {@const code = heldKeyCodesMap.codes[key]}
+ {#if index > 0}
+ +
+ {/if}
+
+ {formatKeyForDebuggingDisplay(key)}
+ {#if code && code !== key}
+
+ {formatKeyForDebuggingDisplay(code, {
+ source: 'code',
+ })}
+
+ {/if}
+
+ {/each}
+ {:else}
+ Press any keys...
+ {/if}
+
+
+ Keys held: {heldKeys.keys.length}
+
+
+
+
+ Usage
+ {`import { getHeldKeys } from '@tanstack/svelte-hotkeys'
+
+const heldKeys = getHeldKeys()
+
+// In template:
+// Currently pressed: {heldKeys.keys.join(' + ') || 'None'}`}
+
+
+
+ Try These Combinations
+
+
+ Hold Shift + Control + A
+
+ Press multiple letter keys at once
+ Hold modifiers and watch them appear
+ Release keys one by one
+
+
+
+
+ Recent Combinations
+ {#if history.length > 0}
+
+ {#each history as combo}
+ {combo}
+ {/each}
+
+ (history = [])}>Clear History
+ {:else}
+ Press some key combinations...
+ {/if}
+
+
+
+ Use Cases
+
+ Building a keyboard shortcut recorder
+ Displaying currently held keys to users
+ Debugging keyboard input
+ Creating key combination tutorials
+
+
+
+
diff --git a/examples/svelte/get-held-keys/src/Root.svelte b/examples/svelte/get-held-keys/src/Root.svelte
new file mode 100644
index 0000000..12e94e9
--- /dev/null
+++ b/examples/svelte/get-held-keys/src/Root.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/examples/svelte/get-held-keys/src/index.css b/examples/svelte/get-held-keys/src/index.css
new file mode 100644
index 0000000..5f83d60
--- /dev/null
+++ b/examples/svelte/get-held-keys/src/index.css
@@ -0,0 +1,131 @@
+* {
+ box-sizing: border-box;
+}
+body {
+ margin: 0;
+ font-family:
+ system-ui,
+ -apple-system,
+ sans-serif;
+ background: #f5f5f5;
+ color: #333;
+}
+.app {
+ max-width: 800px;
+ margin: 0 auto;
+ padding: 20px;
+}
+header {
+ text-align: center;
+ margin-bottom: 40px;
+}
+header h1 {
+ margin: 0 0 10px;
+ color: #0066cc;
+}
+header p {
+ color: #666;
+ margin: 0;
+ max-width: 500px;
+ margin: 0 auto;
+}
+.demo-section {
+ background: white;
+ border-radius: 12px;
+ padding: 24px;
+ margin-bottom: 24px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+.demo-section h2 {
+ margin: 0 0 16px;
+ font-size: 20px;
+}
+.demo-section ul {
+ margin: 0;
+ padding-left: 20px;
+}
+.demo-section li {
+ margin-bottom: 8px;
+}
+kbd {
+ background: linear-gradient(180deg, #f8f8f8 0%, #e8e8e8 100%);
+ border: 1px solid #ccc;
+ border-bottom-width: 2px;
+ border-radius: 4px;
+ padding: 2px 8px;
+ font-family: monospace;
+ font-size: 13px;
+}
+kbd.large {
+ font-size: 24px;
+ padding: 8px 16px;
+ display: inline-flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2px;
+}
+kbd.large .code-label {
+ display: block;
+ font-size: 11px;
+ color: #888;
+ font-weight: normal;
+}
+.key-display {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+ min-height: 80px;
+ flex-wrap: wrap;
+ background: #f8f9fa;
+ border-radius: 8px;
+ padding: 20px;
+}
+.key-display .plus {
+ font-size: 24px;
+ color: #666;
+}
+.placeholder {
+ color: #999;
+ font-style: italic;
+}
+.stats {
+ text-align: center;
+ margin-top: 16px;
+ font-size: 16px;
+ color: #666;
+}
+.code-block {
+ background: #1e1e1e;
+ color: #d4d4d4;
+ padding: 16px;
+ border-radius: 8px;
+ overflow-x: auto;
+ font-size: 13px;
+ line-height: 1.5;
+}
+.history-list {
+ list-style: none;
+ padding: 0;
+ margin: 0 0 16px;
+}
+.history-list li {
+ padding: 8px 12px;
+ background: #f0f0f0;
+ border-radius: 4px;
+ margin-bottom: 4px;
+ font-family: monospace;
+ font-size: 14px;
+}
+button {
+ background: #0066cc;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 14px;
+}
+button:hover {
+ background: #0052a3;
+}
diff --git a/examples/svelte/get-held-keys/src/main.ts b/examples/svelte/get-held-keys/src/main.ts
new file mode 100644
index 0000000..9357900
--- /dev/null
+++ b/examples/svelte/get-held-keys/src/main.ts
@@ -0,0 +1,5 @@
+import { mount } from 'svelte'
+import Root from './Root.svelte'
+import './index.css'
+
+mount(Root, { target: document.getElementById('app')! })
diff --git a/examples/svelte/get-held-keys/static/robots.txt b/examples/svelte/get-held-keys/static/robots.txt
new file mode 100644
index 0000000..b6dd667
--- /dev/null
+++ b/examples/svelte/get-held-keys/static/robots.txt
@@ -0,0 +1,3 @@
+# allow crawling everything by default
+User-agent: *
+Disallow:
diff --git a/examples/svelte/get-held-keys/svelte.config.js b/examples/svelte/get-held-keys/svelte.config.js
new file mode 100644
index 0000000..b30b657
--- /dev/null
+++ b/examples/svelte/get-held-keys/svelte.config.js
@@ -0,0 +1,11 @@
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
+
+/** @type {import('svelte').Config} */
+const config = {
+ preprocess: vitePreprocess(),
+ compilerOptions: {
+ runes: true,
+ },
+}
+
+export default config
diff --git a/examples/svelte/get-held-keys/tsconfig.json b/examples/svelte/get-held-keys/tsconfig.json
new file mode 100644
index 0000000..912a030
--- /dev/null
+++ b/examples/svelte/get-held-keys/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../../tsconfig.json",
+ "compilerOptions": {
+ "moduleResolution": "bundler"
+ },
+ "include": ["src"],
+ "exclude": ["eslint.config.js"]
+}
diff --git a/examples/svelte/get-held-keys/vite.config.ts b/examples/svelte/get-held-keys/vite.config.ts
new file mode 100644
index 0000000..951a9ba
--- /dev/null
+++ b/examples/svelte/get-held-keys/vite.config.ts
@@ -0,0 +1,6 @@
+import { defineConfig } from 'vite'
+import { svelte } from '@sveltejs/vite-plugin-svelte'
+
+export default defineConfig({
+ plugins: [svelte()],
+})
diff --git a/examples/svelte/get-is-key-held/.gitignore b/examples/svelte/get-is-key-held/.gitignore
new file mode 100644
index 0000000..3b462cb
--- /dev/null
+++ b/examples/svelte/get-is-key-held/.gitignore
@@ -0,0 +1,23 @@
+node_modules
+
+# Output
+.output
+.vercel
+.netlify
+.wrangler
+/.svelte-kit
+/build
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Env
+.env
+.env.*
+!.env.example
+!.env.test
+
+# Vite
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
diff --git a/examples/svelte/get-is-key-held/.npmrc b/examples/svelte/get-is-key-held/.npmrc
new file mode 100644
index 0000000..b6f27f1
--- /dev/null
+++ b/examples/svelte/get-is-key-held/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/examples/svelte/get-is-key-held/index.html b/examples/svelte/get-is-key-held/index.html
new file mode 100644
index 0000000..83c4b3b
--- /dev/null
+++ b/examples/svelte/get-is-key-held/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ getIsKeyHeld - TanStack Hotkeys Svelte Example
+
+
+ You need to enable JavaScript to run this app.
+
+
+
+
diff --git a/examples/svelte/get-is-key-held/package.json b/examples/svelte/get-is-key-held/package.json
new file mode 100644
index 0000000..e13b44e
--- /dev/null
+++ b/examples/svelte/get-is-key-held/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@tanstack/hotkeys-example-svelte-get-is-key-held",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite --port=3069"
+ },
+ "dependencies": {
+ "@tanstack/svelte-hotkeys": "0.4.1",
+ "svelte": "^5.53.7"
+ },
+ "devDependencies": {
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
+ "typescript": "5.9.3",
+ "vite": "^7.3.1"
+ }
+}
diff --git a/examples/svelte/get-is-key-held/src/App.svelte b/examples/svelte/get-is-key-held/src/App.svelte
new file mode 100644
index 0000000..19a4c43
--- /dev/null
+++ b/examples/svelte/get-is-key-held/src/App.svelte
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+ Modifier Key States
+
+
+ Shift
+
+ {isShiftHeld.held ? 'HELD' : 'Released'}
+
+
+
+ Control
+
+ {isControlHeld.held ? 'HELD' : 'Released'}
+
+
+
+ Alt / Option
+ {isAltHeld.held ? 'HELD' : 'Released'}
+
+
+ Meta (⌘ / ⊞)
+ {isMetaHeld.held ? 'HELD' : 'Released'}
+
+
+
+
+
+ Space Bar Demo
+
+ {isSpaceHeld.held ? '🚀 SPACE HELD!' : 'Hold Space Bar'}
+
+
+
+
+ Usage
+ {`import { getIsKeyHeld } from '@tanstack/svelte-hotkeys'
+
+const isShiftHeld = getIsKeyHeld('Shift')
+
+// In template:
+//
+// {isShiftHeld.held ? 'Shift is pressed!' : 'Press Shift'}
+//
`}
+
+
+
+ Conditional UI Example
+
+ Hold Shift to reveal the secret message:
+
+
+ {#if isShiftHeld.held}
+ 🎉 The secret password is: tanstack-hotkeys-rocks!
+ {:else}
+ ••••••••••••••••••••••••••
+ {/if}
+
+
+
+
+ Use Cases
+
+ Show different UI based on modifier state
+ Enable "power user" mode while holding a key
+ Hold-to-reveal sensitive information
+ Drag-and-drop with modifier behaviors
+ Show additional options on hover + modifier
+
+
+
+
diff --git a/examples/svelte/get-is-key-held/src/Root.svelte b/examples/svelte/get-is-key-held/src/Root.svelte
new file mode 100644
index 0000000..12e94e9
--- /dev/null
+++ b/examples/svelte/get-is-key-held/src/Root.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/examples/svelte/get-is-key-held/src/index.css b/examples/svelte/get-is-key-held/src/index.css
new file mode 100644
index 0000000..0cbb5ae
--- /dev/null
+++ b/examples/svelte/get-is-key-held/src/index.css
@@ -0,0 +1,127 @@
+* {
+ box-sizing: border-box;
+}
+body {
+ margin: 0;
+ font-family:
+ system-ui,
+ -apple-system,
+ sans-serif;
+ background: #f5f5f5;
+ color: #333;
+}
+.app {
+ max-width: 800px;
+ margin: 0 auto;
+ padding: 20px;
+}
+header {
+ text-align: center;
+ margin-bottom: 40px;
+}
+header h1 {
+ margin: 0 0 10px;
+ color: #0066cc;
+}
+header p {
+ color: #666;
+ margin: 0;
+ max-width: 500px;
+ margin: 0 auto;
+}
+.demo-section {
+ background: white;
+ border-radius: 12px;
+ padding: 24px;
+ margin-bottom: 24px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+.demo-section h2 {
+ margin: 0 0 16px;
+ font-size: 20px;
+}
+.demo-section p {
+ margin: 0 0 12px;
+}
+.demo-section ul {
+ margin: 0;
+ padding-left: 20px;
+}
+.demo-section li {
+ margin-bottom: 8px;
+}
+kbd {
+ background: linear-gradient(180deg, #f8f8f8 0%, #e8e8e8 100%);
+ border: 1px solid #ccc;
+ border-bottom-width: 2px;
+ border-radius: 4px;
+ padding: 2px 8px;
+ font-family: monospace;
+ font-size: 13px;
+}
+.modifier-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 16px;
+}
+.modifier-indicator {
+ background: #f0f0f0;
+ border: 2px solid #ddd;
+ border-radius: 12px;
+ padding: 20px;
+ text-align: center;
+ transition: all 0.15s ease;
+}
+.modifier-indicator.active {
+ background: #4caf50;
+ border-color: #388e3c;
+ color: white;
+ transform: scale(1.02);
+}
+.modifier-indicator .key-name {
+ display: block;
+ font-weight: bold;
+ font-size: 18px;
+ margin-bottom: 8px;
+}
+.modifier-indicator .status {
+ font-size: 14px;
+ opacity: 0.8;
+}
+.space-indicator {
+ background: #f0f0f0;
+ border: 3px solid #ddd;
+ border-radius: 16px;
+ padding: 40px;
+ text-align: center;
+ font-size: 24px;
+ transition: all 0.15s ease;
+}
+.space-indicator.active {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ border-color: #5a67d8;
+ color: white;
+ transform: scale(1.02);
+}
+.secret-box {
+ background: #f0f0f0;
+ border-radius: 8px;
+ padding: 20px;
+ text-align: center;
+ font-family: monospace;
+ font-size: 16px;
+ transition: all 0.3s ease;
+}
+.secret-box.revealed {
+ background: #e8f5e9;
+ color: #2e7d32;
+}
+.code-block {
+ background: #1e1e1e;
+ color: #d4d4d4;
+ padding: 16px;
+ border-radius: 8px;
+ overflow-x: auto;
+ font-size: 13px;
+ line-height: 1.5;
+}
diff --git a/examples/svelte/get-is-key-held/src/main.ts b/examples/svelte/get-is-key-held/src/main.ts
new file mode 100644
index 0000000..9357900
--- /dev/null
+++ b/examples/svelte/get-is-key-held/src/main.ts
@@ -0,0 +1,5 @@
+import { mount } from 'svelte'
+import Root from './Root.svelte'
+import './index.css'
+
+mount(Root, { target: document.getElementById('app')! })
diff --git a/examples/svelte/get-is-key-held/static/robots.txt b/examples/svelte/get-is-key-held/static/robots.txt
new file mode 100644
index 0000000..b6dd667
--- /dev/null
+++ b/examples/svelte/get-is-key-held/static/robots.txt
@@ -0,0 +1,3 @@
+# allow crawling everything by default
+User-agent: *
+Disallow:
diff --git a/examples/svelte/get-is-key-held/svelte.config.js b/examples/svelte/get-is-key-held/svelte.config.js
new file mode 100644
index 0000000..b30b657
--- /dev/null
+++ b/examples/svelte/get-is-key-held/svelte.config.js
@@ -0,0 +1,11 @@
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
+
+/** @type {import('svelte').Config} */
+const config = {
+ preprocess: vitePreprocess(),
+ compilerOptions: {
+ runes: true,
+ },
+}
+
+export default config
diff --git a/examples/svelte/get-is-key-held/tsconfig.json b/examples/svelte/get-is-key-held/tsconfig.json
new file mode 100644
index 0000000..912a030
--- /dev/null
+++ b/examples/svelte/get-is-key-held/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../../tsconfig.json",
+ "compilerOptions": {
+ "moduleResolution": "bundler"
+ },
+ "include": ["src"],
+ "exclude": ["eslint.config.js"]
+}
diff --git a/examples/svelte/get-is-key-held/vite.config.ts b/examples/svelte/get-is-key-held/vite.config.ts
new file mode 100644
index 0000000..951a9ba
--- /dev/null
+++ b/examples/svelte/get-is-key-held/vite.config.ts
@@ -0,0 +1,6 @@
+import { defineConfig } from 'vite'
+import { svelte } from '@sveltejs/vite-plugin-svelte'
+
+export default defineConfig({
+ plugins: [svelte()],
+})
diff --git a/package.json b/package.json
index bb4ec20..4376185 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",
+ "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/svelte-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",
@@ -85,6 +85,7 @@
"@tanstack/react-hotkeys-devtools": "workspace:*",
"@tanstack/solid-hotkeys": "workspace:*",
"@tanstack/solid-hotkeys-devtools": "workspace:*",
+ "@tanstack/svelte-hotkeys": "workspace:*",
"@tanstack/vue-hotkeys": "workspace:*",
"@tanstack/vue-hotkeys-devtools": "workspace:*"
}
diff --git a/packages/angular-hotkeys/README.md b/packages/angular-hotkeys/README.md
index 1c80cd6..8c67395 100644
--- a/packages/angular-hotkeys/README.md
+++ b/packages/angular-hotkeys/README.md
@@ -58,7 +58,7 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
> - [**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)
-> - Svelte Hotkeys – needs a contributor!
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
## Get Involved
diff --git a/packages/hotkeys-devtools/README.md b/packages/hotkeys-devtools/README.md
index 1c80cd6..8c67395 100644
--- a/packages/hotkeys-devtools/README.md
+++ b/packages/hotkeys-devtools/README.md
@@ -58,7 +58,7 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
> - [**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)
-> - Svelte Hotkeys – needs a contributor!
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
## Get Involved
diff --git a/packages/hotkeys/README.md b/packages/hotkeys/README.md
index 1c80cd6..8c67395 100644
--- a/packages/hotkeys/README.md
+++ b/packages/hotkeys/README.md
@@ -58,7 +58,7 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
> - [**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)
-> - Svelte Hotkeys – needs a contributor!
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
## Get Involved
diff --git a/packages/preact-hotkeys-devtools/README.md b/packages/preact-hotkeys-devtools/README.md
index 1c80cd6..8c67395 100644
--- a/packages/preact-hotkeys-devtools/README.md
+++ b/packages/preact-hotkeys-devtools/README.md
@@ -58,7 +58,7 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
> - [**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)
-> - Svelte Hotkeys – needs a contributor!
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
## Get Involved
diff --git a/packages/preact-hotkeys/README.md b/packages/preact-hotkeys/README.md
index 1c80cd6..8c67395 100644
--- a/packages/preact-hotkeys/README.md
+++ b/packages/preact-hotkeys/README.md
@@ -58,7 +58,7 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
> - [**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)
-> - Svelte Hotkeys – needs a contributor!
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
## Get Involved
diff --git a/packages/react-hotkeys-devtools/README.md b/packages/react-hotkeys-devtools/README.md
index 1c80cd6..8c67395 100644
--- a/packages/react-hotkeys-devtools/README.md
+++ b/packages/react-hotkeys-devtools/README.md
@@ -58,7 +58,7 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
> - [**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)
-> - Svelte Hotkeys – needs a contributor!
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
## Get Involved
diff --git a/packages/react-hotkeys/README.md b/packages/react-hotkeys/README.md
index 1c80cd6..8c67395 100644
--- a/packages/react-hotkeys/README.md
+++ b/packages/react-hotkeys/README.md
@@ -58,7 +58,7 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
> - [**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)
-> - Svelte Hotkeys – needs a contributor!
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
## Get Involved
diff --git a/packages/solid-hotkeys-devtools/README.md b/packages/solid-hotkeys-devtools/README.md
index 1c80cd6..8c67395 100644
--- a/packages/solid-hotkeys-devtools/README.md
+++ b/packages/solid-hotkeys-devtools/README.md
@@ -58,7 +58,7 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
> - [**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)
-> - Svelte Hotkeys – needs a contributor!
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
## Get Involved
diff --git a/packages/solid-hotkeys/README.md b/packages/solid-hotkeys/README.md
index 1c80cd6..8c67395 100644
--- a/packages/solid-hotkeys/README.md
+++ b/packages/solid-hotkeys/README.md
@@ -58,7 +58,7 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
> - [**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)
-> - Svelte Hotkeys – needs a contributor!
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
## Get Involved
diff --git a/packages/svelte-hotkeys/.gitignore b/packages/svelte-hotkeys/.gitignore
new file mode 100644
index 0000000..2fd4600
--- /dev/null
+++ b/packages/svelte-hotkeys/.gitignore
@@ -0,0 +1,8 @@
+.svelte-kit
+node_modules
+dist
+build
+.env
+.env.*
+!.env.example
+!.env.test
\ No newline at end of file
diff --git a/packages/svelte-hotkeys/README.md b/packages/svelte-hotkeys/README.md
new file mode 100644
index 0000000..8c67395
--- /dev/null
+++ b/packages/svelte-hotkeys/README.md
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
+
+
+
+# TanStack Hotkeys
+
+> [!NOTE]
+> TanStack Hotkeys is pre-alpha (prototyping phase). 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)
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/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.
+
+
LET'S CHAT
+
+
+
+
+## Explore the TanStack Ecosystem
+
+- TanStack Config – Tooling for JS/TS packages
+- TanStack DB – Reactive sync client store
+- TanStack DevTools – Unified devtools panel
+- TanStack Form – Type‑safe form state
+- TanStack Hotkeys – Type‑safe keyboard shortcuts
+- TanStack Query – Async state & caching
+- TanStack Ranger – Range & slider primitives
+- TanStack Router – Type‑safe routing, caching & URL state
+- TanStack Start – Full‑stack SSR & streaming
+- TanStack Store – Reactive data store
+- TanStack Table – Headless datagrids
+- TanStack Virtual – Virtualized rendering
+
+… and more at TanStack.com »
diff --git a/packages/svelte-hotkeys/eslint.config.js b/packages/svelte-hotkeys/eslint.config.js
new file mode 100644
index 0000000..6e628b9
--- /dev/null
+++ b/packages/svelte-hotkeys/eslint.config.js
@@ -0,0 +1,31 @@
+// @ts-check
+
+import tsParser from '@typescript-eslint/parser'
+import pluginSvelte from 'eslint-plugin-svelte'
+import rootConfig from '../../eslint.config.js'
+import svelteConfig from './svelte.config.js'
+
+/** @type {import('eslint').Linter.Config[]} */
+const config = [
+ ...rootConfig,
+ ...pluginSvelte.configs['recommended'],
+ {
+ files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
+ languageOptions: {
+ parserOptions: {
+ parser: tsParser,
+ extraFileExtensions: ['.svelte'],
+ svelteConfig,
+ },
+ },
+ },
+ {
+ rules: {
+ 'svelte/block-lang': ['error', { script: ['ts'] }],
+ 'svelte/no-svelte-internal': 'error',
+ 'svelte/valid-compile': 'off',
+ },
+ },
+]
+
+export default config
diff --git a/packages/svelte-hotkeys/package.json b/packages/svelte-hotkeys/package.json
new file mode 100644
index 0000000..1e130fc
--- /dev/null
+++ b/packages/svelte-hotkeys/package.json
@@ -0,0 +1,64 @@
+{
+ "name": "@tanstack/svelte-hotkeys",
+ "version": "0.4.1",
+ "description": "Svelte adapter for TanStack Hotkeys",
+ "author": "Kunal Rao",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/TanStack/hotkeys.git",
+ "directory": "packages/svelte-hotkeys"
+ },
+ "homepage": "https://tanstack.com/hotkeys",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kunalrao"
+ },
+ "keywords": [
+ "svelte",
+ "tanstack",
+ "keys",
+ "hotkeys",
+ "keyboard",
+ "shortcuts"
+ ],
+ "scripts": {
+ "clean": "premove ./dist ./coverage",
+ "test:eslint": "eslint ./src",
+ "test:lib": "vitest --passWithNoTests",
+ "test:lib:dev": "pnpm test:lib --watch",
+ "test:types": "tsc",
+ "test:build": "publint --strict",
+ "build": "svelte-package --input ./src --output ./dist"
+ },
+ "type": "module",
+ "types": "dist/index.d.ts",
+ "module": "dist/index.js",
+ "svelte": "./dist/index.js",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "svelte": "./dist/index.js",
+ "import": "./dist/index.js"
+ },
+ "./package.json": "./package.json"
+ },
+ "sideEffects": false,
+ "files": [
+ "dist",
+ "src"
+ ],
+ "dependencies": {
+ "@tanstack/hotkeys": "workspace:*"
+ },
+ "devDependencies": {
+ "@sveltejs/package": "^2.5.7",
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
+ "@typescript-eslint/parser": "^8.56.1",
+ "eslint-plugin-svelte": "^3.15.0",
+ "svelte": "^5.53.7"
+ },
+ "peerDependencies": {
+ "svelte": "^5.25.0"
+ }
+}
diff --git a/packages/svelte-hotkeys/src/HotkeysCtx.ts b/packages/svelte-hotkeys/src/HotkeysCtx.ts
new file mode 100644
index 0000000..686e1c9
--- /dev/null
+++ b/packages/svelte-hotkeys/src/HotkeysCtx.ts
@@ -0,0 +1,57 @@
+import { createContext } from 'svelte'
+import type { HotkeyRecorderOptions } from '@tanstack/hotkeys'
+import type { CreateHotkeyOptions } from './createHotkey.svelte'
+import type { Snippet } from 'svelte'
+import type { CreateHotkeySequenceOptions } from './createHotkeySequence.svelte'
+
+export interface HotkeysProviderOptions {
+ hotkey?: Partial
+ hotkeyRecorder?: Partial
+ hotkeySequence?: Partial
+}
+
+export interface HotkeysProviderProps {
+ children: Snippet
+ defaultOptions?: HotkeysProviderOptions
+}
+
+export const DEFAULT_OPTIONS: HotkeysProviderOptions = {}
+
+interface HotkeysContextValue {
+ defaultOptions: HotkeysProviderOptions
+}
+
+const [useHotkeysContext, setHotkeysContextValue] =
+ createContext()
+
+export function setHotkeysContext(
+ defaultOptions: HotkeysProviderOptions = DEFAULT_OPTIONS,
+): HotkeysContextValue {
+ return setHotkeysContextValue({
+ get defaultOptions() {
+ return defaultOptions
+ },
+ })!
+}
+
+export function setHotkeysContextSource(
+ defaultOptions: () => HotkeysProviderOptions,
+): HotkeysContextValue {
+ return setHotkeysContextValue({
+ get defaultOptions() {
+ return defaultOptions()
+ },
+ })!
+}
+
+export function getHotkeysContext(): HotkeysContextValue | null {
+ try {
+ return useHotkeysContext()
+ } catch {
+ return null
+ }
+}
+
+export function getDefaultHotkeysOptions(): HotkeysProviderOptions {
+ return getHotkeysContext()?.defaultOptions ?? DEFAULT_OPTIONS
+}
diff --git a/packages/svelte-hotkeys/src/HotkeysProvider.svelte b/packages/svelte-hotkeys/src/HotkeysProvider.svelte
new file mode 100644
index 0000000..a431589
--- /dev/null
+++ b/packages/svelte-hotkeys/src/HotkeysProvider.svelte
@@ -0,0 +1,12 @@
+
+
+{@render children()}
diff --git a/packages/svelte-hotkeys/src/createHotkey.svelte.ts b/packages/svelte-hotkeys/src/createHotkey.svelte.ts
new file mode 100644
index 0000000..be32cad
--- /dev/null
+++ b/packages/svelte-hotkeys/src/createHotkey.svelte.ts
@@ -0,0 +1,120 @@
+import {
+ detectPlatform,
+ formatHotkey,
+ getHotkeyManager,
+ rawHotkeyToParsedHotkey,
+} from '@tanstack/hotkeys'
+import { getDefaultHotkeysOptions } from './HotkeysCtx'
+import { resolveMaybeGetter } from './internal.svelte'
+import type {
+ Hotkey,
+ HotkeyCallback,
+ HotkeyOptions,
+ RegisterableHotkey,
+} from '@tanstack/hotkeys'
+import type { MaybeGetter } from './internal.svelte'
+import type { Attachment } from 'svelte/attachments'
+
+export interface CreateHotkeyOptions extends Omit {
+ target?: Document | Window // not html elements, use attachment instead
+}
+
+function normalizeHotkey(
+ hotkey: RegisterableHotkey,
+ options: CreateHotkeyOptions,
+): Hotkey {
+ const platform = options.platform ?? detectPlatform()
+
+ return typeof hotkey === 'string'
+ ? hotkey
+ : (formatHotkey(rawHotkeyToParsedHotkey(hotkey, platform)) as Hotkey)
+}
+
+function registerHotkey(
+ target: HTMLElement | Document | Window,
+ hotkey: MaybeGetter,
+ callback: HotkeyCallback,
+ options: MaybeGetter,
+) {
+ const resolvedHotkey = resolveMaybeGetter(hotkey)
+ const resolvedOptions = resolveMaybeGetter(options)
+ const mergedOptions = {
+ ...getDefaultHotkeysOptions().hotkey,
+ ...resolvedOptions,
+ } as CreateHotkeyOptions
+
+ return getHotkeyManager().register(
+ normalizeHotkey(resolvedHotkey, mergedOptions),
+ callback,
+ {
+ ...mergedOptions,
+ target,
+ },
+ )
+}
+
+/**
+ * Register a global hotkey for the current component.
+ *
+ * @example
+ * ```svelte
+ *
+ * ```
+ */
+export function createHotkey(
+ hotkey: MaybeGetter,
+ callback: HotkeyCallback,
+ options: MaybeGetter = {},
+): void {
+ $effect(() => {
+ if (typeof document === 'undefined') {
+ return
+ }
+
+ const registration = registerHotkey(document, hotkey, callback, options)
+
+ return () => {
+ registration.unregister()
+ }
+ })
+}
+
+/**
+ * Create an attachment for element-scoped hotkeys.
+ *
+ * @example
+ * ```svelte
+ *
+ *
+ *
+ * Count: {count}
+ *
+ * ```
+ */
+export function createHotkeyAttachment(
+ hotkey: MaybeGetter,
+ callback: HotkeyCallback,
+ options: MaybeGetter = {},
+): Attachment {
+ return (element) => {
+ const registration = registerHotkey(element, hotkey, callback, options)
+
+ return () => {
+ registration.unregister()
+ }
+ }
+}
diff --git a/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts b/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts
new file mode 100644
index 0000000..f490a3e
--- /dev/null
+++ b/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts
@@ -0,0 +1,118 @@
+import { HotkeyRecorder } from '@tanstack/hotkeys'
+import { onDestroy } from 'svelte'
+import { getDefaultHotkeysOptions } from './HotkeysCtx'
+import { createStoreSubscriber, resolveMaybeGetter } from './internal.svelte'
+import type { Hotkey, HotkeyRecorderOptions } from '@tanstack/hotkeys'
+import type { MaybeGetter } from './internal.svelte'
+
+export interface SvelteHotkeyRecorder {
+ /** Whether recording is currently active */
+ readonly isRecording: boolean
+ /** The currently recorded hotkey (for live preview) */
+ readonly recordedHotkey: Hotkey | null
+ /** Start recording a new hotkey */
+ startRecording: () => void
+ /** Stop recording (same as cancel) */
+ stopRecording: () => void
+ /** Cancel recording without saving */
+ cancelRecording: () => void
+}
+
+/**
+ * Svelte function for recording keyboard shortcuts.
+ *
+ * This function provides a thin wrapper around the framework-agnostic `HotkeyRecorder`
+ * class, managing all the complexity of capturing keyboard events, converting them
+ * to hotkey strings, and handling edge cases like Escape to cancel or Backspace/Delete
+ * to clear.
+ *
+ * @param options - Configuration options for the recorder
+ * @returns An object with recording state and control functions
+ *
+ * @example
+ * ```svelte
+ *
+ *
+ *
+ *
+ * {recorder.isRecording ? 'Recording...' : 'Edit Shortcut'}
+ *
+ * {#if recorder.recordedHotkey}
+ *
Recording: {recorder.recordedHotkey}
+ * {/if}
+ *
+ * ```
+ */
+
+class SvelteHotkeyRecorderState implements SvelteHotkeyRecorder {
+ #recorder: HotkeyRecorder
+ #subscribe: () => void
+
+ constructor(options: HotkeyRecorderOptions) {
+ this.#recorder = new HotkeyRecorder(options)
+ this.#subscribe = createStoreSubscriber(this.#recorder.store)
+ }
+
+ get isRecording(): boolean {
+ this.#subscribe()
+ return this.#recorder.store.state.isRecording
+ }
+
+ get recordedHotkey(): Hotkey | null {
+ this.#subscribe()
+ return this.#recorder.store.state.recordedHotkey
+ }
+
+ setOptions(options: HotkeyRecorderOptions): void {
+ this.#recorder.setOptions(options)
+ }
+
+ startRecording(): void {
+ this.#recorder.start()
+ }
+
+ stopRecording(): void {
+ this.#recorder.stop()
+ }
+
+ cancelRecording(): void {
+ this.#recorder.cancel()
+ }
+
+ destroy(): void {
+ this.#recorder.destroy()
+ }
+}
+
+export function createHotkeyRecorder(
+ options: MaybeGetter,
+): SvelteHotkeyRecorder {
+ const recorder = new SvelteHotkeyRecorderState({
+ ...getDefaultHotkeysOptions().hotkeyRecorder,
+ ...resolveMaybeGetter(options),
+ } as HotkeyRecorderOptions)
+
+ $effect(() => {
+ recorder.setOptions({
+ ...getDefaultHotkeysOptions().hotkeyRecorder,
+ ...resolveMaybeGetter(options),
+ } as HotkeyRecorderOptions)
+ })
+
+ onDestroy(() => {
+ recorder.destroy()
+ })
+
+ return recorder
+}
diff --git a/packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts b/packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts
new file mode 100644
index 0000000..2fc8dbe
--- /dev/null
+++ b/packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts
@@ -0,0 +1,136 @@
+import { getSequenceManager } from '@tanstack/hotkeys'
+import { getDefaultHotkeysOptions } from './HotkeysCtx'
+import { resolveMaybeGetter } from './internal.svelte'
+import type {
+ HotkeyCallback,
+ HotkeySequence,
+ SequenceOptions,
+} from '@tanstack/hotkeys'
+import type { MaybeGetter } from './internal.svelte'
+import type { Attachment } from 'svelte/attachments'
+
+export interface CreateHotkeySequenceOptions extends Omit<
+ SequenceOptions,
+ 'target'
+> {
+ target?: Document | Window // not html elements, use attachment instead
+}
+
+function registerHotkeySequence(
+ target: HTMLElement | Document | Window,
+ sequence: MaybeGetter,
+ callback: HotkeyCallback,
+ options: MaybeGetter,
+) {
+ const resolvedSequence = resolveMaybeGetter(sequence)
+ const resolvedOptions = resolveMaybeGetter(options)
+ const mergedOptions = {
+ ...getDefaultHotkeysOptions().hotkeySequence,
+ ...resolvedOptions,
+ } as CreateHotkeySequenceOptions
+
+ return getSequenceManager().register(resolvedSequence, callback, {
+ ...mergedOptions,
+ target,
+ })
+}
+
+/**
+ * Register a global keyboard shortcut sequence for the current component.
+ *
+ * @example
+ * ```svelte
+ *
+ *
+ *
+ * ....
+ *
+ * ```
+ */
+export function createHotkeySequence(
+ sequence: MaybeGetter,
+ callback: HotkeyCallback,
+ options: MaybeGetter = {},
+): void {
+ $effect(() => {
+ if (typeof document === 'undefined') {
+ return
+ }
+
+ const resolvedSequence = resolveMaybeGetter(sequence)
+ if (resolvedSequence.length === 0) {
+ return
+ }
+
+ const registration = registerHotkeySequence(
+ document,
+ resolvedSequence,
+ callback,
+ options,
+ )
+
+ return () => {
+ registration.unregister()
+ }
+ })
+}
+
+/**
+ * Create an attachment for element-scoped keyboard sequences.
+ *
+ * @example
+ * ```svelte
+ *
+ *
+ *
+ * Focus here and press g then g
+ *
+ * ```
+ */
+export function createHotkeySequenceAttachment(
+ sequence: MaybeGetter,
+ callback: HotkeyCallback,
+ options: MaybeGetter = {},
+): Attachment {
+ return (element) => {
+ const resolvedSequence = resolveMaybeGetter(sequence)
+
+ if (resolvedSequence.length === 0) {
+ return
+ }
+
+ const registration = registerHotkeySequence(
+ element,
+ resolvedSequence,
+ callback,
+ options,
+ )
+
+ return () => {
+ registration.unregister()
+ }
+ }
+}
diff --git a/packages/svelte-hotkeys/src/getHeldKeyCodesMap.svelte.ts b/packages/svelte-hotkeys/src/getHeldKeyCodesMap.svelte.ts
new file mode 100644
index 0000000..1d59981
--- /dev/null
+++ b/packages/svelte-hotkeys/src/getHeldKeyCodesMap.svelte.ts
@@ -0,0 +1,44 @@
+import { getKeyStateTracker } from '@tanstack/hotkeys'
+import { createStoreSubscriber } from './internal.svelte'
+
+export interface SvelteHeldKeyCodesMap {
+ readonly codes: Record
+}
+
+class HeldKeyCodesMapState implements SvelteHeldKeyCodesMap {
+ #tracker = getKeyStateTracker()
+ #subscribe = createStoreSubscriber(this.#tracker.store)
+
+ get codes(): Record {
+ this.#subscribe()
+ return this.#tracker.store.state.heldCodes
+ }
+}
+
+/**
+ * Svelte function that returns reactive access to the map of currently held key names
+ * to their physical `event.code` values.
+ *
+ * This is useful for debugging which physical key was pressed (e.g. distinguishing
+ * left vs right Shift via "ShiftLeft" / "ShiftRight").
+ *
+ * @returns Object with a reactive `codes` property
+ *
+ * ```svelte
+ *
+ *
+ *
+ * {#each Object.entries(heldKeyCodesMap.codes) as [key, code]}
+ *
+ * {key} {code}
+ *
+ * {/each}
+ *
+ * ```
+ */
+export function getHeldKeyCodesMap(): SvelteHeldKeyCodesMap {
+ return new HeldKeyCodesMapState()
+}
diff --git a/packages/svelte-hotkeys/src/getHeldKeys.svelte.ts b/packages/svelte-hotkeys/src/getHeldKeys.svelte.ts
new file mode 100644
index 0000000..eddc222
--- /dev/null
+++ b/packages/svelte-hotkeys/src/getHeldKeys.svelte.ts
@@ -0,0 +1,40 @@
+import { getKeyStateTracker } from '@tanstack/hotkeys'
+import { createStoreSubscriber } from './internal.svelte'
+
+export interface SvelteHeldKeys {
+ readonly keys: Array
+}
+
+class HeldKeysState implements SvelteHeldKeys {
+ #tracker = getKeyStateTracker()
+ #subscribe = createStoreSubscriber(this.#tracker.store)
+
+ get keys(): Array {
+ this.#subscribe()
+ return this.#tracker.store.state.heldKeys
+ }
+}
+
+/**
+ * Svelte function that returns reactive access to currently held keyboard keys.
+ *
+ * This function uses the global KeyStateTracker and updates whenever keys are pressed
+ * or released.
+ *
+ * @returns Object with a reactive `keys` property
+ *
+ * @example
+ * ```svelte
+ *
+ *
+ * Currently pressed: {heldKeys.keys.join(' + ') || 'None'}
+ *
+ * ```
+ */
+export function getHeldKeys(): SvelteHeldKeys {
+ return new HeldKeysState()
+}
diff --git a/packages/svelte-hotkeys/src/getIsKeyHeld.svelte.ts b/packages/svelte-hotkeys/src/getIsKeyHeld.svelte.ts
new file mode 100644
index 0000000..797681c
--- /dev/null
+++ b/packages/svelte-hotkeys/src/getIsKeyHeld.svelte.ts
@@ -0,0 +1,68 @@
+import { getKeyStateTracker } from '@tanstack/hotkeys'
+import { createStoreSubscriber } from './internal.svelte'
+import type { HeldKey } from '@tanstack/hotkeys'
+
+export interface SvelteHeldKeyState {
+ readonly held: boolean
+}
+
+class HeldKeyState implements SvelteHeldKeyState {
+ #tracker = getKeyStateTracker()
+ #normalizedKey: string
+ #subscribe = createStoreSubscriber(this.#tracker.store)
+
+ constructor(key: HeldKey) {
+ this.#normalizedKey = key.toLowerCase()
+ }
+
+ get held(): boolean {
+ this.#subscribe()
+ return this.#tracker.store.state.heldKeys.some(
+ (heldKey) => heldKey.toLowerCase() === this.#normalizedKey,
+ )
+ }
+}
+
+/**
+ * Svelte function that returns reactive access to whether a specific key is currently being held.
+ *
+ * This function uses the global KeyStateTracker and updates whenever keys are pressed
+ * or released.
+ *
+ * @param key - The key to check (e.g., 'Shift', 'Control', 'A')
+ * @returns Object with a reactive `held` property
+ *
+ * @example
+ * ```svelte
+ *
+ *
+ *
+ * {isShiftHeld.held ? 'Shift is pressed!' : 'Press Shift'}
+ *
+ * ```
+ *
+ * @example
+ * ```svelte
+ *
+ *
+ *
+ * Ctrl
+ * Shift
+ * Alt
+ *
+ * ```
+ */
+
+export function getIsKeyHeld(key: HeldKey): SvelteHeldKeyState {
+ return new HeldKeyState(key)
+}
diff --git a/packages/svelte-hotkeys/src/index.ts b/packages/svelte-hotkeys/src/index.ts
new file mode 100644
index 0000000..0d62f94
--- /dev/null
+++ b/packages/svelte-hotkeys/src/index.ts
@@ -0,0 +1,17 @@
+// Re-export everything from the core package
+export * from '@tanstack/hotkeys'
+
+export * from './createHotkey.svelte'
+export * from './createHotkeySequence.svelte'
+export * from './createHotkeyRecorder.svelte'
+export * from './getHeldKeys.svelte'
+export * from './getHeldKeyCodesMap.svelte'
+export * from './getIsKeyHeld.svelte'
+export { default as HotkeysProvider } from './HotkeysProvider.svelte'
+export {
+ DEFAULT_OPTIONS,
+ getDefaultHotkeysOptions,
+ getHotkeysContext,
+ setHotkeysContext,
+} from './HotkeysCtx'
+export type { HotkeysProviderOptions, HotkeysProviderProps } from './HotkeysCtx'
diff --git a/packages/svelte-hotkeys/src/internal.svelte.ts b/packages/svelte-hotkeys/src/internal.svelte.ts
new file mode 100644
index 0000000..058b925
--- /dev/null
+++ b/packages/svelte-hotkeys/src/internal.svelte.ts
@@ -0,0 +1,26 @@
+import { createSubscriber } from 'svelte/reactivity'
+
+export type MaybeGetter = T | (() => T)
+
+interface SubscribableStore {
+ state: TState
+ subscribe: (
+ listener: () => void,
+ ) => (() => void) | { unsubscribe: () => void }
+}
+
+export function resolveMaybeGetter(value: MaybeGetter): T {
+ return typeof value === 'function' ? (value as () => T)() : value
+}
+
+export function createStoreSubscriber(
+ store: SubscribableStore,
+): () => void {
+ return createSubscriber((update) => {
+ const subscription = store.subscribe(update)
+
+ return typeof subscription === 'function'
+ ? subscription
+ : () => subscription.unsubscribe()
+ })
+}
diff --git a/packages/svelte-hotkeys/svelte.config.js b/packages/svelte-hotkeys/svelte.config.js
new file mode 100644
index 0000000..076d2dc
--- /dev/null
+++ b/packages/svelte-hotkeys/svelte.config.js
@@ -0,0 +1,10 @@
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
+
+const config = {
+ preprocess: vitePreprocess(),
+ compilerOptions: {
+ runes: true,
+ },
+}
+
+export default config
diff --git a/packages/svelte-hotkeys/tests/createHotkey.test.ts b/packages/svelte-hotkeys/tests/createHotkey.test.ts
new file mode 100644
index 0000000..f35fd2f
--- /dev/null
+++ b/packages/svelte-hotkeys/tests/createHotkey.test.ts
@@ -0,0 +1,9 @@
+import { describe, expect, it } from 'vitest'
+import { getDefaultHotkeysOptions, getHotkeysContext } from '../src/HotkeysCtx'
+
+describe('HotkeysCtx', () => {
+ it('falls back cleanly when no parent context exists', () => {
+ expect(getHotkeysContext()).toBeNull()
+ expect(getDefaultHotkeysOptions()).toEqual({})
+ })
+})
diff --git a/packages/svelte-hotkeys/tsconfig.docs.json b/packages/svelte-hotkeys/tsconfig.docs.json
new file mode 100644
index 0000000..08866d6
--- /dev/null
+++ b/packages/svelte-hotkeys/tsconfig.docs.json
@@ -0,0 +1,9 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "paths": {
+ "@tanstack/hotkeys": ["../hotkeys/src"]
+ }
+ },
+ "include": ["src"]
+}
diff --git a/packages/svelte-hotkeys/tsconfig.json b/packages/svelte-hotkeys/tsconfig.json
new file mode 100644
index 0000000..07b3300
--- /dev/null
+++ b/packages/svelte-hotkeys/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "extends": "../../tsconfig.json",
+ "include": [
+ "src/**/*.js",
+ "src/**/*.ts",
+ "src/**/*.svelte",
+ "src/**/*.svelte.ts",
+ "svelte.config.js",
+ "vitest.config.ts"
+ ],
+ "exclude": ["eslint.config.js"]
+}
diff --git a/packages/svelte-hotkeys/vite.config.ts b/packages/svelte-hotkeys/vite.config.ts
new file mode 100644
index 0000000..3d696d6
--- /dev/null
+++ b/packages/svelte-hotkeys/vite.config.ts
@@ -0,0 +1,6 @@
+import { svelte } from '@sveltejs/vite-plugin-svelte'
+import { defineConfig } from 'vitest/config'
+
+export default defineConfig({
+ plugins: [svelte()],
+})
diff --git a/packages/svelte-hotkeys/vitest.config.ts b/packages/svelte-hotkeys/vitest.config.ts
new file mode 100644
index 0000000..9d327db
--- /dev/null
+++ b/packages/svelte-hotkeys/vitest.config.ts
@@ -0,0 +1,10 @@
+import { defineConfig } from 'vitest/config'
+import { svelte } from '@sveltejs/vite-plugin-svelte'
+
+export default defineConfig({
+ plugins: [svelte()],
+ test: {
+ environment: 'happy-dom',
+ globals: true,
+ },
+})
diff --git a/packages/vue-hotkeys-devtools/README.md b/packages/vue-hotkeys-devtools/README.md
index 1c80cd6..8c67395 100644
--- a/packages/vue-hotkeys-devtools/README.md
+++ b/packages/vue-hotkeys-devtools/README.md
@@ -58,7 +58,7 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
> - [**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)
-> - Svelte Hotkeys – needs a contributor!
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
## Get Involved
diff --git a/packages/vue-hotkeys/README.md b/packages/vue-hotkeys/README.md
index 1c80cd6..8c67395 100644
--- a/packages/vue-hotkeys/README.md
+++ b/packages/vue-hotkeys/README.md
@@ -58,7 +58,7 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
> - [**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)
-> - Svelte Hotkeys – needs a contributor!
+> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
## Get Involved
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 652fda8..48e96f4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -58,7 +58,7 @@ importers:
version: 3.8.1
prettier-plugin-svelte:
specifier: ^3.5.1
- version: 3.5.1(prettier@3.8.1)(svelte@5.50.3)
+ version: 3.5.1(prettier@3.8.1)(svelte@5.53.7)
publint:
specifier: ^0.3.18
version: 0.3.18
@@ -857,6 +857,101 @@ importers:
specifier: ^2.11.10
version: 2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.11)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
+ examples/svelte/create-hotkey:
+ dependencies:
+ '@tanstack/svelte-hotkeys':
+ specifier: 0.4.1
+ version: link:../../../packages/svelte-hotkeys
+ svelte:
+ specifier: ^5.53.7
+ version: 5.53.7
+ devDependencies:
+ '@sveltejs/vite-plugin-svelte':
+ specifier: ^6.2.4
+ version: 6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
+ typescript:
+ specifier: 5.9.3
+ version: 5.9.3
+ vite:
+ specifier: ^7.3.1
+ version: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)
+
+ examples/svelte/create-hotkey-recorder:
+ dependencies:
+ '@tanstack/svelte-hotkeys':
+ specifier: 0.4.1
+ version: link:../../../packages/svelte-hotkeys
+ svelte:
+ specifier: ^5.53.7
+ version: 5.53.7
+ devDependencies:
+ '@sveltejs/vite-plugin-svelte':
+ specifier: ^6.2.4
+ version: 6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
+ typescript:
+ specifier: 5.9.3
+ version: 5.9.3
+ vite:
+ specifier: ^7.3.1
+ version: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)
+
+ examples/svelte/create-hotkey-sequence:
+ dependencies:
+ '@tanstack/svelte-hotkeys':
+ specifier: 0.4.1
+ version: link:../../../packages/svelte-hotkeys
+ svelte:
+ specifier: ^5.53.7
+ version: 5.53.7
+ devDependencies:
+ '@sveltejs/vite-plugin-svelte':
+ specifier: ^6.2.4
+ version: 6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
+ typescript:
+ specifier: 5.9.3
+ version: 5.9.3
+ vite:
+ specifier: ^7.3.1
+ version: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)
+
+ examples/svelte/get-held-keys:
+ dependencies:
+ '@tanstack/svelte-hotkeys':
+ specifier: 0.4.1
+ version: link:../../../packages/svelte-hotkeys
+ svelte:
+ specifier: ^5.53.7
+ version: 5.53.7
+ devDependencies:
+ '@sveltejs/vite-plugin-svelte':
+ specifier: ^6.2.4
+ version: 6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
+ typescript:
+ specifier: 5.9.3
+ version: 5.9.3
+ vite:
+ specifier: ^7.3.1
+ version: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)
+
+ examples/svelte/get-is-key-held:
+ dependencies:
+ '@tanstack/svelte-hotkeys':
+ specifier: 0.4.1
+ version: link:../../../packages/svelte-hotkeys
+ svelte:
+ specifier: ^5.53.7
+ version: 5.53.7
+ devDependencies:
+ '@sveltejs/vite-plugin-svelte':
+ specifier: ^6.2.4
+ version: 6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
+ typescript:
+ specifier: 5.9.3
+ version: 5.9.3
+ vite:
+ specifier: ^7.3.1
+ version: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)
+
examples/vue/useHeldKeys:
dependencies:
'@tanstack/vue-hotkeys':
@@ -1176,6 +1271,28 @@ importers:
specifier: ^2.11.10
version: 2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.11)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
+ packages/svelte-hotkeys:
+ dependencies:
+ '@tanstack/hotkeys':
+ specifier: workspace:*
+ version: link:../hotkeys
+ devDependencies:
+ '@sveltejs/package':
+ specifier: ^2.5.7
+ version: 2.5.7(svelte@5.53.7)(typescript@5.9.3)
+ '@sveltejs/vite-plugin-svelte':
+ specifier: ^6.2.4
+ version: 6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
+ '@typescript-eslint/parser':
+ specifier: ^8.56.1
+ version: 8.56.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ eslint-plugin-svelte:
+ specifier: ^3.15.0
+ version: 3.15.0(eslint@9.39.2(jiti@2.6.1))(svelte@5.53.7)
+ svelte:
+ specifier: ^5.53.7
+ version: 5.53.7
+
packages/vue-hotkeys:
dependencies:
'@tanstack/hotkeys':
@@ -3761,6 +3878,28 @@ packages:
peerDependencies:
acorn: ^8.9.0
+ '@sveltejs/package@2.5.7':
+ resolution: {integrity: sha512-qqD9xa9H7TDiGFrF6rz7AirOR8k15qDK/9i4MIE8te4vWsv5GEogPks61rrZcLy+yWph+aI6pIj2MdoK3YI8AQ==}
+ engines: {node: ^16.14 || >=18}
+ hasBin: true
+ peerDependencies:
+ svelte: ^3.44.0 || ^4.0.0 || ^5.0.0-next.1
+
+ '@sveltejs/vite-plugin-svelte-inspector@5.0.2':
+ resolution: {integrity: sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig==}
+ engines: {node: ^20.19 || ^22.12 || >=24}
+ peerDependencies:
+ '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0
+ svelte: ^5.0.0
+ vite: ^6.3.0 || ^7.0.0
+
+ '@sveltejs/vite-plugin-svelte@6.2.4':
+ resolution: {integrity: sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==}
+ engines: {node: ^20.19 || ^22.12 || >=24}
+ peerDependencies:
+ svelte: ^5.0.0
+ vite: ^6.3.0 || ^7.0.0
+
'@svitejs/changesets-changelog-github-compact@1.2.0':
resolution: {integrity: sha512-08eKiDAjj4zLug1taXSIJ0kGL5cawjVCyJkBb6EWSg5fEPX6L+Wtr0CH2If4j5KYylz85iaZiFlUItvgJvll5g==}
engines: {node: ^14.13.1 || ^16.0.0 || >=18}
@@ -4036,6 +4175,9 @@ packages:
'@types/sockjs@0.3.36':
resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==}
+ '@types/trusted-types@2.0.7':
+ resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
+
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
@@ -4060,22 +4202,45 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
+ '@typescript-eslint/parser@8.56.1':
+ resolution: {integrity: sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
'@typescript-eslint/project-service@8.55.0':
resolution: {integrity: sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
+ '@typescript-eslint/project-service@8.56.1':
+ resolution: {integrity: sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
'@typescript-eslint/scope-manager@8.55.0':
resolution: {integrity: sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ '@typescript-eslint/scope-manager@8.56.1':
+ resolution: {integrity: sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@typescript-eslint/tsconfig-utils@8.55.0':
resolution: {integrity: sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
+ '@typescript-eslint/tsconfig-utils@8.56.1':
+ resolution: {integrity: sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
'@typescript-eslint/type-utils@8.55.0':
resolution: {integrity: sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -4087,12 +4252,22 @@ packages:
resolution: {integrity: sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ '@typescript-eslint/types@8.56.1':
+ resolution: {integrity: sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@typescript-eslint/typescript-estree@8.55.0':
resolution: {integrity: sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
+ '@typescript-eslint/typescript-estree@8.56.1':
+ resolution: {integrity: sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
'@typescript-eslint/utils@8.55.0':
resolution: {integrity: sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -4104,6 +4279,10 @@ packages:
resolution: {integrity: sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ '@typescript-eslint/visitor-keys@8.56.1':
+ resolution: {integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==}
cpu: [arm]
@@ -4473,6 +4652,10 @@ packages:
aria-query@5.3.0:
resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+ aria-query@5.3.1:
+ resolution: {integrity: sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==}
+ engines: {node: '>= 0.4'}
+
aria-query@5.3.2:
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
engines: {node: '>= 0.4'}
@@ -4967,6 +5150,9 @@ packages:
supports-color:
optional: true
+ dedent-js@1.0.1:
+ resolution: {integrity: sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==}
+
deep-equal@2.2.3:
resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==}
engines: {node: '>= 0.4'}
@@ -4974,6 +5160,10 @@ packages:
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+ deepmerge@4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+
default-browser-id@5.0.1:
resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==}
engines: {node: '>=18'}
@@ -5035,8 +5225,8 @@ packages:
detect-node@2.1.0:
resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==}
- devalue@5.6.2:
- resolution: {integrity: sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==}
+ devalue@5.6.3:
+ resolution: {integrity: sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==}
di@0.0.1:
resolution: {integrity: sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==}
@@ -5347,6 +5537,16 @@ packages:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0'
+ eslint-plugin-svelte@3.15.0:
+ resolution: {integrity: sha512-QKB7zqfuB8aChOfBTComgDptMf2yxiJx7FE04nneCmtQzgTHvY8UJkuh8J2Rz7KB9FFV9aTHX6r7rdYGvG8T9Q==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.1 || ^9.0.0 || ^10.0.0
+ svelte: ^3.37.0 || ^4.0.0 || ^5.0.0
+ peerDependenciesMeta:
+ svelte:
+ optional: true
+
eslint-plugin-unused-imports@4.4.1:
resolution: {integrity: sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==}
peerDependencies:
@@ -5372,6 +5572,10 @@ packages:
resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ eslint-visitor-keys@5.0.1:
+ resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
eslint@9.39.2:
resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -5687,6 +5891,10 @@ packages:
resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
engines: {node: '>=18'}
+ globals@16.5.0:
+ resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==}
+ engines: {node: '>=18'}
+
globals@17.3.0:
resolution: {integrity: sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==}
engines: {node: '>=18'}
@@ -6268,6 +6476,10 @@ packages:
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
engines: {node: '>=0.10.0'}
+ kleur@4.1.5:
+ resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
+ engines: {node: '>=6'}
+
knip@5.86.0:
resolution: {integrity: sha512-tGpRCbP+L+VysXnAp1bHTLQ0k/SdC3M3oX18+Cpiqax1qdS25iuCPzpK8LVmAKARZv0Ijri81Wq09Rzk0JTl+Q==}
engines: {node: '>=18.18.0'}
@@ -6276,6 +6488,9 @@ packages:
'@types/node': '>=18'
typescript: '>=5.0.4 <7'
+ known-css-properties@0.37.0:
+ resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==}
+
kolorist@1.8.0:
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
@@ -6312,6 +6527,10 @@ packages:
webpack:
optional: true
+ lilconfig@2.1.0:
+ resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
+ engines: {node: '>=10'}
+
lilconfig@3.1.3:
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
engines: {node: '>=14'}
@@ -6981,6 +7200,18 @@ packages:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
+ postcss-load-config@3.1.4:
+ resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
+ engines: {node: '>= 10'}
+ peerDependencies:
+ postcss: '>=8.0.9'
+ ts-node: '>=9.0.0'
+ peerDependenciesMeta:
+ postcss:
+ optional: true
+ ts-node:
+ optional: true
+
postcss-loader@8.2.0:
resolution: {integrity: sha512-tHX+RkpsXVcc7st4dSdDGliI+r4aAQDuv+v3vFYHixb6YgjreG5AG4SEB0kDK8u2s6htqEEpKlkhSBUTvWKYnA==}
engines: {node: '>= 18.12.0'}
@@ -7027,6 +7258,12 @@ packages:
peerDependencies:
postcss: ^8.4.31
+ postcss-scss@4.0.9:
+ resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==}
+ engines: {node: '>=12.0'}
+ peerDependencies:
+ postcss: ^8.4.29
+
postcss-selector-parser@7.1.1:
resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==}
engines: {node: '>=4'}
@@ -7396,6 +7633,9 @@ packages:
resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==}
engines: {node: '>= 10.13.0'}
+ scule@1.3.0:
+ resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
+
select-hose@2.0.0:
resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==}
@@ -7763,8 +8003,23 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
- svelte@5.50.3:
- resolution: {integrity: sha512-5JCO8P/cFlwyfi1LeZ9uppMZvuaHWygyZmqxyKOIqbV3PoHKaddvV1C6njL/InpDXplNYZnAVEbn8mLslycBxQ==}
+ svelte-eslint-parser@1.4.1:
+ resolution: {integrity: sha512-1eqkfQ93goAhjAXxZiu1SaKI9+0/sxp4JIWQwUpsz7ybehRE5L8dNuz7Iry7K22R47p5/+s9EM+38nHV2OlgXA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0, pnpm: 10.24.0}
+ peerDependencies:
+ svelte: ^3.37.0 || ^4.0.0 || ^5.0.0
+ peerDependenciesMeta:
+ svelte:
+ optional: true
+
+ svelte2tsx@0.7.51:
+ resolution: {integrity: sha512-YbVMQi5LtQkVGOMdATTY8v3SMtkNjzYtrVDGaN3Bv+0LQ47tGXu/Oc8ryTkcYuEJWTZFJ8G2+2I8ORcQVGt9Ag==}
+ peerDependencies:
+ svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0
+ typescript: ^4.9.4 || ^5.0.0
+
+ svelte@5.53.7:
+ resolution: {integrity: sha512-uxck1KI7JWtlfP3H6HOWi/94soAl23jsGJkBzN2BAWcQng0+lTrRNhxActFqORgnO9BHVd1hKJhG+ljRuIUWfQ==}
engines: {node: '>=18'}
tapable@2.3.0:
@@ -8383,6 +8638,10 @@ packages:
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
engines: {node: '>=18'}
+ yaml@1.10.2:
+ resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
+ engines: {node: '>= 6'}
+
yaml@2.8.2:
resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==}
engines: {node: '>= 14.6'}
@@ -8571,35 +8830,35 @@ snapshots:
'@babel/preset-env': 7.29.0(@babel/core@7.29.0)
'@babel/runtime': 7.28.6
'@discoveryjs/json-ext': 0.6.3
- '@ngtools/webpack': 21.2.1(@angular/compiler-cli@21.2.1(@angular/compiler@21.2.1)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.105.2)
+ '@ngtools/webpack': 21.2.1(@angular/compiler-cli@21.2.1(@angular/compiler@21.2.1)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.105.2(esbuild@0.27.3))
ansi-colors: 4.1.3
autoprefixer: 10.4.27(postcss@8.5.6)
- babel-loader: 10.0.0(@babel/core@7.29.0)(webpack@5.105.2)
+ babel-loader: 10.0.0(@babel/core@7.29.0)(webpack@5.105.2(esbuild@0.27.3))
browserslist: 4.28.1
- copy-webpack-plugin: 14.0.0(webpack@5.105.2)
- css-loader: 7.1.3(webpack@5.105.2)
+ copy-webpack-plugin: 14.0.0(webpack@5.105.2(esbuild@0.27.3))
+ css-loader: 7.1.3(webpack@5.105.2(esbuild@0.27.3))
esbuild-wasm: 0.27.3
http-proxy-middleware: 3.0.5
istanbul-lib-instrument: 6.0.3
jsonc-parser: 3.3.1
karma-source-map-support: 1.4.0
less: 4.4.2
- less-loader: 12.3.1(less@4.4.2)(webpack@5.105.2)
- license-webpack-plugin: 4.0.2(webpack@5.105.2)
+ less-loader: 12.3.1(less@4.4.2)(webpack@5.105.2(esbuild@0.27.3))
+ license-webpack-plugin: 4.0.2(webpack@5.105.2(esbuild@0.27.3))
loader-utils: 3.3.1
- mini-css-extract-plugin: 2.10.0(webpack@5.105.2)
+ mini-css-extract-plugin: 2.10.0(webpack@5.105.2(esbuild@0.27.3))
open: 11.0.0
ora: 9.3.0
picomatch: 4.0.3
piscina: 5.1.4
postcss: 8.5.6
- postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.105.2)
+ postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.105.2(esbuild@0.27.3))
resolve-url-loader: 5.0.0
rxjs: 7.8.2
sass: 1.97.3
- sass-loader: 16.0.7(sass@1.97.3)(webpack@5.105.2)
+ sass-loader: 16.0.7(sass@1.97.3)(webpack@5.105.2(esbuild@0.27.3))
semver: 7.7.4
- source-map-loader: 5.0.0(webpack@5.105.2)
+ source-map-loader: 5.0.0(webpack@5.105.2(esbuild@0.27.3))
source-map-support: 0.5.21
terser: 5.46.0
tinyglobby: 0.2.15
@@ -8610,7 +8869,7 @@ snapshots:
webpack-dev-middleware: 7.4.5(tslib@2.8.1)(webpack@5.105.2(esbuild@0.27.3))
webpack-dev-server: 5.2.3(tslib@2.8.1)(webpack@5.105.2(esbuild@0.27.3))
webpack-merge: 6.0.1
- webpack-subresource-integrity: 5.1.0(webpack@5.105.2)
+ webpack-subresource-integrity: 5.1.0(webpack@5.105.2(esbuild@0.27.3))
optionalDependencies:
'@angular/core': 21.2.1(@angular/compiler@21.2.1)(rxjs@7.8.2)(zone.js@0.16.1)
'@angular/platform-browser': 21.2.1(@angular/common@21.2.1(@angular/core@21.2.1(@angular/compiler@21.2.1)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@21.2.1(@angular/compiler@21.2.1)(rxjs@7.8.2)(zone.js@0.16.1))
@@ -10443,7 +10702,7 @@ snapshots:
'@tybys/wasm-util': 0.10.1
optional: true
- '@ngtools/webpack@21.2.1(@angular/compiler-cli@21.2.1(@angular/compiler@21.2.1)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.105.2)':
+ '@ngtools/webpack@21.2.1(@angular/compiler-cli@21.2.1(@angular/compiler@21.2.1)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.105.2(esbuild@0.27.3))':
dependencies:
'@angular/compiler-cli': 21.2.1(@angular/compiler@21.2.1)(typescript@5.9.3)
typescript: 5.9.3
@@ -11135,6 +11394,34 @@ snapshots:
dependencies:
acorn: 8.15.0
+ '@sveltejs/package@2.5.7(svelte@5.53.7)(typescript@5.9.3)':
+ dependencies:
+ chokidar: 5.0.0
+ kleur: 4.1.5
+ sade: 1.8.1
+ semver: 7.7.4
+ svelte: 5.53.7
+ svelte2tsx: 0.7.51(svelte@5.53.7)(typescript@5.9.3)
+ transitivePeerDependencies:
+ - typescript
+
+ '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)))(svelte@5.53.7)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))':
+ dependencies:
+ '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
+ obug: 2.1.1
+ svelte: 5.53.7
+ vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)
+
+ '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))':
+ dependencies:
+ '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)))(svelte@5.53.7)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
+ deepmerge: 4.3.1
+ magic-string: 0.30.21
+ obug: 2.1.1
+ svelte: 5.53.7
+ vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)
+ vitefu: 1.1.1(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))
+
'@svitejs/changesets-changelog-github-compact@1.2.0(encoding@0.1.13)':
dependencies:
'@changesets/get-github-info': 0.6.0(encoding@0.1.13)
@@ -11490,6 +11777,8 @@ snapshots:
dependencies:
'@types/node': 25.3.5
+ '@types/trusted-types@2.0.7': {}
+
'@types/unist@3.0.3': {}
'@types/whatwg-mimetype@3.0.2': {}
@@ -11526,6 +11815,18 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@typescript-eslint/parser@8.56.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.56.1
+ '@typescript-eslint/types': 8.56.1
+ '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.56.1
+ debug: 4.4.3
+ eslint: 9.39.2(jiti@2.6.1)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
'@typescript-eslint/project-service@8.55.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.55.0(typescript@5.9.3)
@@ -11535,15 +11836,33 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@typescript-eslint/project-service@8.56.1(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.3)
+ '@typescript-eslint/types': 8.56.1
+ debug: 4.4.3
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
'@typescript-eslint/scope-manager@8.55.0':
dependencies:
'@typescript-eslint/types': 8.55.0
'@typescript-eslint/visitor-keys': 8.55.0
+ '@typescript-eslint/scope-manager@8.56.1':
+ dependencies:
+ '@typescript-eslint/types': 8.56.1
+ '@typescript-eslint/visitor-keys': 8.56.1
+
'@typescript-eslint/tsconfig-utils@8.55.0(typescript@5.9.3)':
dependencies:
typescript: 5.9.3
+ '@typescript-eslint/tsconfig-utils@8.56.1(typescript@5.9.3)':
+ dependencies:
+ typescript: 5.9.3
+
'@typescript-eslint/type-utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@typescript-eslint/types': 8.55.0
@@ -11558,6 +11877,8 @@ snapshots:
'@typescript-eslint/types@8.55.0': {}
+ '@typescript-eslint/types@8.56.1': {}
+
'@typescript-eslint/typescript-estree@8.55.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/project-service': 8.55.0(typescript@5.9.3)
@@ -11573,6 +11894,21 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@typescript-eslint/typescript-estree@8.56.1(typescript@5.9.3)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.56.1(typescript@5.9.3)
+ '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.3)
+ '@typescript-eslint/types': 8.56.1
+ '@typescript-eslint/visitor-keys': 8.56.1
+ debug: 4.4.3
+ minimatch: 10.2.4
+ semver: 7.7.4
+ tinyglobby: 0.2.15
+ ts-api-utils: 2.4.0(typescript@5.9.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
'@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
@@ -11589,6 +11925,11 @@ snapshots:
'@typescript-eslint/types': 8.55.0
eslint-visitor-keys: 4.2.1
+ '@typescript-eslint/visitor-keys@8.56.1':
+ dependencies:
+ '@typescript-eslint/types': 8.56.1
+ eslint-visitor-keys: 5.0.1
+
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
optional: true
@@ -11975,6 +12316,8 @@ snapshots:
dependencies:
dequal: 2.0.3
+ aria-query@5.3.1: {}
+
aria-query@5.3.2: {}
array-buffer-byte-length@1.0.2:
@@ -12027,7 +12370,7 @@ snapshots:
axobject-query@4.1.0: {}
- babel-loader@10.0.0(@babel/core@7.29.0)(webpack@5.105.2):
+ babel-loader@10.0.0(@babel/core@7.29.0)(webpack@5.105.2(esbuild@0.27.3)):
dependencies:
'@babel/core': 7.29.0
find-up: 5.0.0
@@ -12429,7 +12772,7 @@ snapshots:
dependencies:
is-what: 3.14.1
- copy-webpack-plugin@14.0.0(webpack@5.105.2):
+ copy-webpack-plugin@14.0.0(webpack@5.105.2(esbuild@0.27.3)):
dependencies:
glob-parent: 6.0.2
normalize-path: 3.0.0
@@ -12464,7 +12807,7 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
- css-loader@7.1.3(webpack@5.105.2):
+ css-loader@7.1.3(webpack@5.105.2(esbuild@0.27.3)):
dependencies:
icss-utils: 5.1.0(postcss@8.5.6)
postcss: 8.5.6
@@ -12519,6 +12862,8 @@ snapshots:
dependencies:
ms: 2.1.3
+ dedent-js@1.0.1: {}
+
deep-equal@2.2.3:
dependencies:
array-buffer-byte-length: 1.0.2
@@ -12542,6 +12887,8 @@ snapshots:
deep-is@0.1.4: {}
+ deepmerge@4.3.1: {}
+
default-browser-id@5.0.1: {}
default-browser@5.5.0:
@@ -12588,7 +12935,7 @@ snapshots:
detect-node@2.1.0: {}
- devalue@5.6.2: {}
+ devalue@5.6.3: {}
di@0.0.1: {}
@@ -12999,6 +13346,24 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ eslint-plugin-svelte@3.15.0(eslint@9.39.2(jiti@2.6.1))(svelte@5.53.7):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
+ '@jridgewell/sourcemap-codec': 1.5.5
+ eslint: 9.39.2(jiti@2.6.1)
+ esutils: 2.0.3
+ globals: 16.5.0
+ known-css-properties: 0.37.0
+ postcss: 8.5.6
+ postcss-load-config: 3.1.4(postcss@8.5.6)
+ postcss-safe-parser: 7.0.1(postcss@8.5.6)
+ semver: 7.7.4
+ svelte-eslint-parser: 1.4.1(svelte@5.53.7)
+ optionalDependencies:
+ svelte: 5.53.7
+ transitivePeerDependencies:
+ - ts-node
+
eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.55.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)):
dependencies:
eslint: 9.39.2(jiti@2.6.1)
@@ -13019,6 +13384,8 @@ snapshots:
eslint-visitor-keys@4.2.1: {}
+ eslint-visitor-keys@5.0.1: {}
+
eslint@9.39.2(jiti@2.6.1):
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
@@ -13426,6 +13793,8 @@ snapshots:
globals@15.15.0: {}
+ globals@16.5.0: {}
+
globals@17.3.0: {}
globby@11.1.0:
@@ -14011,6 +14380,8 @@ snapshots:
kind-of@6.0.3: {}
+ kleur@4.1.5: {}
+
knip@5.86.0(@types/node@25.3.5)(typescript@5.9.3):
dependencies:
'@nodelib/fs.walk': 1.2.8
@@ -14029,6 +14400,8 @@ snapshots:
yaml: 2.8.2
zod: 4.3.6
+ known-css-properties@0.37.0: {}
+
kolorist@1.8.0: {}
launch-editor@2.13.0:
@@ -14036,7 +14409,7 @@ snapshots:
picocolors: 1.1.1
shell-quote: 1.8.3
- less-loader@12.3.1(less@4.4.2)(webpack@5.105.2):
+ less-loader@12.3.1(less@4.4.2)(webpack@5.105.2(esbuild@0.27.3)):
dependencies:
less: 4.4.2
optionalDependencies:
@@ -14061,12 +14434,14 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
- license-webpack-plugin@4.0.2(webpack@5.105.2):
+ license-webpack-plugin@4.0.2(webpack@5.105.2(esbuild@0.27.3)):
dependencies:
webpack-sources: 3.3.4
optionalDependencies:
webpack: 5.105.2(esbuild@0.27.3)
+ lilconfig@2.1.0: {}
+
lilconfig@3.1.3: {}
lines-and-columns@1.2.4: {}
@@ -14284,7 +14659,7 @@ snapshots:
min-indent@1.0.1: {}
- mini-css-extract-plugin@2.10.0(webpack@5.105.2):
+ mini-css-extract-plugin@2.10.0(webpack@5.105.2(esbuild@0.27.3)):
dependencies:
schema-utils: 4.3.3
tapable: 2.3.0
@@ -14849,7 +15224,14 @@ snapshots:
possible-typed-array-names@1.1.0: {}
- postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.105.2):
+ postcss-load-config@3.1.4(postcss@8.5.6):
+ dependencies:
+ lilconfig: 2.1.0
+ yaml: 1.10.2
+ optionalDependencies:
+ postcss: 8.5.6
+
+ postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.105.2(esbuild@0.27.3)):
dependencies:
cosmiconfig: 9.0.0(typescript@5.9.3)
jiti: 2.6.1
@@ -14887,6 +15269,10 @@ snapshots:
dependencies:
postcss: 8.5.6
+ postcss-scss@4.0.9(postcss@8.5.6):
+ dependencies:
+ postcss: 8.5.6
+
postcss-selector-parser@7.1.1:
dependencies:
cssesc: 3.0.0
@@ -14908,10 +15294,10 @@ snapshots:
premove@4.0.0: {}
- prettier-plugin-svelte@3.5.1(prettier@3.8.1)(svelte@5.50.3):
+ prettier-plugin-svelte@3.5.1(prettier@3.8.1)(svelte@5.53.7):
dependencies:
prettier: 3.8.1
- svelte: 5.50.3
+ svelte: 5.53.7
prettier@2.8.8: {}
@@ -15258,7 +15644,7 @@ snapshots:
safer-buffer@2.1.2: {}
- sass-loader@16.0.7(sass@1.97.3)(webpack@5.105.2):
+ sass-loader@16.0.7(sass@1.97.3)(webpack@5.105.2(esbuild@0.27.3)):
dependencies:
neo-async: 2.6.2
optionalDependencies:
@@ -15285,6 +15671,8 @@ snapshots:
ajv-formats: 2.1.1(ajv@8.18.0)
ajv-keywords: 5.1.0(ajv@8.18.0)
+ scule@1.3.0: {}
+
select-hose@2.0.0: {}
selfsigned@5.5.0:
@@ -15581,7 +15969,7 @@ snapshots:
source-map-js@1.2.1: {}
- source-map-loader@5.0.0(webpack@5.105.2):
+ source-map-loader@5.0.0(webpack@5.105.2(esbuild@0.27.3)):
dependencies:
iconv-lite: 0.6.3
source-map-js: 1.2.1
@@ -15727,17 +16115,36 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
- svelte@5.50.3:
+ svelte-eslint-parser@1.4.1(svelte@5.53.7):
+ dependencies:
+ eslint-scope: 8.4.0
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
+ postcss: 8.5.6
+ postcss-scss: 4.0.9(postcss@8.5.6)
+ postcss-selector-parser: 7.1.1
+ optionalDependencies:
+ svelte: 5.53.7
+
+ svelte2tsx@0.7.51(svelte@5.53.7)(typescript@5.9.3):
+ dependencies:
+ dedent-js: 1.0.1
+ scule: 1.3.0
+ svelte: 5.53.7
+ typescript: 5.9.3
+
+ svelte@5.53.7:
dependencies:
'@jridgewell/remapping': 2.3.5
'@jridgewell/sourcemap-codec': 1.5.5
'@sveltejs/acorn-typescript': 1.0.9(acorn@8.15.0)
'@types/estree': 1.0.8
+ '@types/trusted-types': 2.0.7
acorn: 8.15.0
- aria-query: 5.3.2
+ aria-query: 5.3.1
axobject-query: 4.1.0
clsx: 2.1.1
- devalue: 5.6.2
+ devalue: 5.6.3
esm-env: 1.2.2
esrap: 2.2.3
is-reference: 3.0.3
@@ -16211,7 +16618,7 @@ snapshots:
webpack-sources@3.3.4: {}
- webpack-subresource-integrity@5.1.0(webpack@5.105.2):
+ webpack-subresource-integrity@5.1.0(webpack@5.105.2(esbuild@0.27.3)):
dependencies:
typed-assert: 1.0.9
webpack: 5.105.2(esbuild@0.27.3)
@@ -16358,6 +16765,8 @@ snapshots:
yallist@5.0.0: {}
+ yaml@1.10.2: {}
+
yaml@2.8.2: {}
yargs-parser@20.2.9: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 5add109..2113f35 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -5,3 +5,5 @@ preferWorkspacePackages: true
packages:
- 'examples/**/*'
- 'packages/*'
+onlyBuiltDependencies:
+ - esbuild
diff --git a/scripts/generate-docs.ts b/scripts/generate-docs.ts
index 0c9ebdf..8047ed2 100644
--- a/scripts/generate-docs.ts
+++ b/scripts/generate-docs.ts
@@ -70,6 +70,18 @@ await generateReferenceDocs({
outputDir: resolve(__dirname, '../docs/framework/vue/reference'),
exclude: ['packages/hotkeys/**/*'],
},
+ {
+ name: 'svelte-hotkeys',
+ entryPoints: [
+ resolve(__dirname, '../packages/svelte-hotkeys/src/index.ts'),
+ ],
+ tsconfig: resolve(
+ __dirname,
+ '../packages/svelte-hotkeys/tsconfig.docs.json',
+ ),
+ outputDir: resolve(__dirname, '../docs/framework/svelte/reference'),
+ exclude: ['packages/hotkeys/**/*'],
+ },
],
})
diff --git a/vitest.workspace.ts b/vitest.workspace.ts
index 5d00262..0e0f245 100644
--- a/vitest.workspace.ts
+++ b/vitest.workspace.ts
@@ -12,6 +12,8 @@ export default defineConfig({
'./packages/solid-hotkeys-devtools/vitest.config.ts',
'./packages/solid-hotkeys/vitest.config.ts',
'./packages/angular-hotkeys/vitest.config.ts',
+ './packages/vue-hotkeys/vitest.config.ts',
+ './packages/svelte-hotkeys/vitest.config.ts',
],
},
})