From aa808a6ad11308daf6f5f930b3636a80f250e5d6 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Wed, 14 Oct 2026 10:00:24 -0500 Subject: [PATCH 1/7] feat: Unify hotkeys and sequences and add app hotkeys Hotkey APIs accept chords and sequences, the recorder records sequences with maxSteps, and app hotkeys are declared with defineAppHotkeys and customized through each adapter's provider. --- .changeset/app-hotkeys.md | 44 + docs/config.json | 1664 ++++++----- docs/devtools.md | 4 +- docs/framework/alpine/guides/app-hotkeys.md | 211 ++ .../alpine/guides/formatting-display.md | 40 +- .../alpine/guides/hotkey-recording.md | 249 +- docs/framework/alpine/guides/hotkeys.md | 28 +- .../alpine/guides/key-state-tracking.md | 2 +- .../alpine/guides/sequence-recording.md | 111 - docs/framework/alpine/guides/sequences.md | 167 +- docs/framework/alpine/quick-start.md | 16 +- .../reference/functions/createHotkeysScope.md | 6 +- docs/framework/alpine/reference/index.md | 9 +- .../interfaces/AlpineCreateHotkey.md | 82 + .../interfaces/AlpineCreateHotkeyRecorder.md | 108 + .../interfaces/AlpineCustomHotkeys.md | 111 + .../interfaces/AlpineHotkeyRecorder.md | 50 +- .../AlpineHotkeySequenceRecorder.md | 108 - .../reference/interfaces/AlpineHotkeyState.md | 4 +- .../reference/interfaces/AlpineHotkeys.md | 171 +- .../interfaces/DefaultHotkeysOptions.md | 32 +- .../interfaces/HotkeyRegistrationsResult.md | 16 +- .../type-aliases/AlpineAppHotkeyOptions.md | 18 + .../type-aliases/AlpineHotkeyCommonOptions.md | 12 + .../type-aliases/AlpineHotkeyDefinition.md | 12 + .../type-aliases/AlpineHotkeyOptionsFor.md | 18 + .../type-aliases/AlpineHotkeyTarget.md | 12 + .../reference/type-aliases/MaybeGetter.md | 2 +- docs/framework/angular/guides/app-hotkeys.md | 205 ++ .../angular/guides/formatting-display.md | 24 +- .../angular/guides/hotkey-recording.md | 214 +- docs/framework/angular/guides/hotkeys.md | 48 +- .../angular/guides/key-state-tracking.md | 2 +- .../angular/guides/sequence-recording.md | 65 - docs/framework/angular/guides/sequences.md | 186 +- docs/framework/angular/quick-start.md | 20 +- .../functions/injectAppHotkeyList.md | 18 + .../reference/functions/injectAppHotkeys.md | 26 + .../functions/injectCustomHotkeys.md | 18 + .../functions/injectDefaultHotkeysOptions.md | 2 +- .../reference/functions/injectHotkey.md | 118 +- .../reference/functions/injectHotkeyHint.md | 16 +- .../functions/injectHotkeyRecorder.md | 222 +- .../functions/injectHotkeyRegistrations.md | 34 +- .../functions/injectHotkeySequence.md | 65 - .../functions/injectHotkeySequenceRecorder.md | 22 - .../functions/injectHotkeySequences.md | 55 - .../reference/functions/injectHotkeys.md | 62 +- .../functions/injectHotkeysContext.md | 2 +- .../reference/functions/provideHotkeys.md | 28 +- docs/framework/angular/reference/index.md | 25 +- .../interfaces/AngularHotkeyRecorder.md | 60 +- .../AngularHotkeySequenceRecorder.md | 104 - .../interfaces/HotkeyRegistrationsResult.md | 32 - .../interfaces/HotkeysContextValue.md | 30 +- .../interfaces/HotkeysProviderOptions.md | 33 +- .../interfaces/InjectCustomHotkeysResult.md | 111 + .../interfaces/InjectHotkeyDefinition.md | 46 - .../interfaces/InjectHotkeyOptions.md | 26 - .../InjectHotkeySequenceDefinition.md | 46 - .../interfaces/InjectHotkeySequenceOptions.md | 36 - .../interfaces/ProvideHotkeysOptions.md | 68 + .../type-aliases/InjectAppHotkeyOptions.md | 18 + .../type-aliases/InjectHotkeyChordOptions.md | 12 + .../type-aliases/InjectHotkeyCommonOptions.md | 12 + .../type-aliases/InjectHotkeyDefinition.md | 13 + .../type-aliases/InjectHotkeyOptions.md | 12 + .../type-aliases/InjectHotkeyOptionsFor.md | 18 + .../InjectHotkeySequenceOptions.md | 12 + .../type-aliases/InjectHotkeyTarget.md | 14 + .../variables/HOTKEYS_INJECTION_TOKEN.md | 2 +- docs/framework/ember/guides/app-hotkeys.md | 201 ++ .../ember/guides/formatting-display.md | 40 +- .../ember/guides/hotkey-recording.md | 259 +- docs/framework/ember/guides/hotkeys.md | 36 +- .../ember/guides/key-state-tracking.md | 2 +- .../ember/guides/sequence-recording.md | 101 - docs/framework/ember/guides/sequences.md | 185 +- docs/framework/ember/quick-start.md | 20 +- .../ember/reference/classes/onHotkey.md | 22 +- .../ember/reference/classes/onHotkeys.md | 16 +- .../ember/reference/classes/useHotkey.md | 27 +- .../reference/classes/useHotkeySequence.md | 2515 ---------------- .../reference/classes/useHotkeySequences.md | 2518 ----------------- .../ember/reference/classes/useHotkeys.md | 21 +- .../reference/functions/createHotkeysScope.md | 2 +- .../reference/functions/useAppHotkeyList.md | 24 + .../reference/functions/useAppHotkeys.md | 22 + .../reference/functions/useCustomHotkeys.md | 24 + .../reference/functions/useHotkeyHint.md | 10 +- .../reference/functions/useHotkeyRecorder.md | 132 +- .../functions/useHotkeyRegistrations.md | 4 +- .../functions/useHotkeySequenceRecorder.md | 26 - docs/framework/ember/reference/index.md | 12 +- .../interfaces/DefaultHotkeysOptions.md | 32 +- .../interfaces/EmberCustomHotkeys.md | 111 + .../interfaces/EmberHotkeyRecorder.md | 50 +- .../interfaces/EmberHotkeySequenceRecorder.md | 108 - .../reference/interfaces/EmberHotkeyState.md | 4 +- .../reference/interfaces/EmberHotkeysScope.md | 146 +- .../interfaces/HotkeyRegistrationsResult.md | 16 +- .../type-aliases/ElementHotkeyOptions.md | 4 +- .../type-aliases/EmberAppHotkeyList.md | 12 + .../type-aliases/EmberHotkeyCommonOptions.md | 12 + .../type-aliases/EmberHotkeyDefinition.md | 12 + .../type-aliases/EmberHotkeyTarget.md | 12 + .../reference/type-aliases/MaybeGetter.md | 2 +- docs/framework/lit/guides/app-hotkeys.md | 197 ++ .../lit/guides/formatting-display.md | 24 +- docs/framework/lit/guides/hotkey-recording.md | 260 +- docs/framework/lit/guides/hotkeys.md | 69 +- .../lit/guides/key-state-tracking.md | 2 +- .../lit/guides/sequence-recording.md | 147 - docs/framework/lit/guides/sequences.md | 316 +-- docs/framework/lit/quick-start.md | 15 +- .../reference/classes/AppHotkeysController.md | 244 ++ .../lit/reference/classes/HotkeyController.md | 77 +- .../reference/classes/HotkeyHintController.md | 16 +- .../classes/HotkeyRecorderController.md | 160 +- .../classes/HotkeyRegistrationsController.md | 49 +- .../classes/HotkeySequenceController.md | 136 - .../HotkeySequenceRecorderController.md | 259 -- .../reference/classes/KeyHoldController.md | 4 + .../lit/reference/functions/hotkey.md | 89 +- .../lit/reference/functions/hotkeySequence.md | 46 - docs/framework/lit/reference/index.md | 14 +- .../AppHotkeyControllerOptions.md | 18 + .../HotkeyControllerChordOptions.md | 12 + .../HotkeyControllerCommonOptions.md | 12 + .../type-aliases/HotkeyControllerOptions.md | 12 + .../HotkeyControllerOptionsFor.md | 18 + .../HotkeyControllerSequenceOptions.md | 12 + .../type-aliases/HotkeyControllerTarget.md | 12 + docs/framework/octane/guides/app-hotkeys.md | 190 ++ .../octane/guides/formatting-display.md | 40 +- .../octane/guides/hotkey-recording.md | 230 +- docs/framework/octane/guides/hotkeys.md | 36 +- .../octane/guides/key-state-tracking.md | 2 +- .../octane/guides/sequence-recording.md | 92 - docs/framework/octane/guides/sequences.md | 157 +- docs/framework/octane/quick-start.md | 16 +- .../reference/functions/HotkeysProvider.md | 16 +- .../reference/functions/useAppHotkeyList.md | 18 + .../reference/functions/useAppHotkeys.md | 17 + .../reference/functions/useCustomHotkeys.md | 18 + .../functions/useDefaultHotkeysOptions.md | 2 +- .../octane/reference/functions/useHotkey.md | 69 +- .../reference/functions/useHotkeyHint.md | 9 +- .../reference/functions/useHotkeyRecorder.md | 42 +- .../functions/useHotkeyRegistrations.md | 4 +- .../reference/functions/useHotkeySequence.md | 33 - .../functions/useHotkeySequenceRecorder.md | 22 - .../reference/functions/useHotkeySequences.md | 26 - .../octane/reference/functions/useHotkeys.md | 11 +- .../reference/functions/useHotkeysContext.md | 13 +- docs/framework/octane/reference/index.md | 16 +- .../interfaces/DefaultHotkeysOptions.md | 33 +- .../interfaces/HotkeyRegistrationsResult.md | 16 +- .../interfaces/HotkeysProviderProps.md | 56 +- .../interfaces/OctaneHotkeyRecorder.md | 50 +- .../OctaneHotkeySequenceRecorder.md | 108 - .../interfaces/UseCustomHotkeysResult.md | 111 + .../type-aliases/HotkeysProviderOptions.md | 2 +- .../type-aliases/UseAppHotkeyOptions.md | 18 + .../type-aliases/UseHotkeyChordOptions.md | 12 + .../type-aliases/UseHotkeyCommonOptions.md | 12 + .../type-aliases/UseHotkeyDefinition.md | 13 + .../type-aliases/UseHotkeyOptions.md | 12 + .../type-aliases/UseHotkeyOptionsFor.md | 18 + .../type-aliases/UseHotkeySequenceOptions.md | 12 + .../reference/type-aliases/UseHotkeyTarget.md | 12 + docs/framework/preact/guides/app-hotkeys.md | 210 ++ .../preact/guides/formatting-display.md | 24 +- .../preact/guides/hotkey-recording.md | 136 +- docs/framework/preact/guides/hotkeys.md | 117 +- .../preact/guides/key-state-tracking.md | 18 +- .../preact/guides/sequence-recording.md | 108 - docs/framework/preact/guides/sequences.md | 159 +- docs/framework/preact/quick-start.md | 220 ++ .../reference/functions/HotkeysProvider.md | 28 +- .../reference/functions/useAppHotkeyList.md | 36 + .../reference/functions/useAppHotkeys.md | 25 + .../reference/functions/useCustomHotkeys.md | 30 + .../functions/useDefaultHotkeysOptions.md | 2 +- .../preact/reference/functions/useHotkey.md | 152 +- .../reference/functions/useHotkeyHint.md | 11 +- .../reference/functions/useHotkeyRecorder.md | 113 +- .../functions/useHotkeyRegistrations.md | 35 +- .../reference/functions/useHotkeySequence.md | 76 - .../functions/useHotkeySequenceRecorder.md | 22 - .../reference/functions/useHotkeySequences.md | 68 - .../preact/reference/functions/useHotkeys.md | 31 +- .../reference/functions/useHotkeysContext.md | 2 +- docs/framework/preact/reference/index.md | 24 +- .../interfaces/HotkeyRegistrationsResult.md | 32 - .../interfaces/HotkeysProviderOptions.md | 33 +- .../interfaces/HotkeysProviderProps.md | 60 +- .../interfaces/PreactHotkeyRecorder.md | 54 +- .../PreactHotkeySequenceRecorder.md | 92 - .../interfaces/UseCustomHotkeysResult.md | 111 + .../interfaces/UseHotkeyDefinition.md | 44 - .../reference/interfaces/UseHotkeyOptions.md | 29 - .../interfaces/UseHotkeySequenceDefinition.md | 44 - .../interfaces/UseHotkeySequenceOptions.md | 29 - .../type-aliases/UseAppHotkeyOptions.md | 21 + .../type-aliases/UseHotkeyChordOptions.md | 12 + .../type-aliases/UseHotkeyCommonOptions.md | 12 + .../type-aliases/UseHotkeyDefinition.md | 13 + .../type-aliases/UseHotkeyOptions.md | 12 + .../type-aliases/UseHotkeyOptionsFor.md | 18 + .../type-aliases/UseHotkeySequenceOptions.md | 12 + .../reference/type-aliases/UseHotkeyTarget.md | 17 + docs/framework/react/guides/app-hotkeys.md | 210 ++ .../react/guides/formatting-display.md | 24 +- .../react/guides/hotkey-recording.md | 130 +- docs/framework/react/guides/hotkeys.md | 95 +- .../react/guides/key-state-tracking.md | 6 + .../react/guides/sequence-recording.md | 108 - docs/framework/react/guides/sequences.md | 154 +- docs/framework/react/quick-start.md | 53 +- .../reference/functions/HotkeysProvider.md | 28 +- .../reference/functions/useAppHotkeyList.md | 36 + .../reference/functions/useAppHotkeys.md | 25 + .../reference/functions/useCustomHotkeys.md | 30 + .../functions/useDefaultHotkeysOptions.md | 2 +- .../react/reference/functions/useHotkey.md | 150 +- .../reference/functions/useHotkeyHint.md | 11 +- .../reference/functions/useHotkeyRecorder.md | 113 +- .../functions/useHotkeyRegistrations.md | 28 +- .../reference/functions/useHotkeySequence.md | 76 - .../functions/useHotkeySequenceRecorder.md | 24 - .../reference/functions/useHotkeySequences.md | 68 - .../react/reference/functions/useHotkeys.md | 31 +- .../reference/functions/useHotkeysContext.md | 2 +- docs/framework/react/reference/index.md | 24 +- .../interfaces/HotkeyRegistrationsResult.md | 32 - .../interfaces/HotkeysProviderOptions.md | 33 +- .../interfaces/HotkeysProviderProps.md | 60 +- .../interfaces/ReactHotkeyRecorder.md | 54 +- .../interfaces/ReactHotkeySequenceRecorder.md | 100 - .../interfaces/UseCustomHotkeysResult.md | 111 + .../interfaces/UseHotkeyDefinition.md | 44 - .../reference/interfaces/UseHotkeyOptions.md | 29 - .../interfaces/UseHotkeySequenceDefinition.md | 44 - .../interfaces/UseHotkeySequenceOptions.md | 29 - .../type-aliases/UseAppHotkeyOptions.md | 21 + .../type-aliases/UseHotkeyChordOptions.md | 12 + .../type-aliases/UseHotkeyCommonOptions.md | 12 + .../type-aliases/UseHotkeyDefinition.md | 13 + .../type-aliases/UseHotkeyOptions.md | 12 + .../type-aliases/UseHotkeyOptionsFor.md | 18 + .../type-aliases/UseHotkeySequenceOptions.md | 12 + .../reference/type-aliases/UseHotkeyTarget.md | 17 + docs/framework/solid/guides/app-hotkeys.md | 204 ++ .../solid/guides/formatting-display.md | 24 +- .../solid/guides/hotkey-recording.md | 227 +- docs/framework/solid/guides/hotkeys.md | 59 +- .../solid/guides/key-state-tracking.md | 2 +- .../solid/guides/sequence-recording.md | 65 - docs/framework/solid/guides/sequences.md | 193 +- docs/framework/solid/quick-start.md | 163 ++ .../functions/createAppHotkeyList.md | 18 + .../solid/reference/functions/createHotkey.md | 121 +- .../reference/functions/createHotkeyHint.md | 15 +- .../functions/createHotkeyRecorder.md | 178 +- .../functions/createHotkeyRegistrations.md | 25 +- .../functions/createHotkeySequence.md | 76 - .../functions/createHotkeySequenceRecorder.md | 22 - .../functions/createHotkeySequences.md | 64 - .../reference/functions/createHotkeys.md | 53 +- .../reference/functions/useAppHotkeys.md | 24 + .../reference/functions/useCustomHotkeys.md | 18 + .../functions/useDefaultHotkeysOptions.md | 2 +- .../reference/functions/useHotkeysContext.md | 2 +- docs/framework/solid/reference/index.md | 24 +- .../interfaces/CreateHotkeyDefinition.md | 44 - .../interfaces/CreateHotkeyOptions.md | 26 - .../CreateHotkeySequenceDefinition.md | 44 - .../interfaces/CreateHotkeySequenceOptions.md | 23 - .../interfaces/HotkeyRegistrationsResult.md | 40 - .../interfaces/HotkeysProviderOptions.md | 33 +- .../interfaces/HotkeysProviderProps.md | 56 +- .../interfaces/SolidHotkeyRecorder.md | 60 +- .../interfaces/SolidHotkeySequenceRecorder.md | 104 - .../interfaces/UseCustomHotkeysResult.md | 115 + .../type-aliases/CreateAppHotkeyOptions.md | 18 + .../type-aliases/CreateHotkeyChordOptions.md | 12 + .../type-aliases/CreateHotkeyCommonOptions.md | 12 + .../type-aliases/CreateHotkeyDefinition.md | 13 + .../type-aliases/CreateHotkeyOptions.md | 12 + .../type-aliases/CreateHotkeyOptionsFor.md | 18 + .../CreateHotkeySequenceOptions.md | 12 + .../type-aliases/CreateHotkeyTarget.md | 14 + .../reference/variables/HotkeysProvider.md | 18 +- docs/framework/svelte/guides/app-hotkeys.md | 194 ++ .../svelte/guides/formatting-display.md | 24 +- .../svelte/guides/hotkey-recording.md | 205 +- docs/framework/svelte/guides/hotkeys.md | 58 +- .../svelte/guides/key-state-tracking.md | 2 +- .../svelte/guides/sequence-recording.md | 58 - docs/framework/svelte/guides/sequences.md | 183 +- docs/framework/svelte/quick-start.md | 14 +- .../reference/functions/createHotkey.md | 99 +- .../functions/createHotkeyAttachment.md | 87 +- .../functions/createHotkeyRecorder.md | 184 +- .../functions/createHotkeySequence.md | 70 - .../createHotkeySequenceAttachment.md | 49 - .../functions/createHotkeySequenceRecorder.md | 22 - .../functions/createHotkeySequences.md | 39 - .../createHotkeySequencesAttachment.md | 41 - .../reference/functions/createHotkeys.md | 19 +- .../functions/createHotkeysAttachment.md | 17 +- .../reference/functions/getAppHotkeyList.md | 18 + .../reference/functions/getAppHotkeys.md | 26 + .../reference/functions/getCustomHotkeys.md | 18 + .../functions/getDefaultHotkeysOptions.md | 2 +- .../reference/functions/getHotkeyHint.md | 16 +- .../functions/getHotkeyRegistrations.md | 19 +- .../reference/functions/getHotkeysContext.md | 2 +- .../reference/functions/setHotkeysContext.md | 24 +- docs/framework/svelte/reference/index.md | 25 +- .../interfaces/CreateHotkeyDefinition.md | 44 - .../interfaces/CreateHotkeyOptions.md | 20 - .../CreateHotkeySequenceDefinition.md | 44 - .../interfaces/CreateHotkeySequenceOptions.md | 20 - .../interfaces/HotkeysContextOptions.md | 72 + .../interfaces/HotkeysProviderOptions.md | 33 +- .../interfaces/HotkeysProviderProps.md | 78 +- .../interfaces/SvelteAppHotkeyList.md | 18 + .../reference/interfaces/SvelteAppHotkeys.md | 18 + .../interfaces/SvelteCustomHotkeys.md | 109 + .../reference/interfaces/SvelteHotkeyHint.md | 4 +- .../interfaces/SvelteHotkeyRecorder.md | 54 +- .../interfaces/SvelteHotkeyRegistrations.md | 14 +- .../SvelteHotkeySequenceRecorder.md | 92 - .../type-aliases/CreateAppHotkeyOptions.md | 18 + .../type-aliases/CreateHotkeyChordOptions.md | 12 + .../type-aliases/CreateHotkeyCommonOptions.md | 12 + .../type-aliases/CreateHotkeyDefinition.md | 166 ++ .../type-aliases/CreateHotkeyOptions.md | 12 + .../type-aliases/CreateHotkeyOptionsFor.md | 18 + .../CreateHotkeySequenceOptions.md | 12 + .../type-aliases/CreateHotkeyTarget.md | 12 + .../reference/variables/DEFAULT_OPTIONS.md | 2 +- docs/framework/vue/guides/app-hotkeys.md | 201 ++ .../vue/guides/formatting-display.md | 24 +- docs/framework/vue/guides/hotkey-recording.md | 220 +- docs/framework/vue/guides/hotkeys.md | 48 +- .../vue/guides/key-state-tracking.md | 2 +- .../vue/guides/sequence-recording.md | 111 - docs/framework/vue/guides/sequences.md | 169 +- docs/framework/vue/quick-start.md | 18 +- .../functions/provideHotkeysContext.md | 23 +- .../reference/functions/useAppHotkeyList.md | 18 + .../vue/reference/functions/useAppHotkeys.md | 24 + .../reference/functions/useCustomHotkeys.md | 18 + .../functions/useDefaultHotkeysOptions.md | 2 +- .../vue/reference/functions/useHotkey.md | 125 +- .../vue/reference/functions/useHotkeyHint.md | 14 +- .../reference/functions/useHotkeyRecorder.md | 186 +- .../functions/useHotkeyRegistrations.md | 29 +- .../reference/functions/useHotkeySequence.md | 79 - .../functions/useHotkeySequenceRecorder.md | 22 - .../reference/functions/useHotkeySequences.md | 68 - .../vue/reference/functions/useHotkeys.md | 52 +- .../reference/functions/useHotkeysContext.md | 2 +- docs/framework/vue/reference/index.md | 25 +- .../interfaces/HotkeyRegistrationsResult.md | 32 - .../interfaces/HotkeysProviderOptions.md | 33 +- .../ProvideHotkeysContextOptions.md | 70 + .../interfaces/UseCustomHotkeysResult.md | 111 + .../interfaces/UseHotkeyDefinition.md | 44 - .../reference/interfaces/UseHotkeyOptions.md | 38 - .../interfaces/UseHotkeySequenceDefinition.md | 44 - .../interfaces/UseHotkeySequenceOptions.md | 38 - .../reference/interfaces/VueHotkeyRecorder.md | 56 +- .../interfaces/VueHotkeySequenceRecorder.md | 92 - .../type-aliases/UseAppHotkeyOptions.md | 18 + .../type-aliases/UseHotkeyChordOptions.md | 12 + .../type-aliases/UseHotkeyCommonOptions.md | 12 + .../type-aliases/UseHotkeyDefinition.md | 166 ++ .../type-aliases/UseHotkeyOptions.md | 12 + .../type-aliases/UseHotkeyOptionsFor.md | 18 + .../type-aliases/UseHotkeySequenceOptions.md | 12 + .../reference/type-aliases/UseHotkeyTarget.md | 13 + .../reference/variables/HotkeysProvider.md | 41 +- docs/overview.md | 80 +- .../adapter/functions/createHotkeyBindings.md | 15 +- .../functions/createHotkeySequenceBindings.md | 46 - .../functions/prepareHotkeyDefinitions.md | 36 + .../functions/subscribePublishedAppHotkeys.md | 25 + docs/reference/adapter/index.md | 19 +- .../adapter/interfaces/HotkeyDefinition.md | 38 - .../interfaces/HotkeySequenceDefinition.md | 38 - .../PrepareHotkeyDefinitionsOptions.md | 76 + .../type-aliases/AdapterAppHotkeyOptions.md | 26 + .../type-aliases/AdapterChordOptions.md | 18 + .../type-aliases/AdapterCommonOptions.md | 18 + .../type-aliases/AdapterHotkeyDefinition.md | 174 ++ .../type-aliases/AdapterHotkeyOptions.md | 18 + .../adapter/type-aliases/AdapterOptionsFor.md | 22 + .../type-aliases/AdapterSequenceOptions.md | 18 + .../HotkeyBindingsCommonOptions.md | 12 + .../adapter/type-aliases/HotkeyDefinition.md | 23 + .../adapter/type-aliases/WithTarget.md | 32 + docs/reference/classes/AppHotkeysStore.md | 303 ++ docs/reference/classes/HotkeyManager.md | 106 +- docs/reference/classes/HotkeyRecorder.md | 98 +- .../classes/HotkeySequenceRecorder.md | 145 - docs/reference/classes/SequenceManager.md | 167 -- docs/reference/functions/areHotkeysEqual.md | 5 +- docs/reference/functions/assertValidHotkey.md | 4 +- docs/reference/functions/checkHotkey.md | 4 +- .../functions/createHotkeyHandler.md | 4 +- .../functions/createMultiHotkeyHandler.md | 2 +- .../functions/createSequenceMatcher.md | 2 +- docs/reference/functions/defineAppHotkeys.md | 40 + .../functions/findHotkeyConflicts.md | 5 +- docs/reference/functions/formatForDisplay.md | 96 +- docs/reference/functions/formatHotkey.md | 2 +- .../functions/formatHotkeySequence.md | 34 - docs/reference/functions/formatWithLabels.md | 28 - .../getAppHotkeyRegistrationOptions.md | 24 + docs/reference/functions/getHotkeyManager.md | 2 +- .../reference/functions/getSequenceManager.md | 17 - docs/reference/functions/hasNonModifierKey.md | 4 +- docs/reference/functions/isAppHotkey.md | 22 + docs/reference/functions/isCustomHotkey.md | 26 + docs/reference/functions/isHotkeySequence.md | 23 + docs/reference/functions/isModifierKey.md | 2 +- .../functions/matchesHeldModifiers.md | 3 +- .../functions/matchesKeyboardEvent.md | 4 +- docs/reference/functions/normalizeHotkey.md | 6 +- .../functions/normalizeHotkeyFromEvent.md | 6 +- .../functions/normalizeHotkeyFromParsed.md | 6 +- .../functions/normalizeRegisterableHotkey.md | 41 +- docs/reference/functions/parseHotkey.md | 4 +- .../reference/functions/parseKeyboardEvent.md | 2 +- .../functions/parseRegisterableHotkey.md | 4 +- docs/reference/functions/provideAppHotkeys.md | 65 + .../functions/publishAppHotkeysStore.md | 24 + .../functions/rawHotkeyToParsedHotkey.md | 2 +- docs/reference/functions/resolveAppHotkey.md | 33 + docs/reference/functions/simplifyHotkey.md | 29 + .../functions/toHotkeyRegistrationView.md | 9 +- docs/reference/functions/validateHotkey.md | 10 +- docs/reference/index.md | 59 +- docs/reference/interfaces/AppHotkey.md | 106 + docs/reference/interfaces/AppHotkeyInput.md | 59 + .../reference/interfaces/AppHotkeyListItem.md | 58 + docs/reference/interfaces/AppHotkeysState.md | 38 + .../interfaces/AppHotkeysStoreOptions.md | 60 + .../interfaces/CreateHotkeyHandlerOptions.md | 8 +- .../interfaces/FormatDisplayOptions.md | 26 +- .../interfaces/HotkeyCallbackContext.md | 10 +- .../interfaces/HotkeyChordOptions.md | 186 ++ ...tions.md => HotkeyChordRecorderOptions.md} | 78 +- .../interfaces/HotkeyChordRegistration.md | 174 ++ .../interfaces/HotkeyChordRegistrationView.md | 116 + docs/reference/interfaces/HotkeyConflict.md | 18 + .../interfaces/HotkeyConflictOptions.md | 17 +- docs/reference/interfaces/HotkeyMeta.md | 10 +- docs/reference/interfaces/HotkeyOptions.md | 55 +- .../interfaces/HotkeyRecorderState.md | 20 +- .../HotkeyRecorderValidationContext.md | 36 +- .../interfaces/HotkeyRegistration.md | 128 - .../interfaces/HotkeyRegistrationHandle.md | 27 +- .../interfaces/HotkeyRegistrationView.md | 93 - ...nceOptions.md => HotkeySequenceOptions.md} | 53 +- .../HotkeySequenceRecorderOptions.md | 122 +- .../interfaces/HotkeySequenceRecorderState.md | 44 - ...HotkeySequenceRecorderValidationContext.md | 26 - .../interfaces/HotkeySequenceRegistration.md | 174 ++ .../HotkeySequenceRegistrationView.md | 140 + .../interfaces/KeyboardEventMatch.md | 10 +- docs/reference/interfaces/RecorderOptions.md | 5 - .../reference/interfaces/RecorderRejection.md | 14 +- docs/reference/interfaces/Register.md | 18 + .../interfaces/SequenceRegistration.md | 108 - .../interfaces/SequenceRegistrationHandle.md | 83 - .../interfaces/SequenceRegistrationView.md | 95 - docs/reference/interfaces/ValidationResult.md | 8 +- .../type-aliases/AppHotkeyChordOptions.md | 12 + .../type-aliases/AppHotkeyDefaultKind.md | 18 + docs/reference/type-aliases/AppHotkeyId.md | 12 + .../type-aliases/AppHotkeyOptions.md | 12 + .../type-aliases/AppHotkeySequenceOptions.md | 12 + .../reference/type-aliases/AppHotkeyStatus.md | 16 + docs/reference/type-aliases/AppHotkeys.md | 12 + .../type-aliases/ConflictBehavior.md | 2 +- docs/reference/type-aliases/CustomHotkeys.md | 19 + .../type-aliases/DefinedAppHotkeys.md | 18 + docs/reference/type-aliases/DisplayHotkey.md | 9 +- .../type-aliases/DisplayHotkeyChord.md | 15 + docs/reference/type-aliases/Hotkey.md | 61 +- docs/reference/type-aliases/HotkeyCallback.md | 2 +- docs/reference/type-aliases/HotkeyChord.md | 67 + docs/reference/type-aliases/HotkeyConflict.md | 18 - .../type-aliases/HotkeyOptionsFor.md | 19 + .../type-aliases/HotkeyRecorderCommitKeys.md | 12 + .../type-aliases/HotkeyRecorderOptions.md | 15 + .../type-aliases/HotkeyRegistration.md | 14 + .../type-aliases/HotkeyRegistrationView.md | 15 + docs/reference/type-aliases/HotkeySequence.md | 23 +- .../HotkeySequenceRecorderCommitKeys.md | 14 - .../type-aliases/MultiHotkeyHandler.md | 4 +- .../type-aliases/RegisterableHotkey.md | 9 +- .../type-aliases/RegisterableHotkeyChord.md | 12 + .../type-aliases/RegisteredAppHotkeys.md | 12 + docs/reference/type-aliases/Target.md | 4 +- .../type-aliases/ValidatableHotkey.md | 12 + .../variables/DEFAULT_SEQUENCE_TIMEOUT.md | 5 +- .../index.html | 7 +- .../package.json | 0 .../src/index.css | 0 .../src/main.ts | 25 +- .../tsconfig.json | 0 .../index.html | 63 +- .../package.json | 2 +- .../src/index.css | 0 .../src/main.ts | 72 +- .../tsconfig.json | 0 .../index.html | 66 +- .../package.json | 2 +- .../src/index.css | 0 .../src/main.ts | 28 +- .../tsconfig.json | 0 examples/alpine/createHotkeys/index.html | 50 +- examples/alpine/createHotkeys/src/main.ts | 10 +- examples/alpine/kitchen-sink/index.html | 8 +- examples/alpine/kitchen-sink/src/main.ts | 82 +- .../angular.json | 6 +- .../package.json | 0 .../src/app/app.component.css | 0 .../src/app/app.component.html | 17 +- .../src/app/app.component.ts | 26 +- .../src/app/app.config.ts | 0 .../src/index.html | 4 +- .../src/main.ts | 0 .../src/styles.css | 0 .../tsconfig.json | 0 .../angular.json | 8 +- .../package.json | 2 +- .../src/app/app.component.css | 0 .../src/app/app.component.html | 69 +- .../src/app/app.component.ts | 80 +- .../src/app/app.config.ts | 0 .../src/app/shortcut-list-item.component.css | 0 .../src/app/shortcut-list-item.component.html | 6 +- .../src/app/shortcut-list-item.component.ts | 7 +- .../src/index.html | 2 +- .../src/main.ts | 0 .../src/styles.css | 0 .../tsconfig.json | 0 .../src/app/app.component.html | 8 +- .../angular.json | 8 +- .../package.json | 2 +- .../src/app/app.component.css | 0 .../src/app/app.component.html | 69 +- .../src/app/app.component.ts | 34 +- .../src/app/app.config.ts | 2 +- .../src/index.html | 4 +- .../src/main.ts | 0 .../src/styles.css | 0 .../tsconfig.json | 0 .../injectHotkeys/src/app/app.component.html | 47 +- .../injectHotkeys/src/app/app.component.ts | 6 +- examples/angular/kitchen-sink/README.md | 15 - .../kitchen-sink/src/app/app.component.ts | 60 +- .../kitchen-sink/src/app/app.config.ts | 2 +- .../app/templates/application.gts | 84 +- .../app/app.css | 0 .../app/app.ts | 0 .../app/config.ts | 0 .../app/router.ts | 0 .../app/templates/application.gts | 32 +- .../babel.config.js | 0 .../index.html | 2 +- .../package.json | 0 .../tsconfig.json | 0 .../vite.config.mjs | 0 .../app/app.css | 0 .../app/app.ts | 0 .../app/config.ts | 0 .../app/router.ts | 0 .../app/templates/application.gts | 131 +- .../babel.config.js | 0 .../index.html | 2 +- .../package.json | 2 +- .../tsconfig.json | 0 .../vite.config.mjs | 0 .../app/templates/application.gts | 4 +- .../app/app.css | 0 .../app/app.ts | 0 .../app/config.ts | 0 .../app/router.ts | 0 .../app/templates/application.gts | 96 +- .../babel.config.js | 0 .../index.html | 2 +- .../package.json | 2 +- .../tsconfig.json | 0 .../vite.config.mjs | 0 .../useHotkeys/app/templates/application.gts | 56 +- .../index.html | 2 +- .../package.json | 2 +- .../src/app.ts | 167 +- .../src/index.css | 0 .../tsconfig.json | 0 .../.gitignore | 0 .../index.html | 2 +- .../package.json | 2 +- .../src/app.ts | 35 +- .../src/index.css | 0 .../tsconfig.json | 0 examples/lit/kitchen-sink/README.md | 15 - examples/lit/kitchen-sink/src/app.ts | 71 +- examples/octane/kitchen-sink/src/main.tsrx | 56 +- .../index.html | 2 +- .../package.json | 0 .../src/index.css | 0 .../src/main.tsrx | 43 +- .../tsconfig.json | 0 .../vite.config.ts | 0 .../index.html | 2 +- .../package.json | 2 +- .../src/index.css | 0 .../src/main.tsrx | 141 +- .../tsconfig.json | 0 .../vite.config.ts | 0 .../index.html | 2 +- .../package.json | 2 +- .../src/index.css | 0 .../src/main.tsrx | 102 +- .../tsconfig.json | 0 .../vite.config.ts | 0 examples/octane/useHotkeys/src/main.tsrx | 53 +- examples/preact/kitchen-sink/README.md | 15 - examples/preact/kitchen-sink/src/index.tsx | 56 +- .../eslint.config.js | 0 .../index.html | 2 +- .../package.json | 0 .../src/index.css | 0 .../src/index.tsx | 44 +- .../tsconfig.json | 0 .../vite.config.ts | 0 .../eslint.config.js | 0 .../index.html | 0 .../package.json | 2 +- .../src/index.css | 0 .../src/index.tsx | 148 +- .../tsconfig.json | 0 .../vite.config.ts | 0 .../preact/useHotkeyRecorder/src/index.tsx | 4 +- .../eslint.config.js | 0 .../index.html | 2 +- .../package.json | 2 +- .../src/index.css | 0 .../src/index.tsx | 101 +- .../tsconfig.json | 0 .../vite.config.ts | 0 examples/preact/useHotkeys/src/index.tsx | 67 +- examples/react/kitchen-sink/README.md | 29 - .../react/kitchen-sink/src/app-hotkeys.ts | 118 + examples/react/kitchen-sink/src/index.tsx | 768 +---- .../react/kitchen-sink/src/routes/editor.tsx | 134 + .../kitchen-sink/src/routes/formatting.tsx | 86 + .../kitchen-sink/src/routes/sequences.tsx | 90 + .../kitchen-sink/src/routes/shortcuts.tsx | 244 ++ .../react/kitchen-sink/src/routes/tickets.tsx | 67 + examples/react/kitchen-sink/src/shared.tsx | 40 + .../eslint.config.js | 0 .../index.html | 2 +- .../package.json | 0 .../src/index.css | 0 .../src/index.tsx | 49 +- .../tsconfig.json | 0 .../vite.config.ts | 0 .../eslint.config.js | 0 .../index.html | 2 +- .../package.json | 2 +- .../src/index.css | 0 .../src/index.tsx | 134 +- .../tsconfig.json | 0 .../vite.config.ts | 0 .../react/useHotkeyRecorder/src/index.tsx | 4 +- .../eslint.config.js | 0 .../index.html | 2 +- .../package.json | 2 +- .../src/index.css | 0 .../src/index.tsx | 112 +- .../tsconfig.json | 0 .../vite.config.ts | 0 examples/react/useHotkeys/src/index.tsx | 51 +- .../index.html | 2 +- .../package.json | 0 .../src/index.css | 0 .../src/index.tsx | 40 +- .../tsconfig.json | 0 .../vite.config.ts | 0 .../index.html | 2 +- .../package.json | 2 +- .../src/index.css | 0 .../src/index.tsx | 150 +- .../tsconfig.json | 0 .../vite.config.ts | 0 .../solid/createHotkeyRecorder/src/index.tsx | 4 +- .../index.html | 2 +- .../package.json | 2 +- .../src/index.css | 0 .../src/index.tsx | 103 +- .../tsconfig.json | 0 .../vite.config.ts | 0 examples/solid/createHotkeys/src/index.tsx | 71 +- examples/solid/kitchen-sink/README.md | 15 - examples/solid/kitchen-sink/src/index.tsx | 62 +- .../.gitignore | 0 .../.npmrc | 0 .../index.html | 2 +- .../package.json | 2 +- .../src/App.svelte | 123 +- .../src/Root.svelte | 0 .../src/ShortcutListItem.svelte | 14 +- .../src/index.css | 0 .../src/main.ts | 0 .../static/robots.txt | 0 .../svelte.config.js | 0 .../tsconfig.json | 0 .../vite.config.ts | 0 .../svelte/create-hotkey-sequence/README.md | 42 - .../create-hotkey-sequence/src/App.svelte | 246 -- .../svelte/create-hotkey-sequences/README.md | 42 - .../svelte/create-hotkey-sequences/index.html | 2 +- .../create-hotkey-sequences/src/App.svelte | 263 +- .../create-hotkey-sequences/src/index.css | 45 +- .../.gitignore | 0 .../.npmrc | 0 .../index.html | 4 +- .../package.json | 2 +- .../create-hotkeys-sequences/src/App.svelte | 374 +++ .../src/Root.svelte | 0 .../src/index.css | 45 +- .../src/main.ts | 0 .../static/robots.txt | 0 .../svelte.config.js | 0 .../tsconfig.json | 0 .../vite.config.ts | 0 examples/svelte/create-hotkeys/src/App.svelte | 51 +- examples/svelte/kitchen-sink/README.md | 15 - examples/svelte/kitchen-sink/src/App.svelte | 6 +- .../svelte/kitchen-sink/src/Recording.svelte | 27 +- .../svelte/kitchen-sink/src/Sequences.svelte | 22 +- examples/vanilla/formatForDisplay/src/main.ts | 4 +- examples/vue/kitchen-sink/README.md | 15 - examples/vue/kitchen-sink/src/App.vue | 9 +- examples/vue/kitchen-sink/src/Recording.vue | 27 +- examples/vue/kitchen-sink/src/Sequences.vue | 24 +- .../eslint.config.js | 0 .../index.html | 2 +- .../package.json | 0 .../src/App.vue | 47 +- .../src/index.css | 0 .../src/index.ts | 0 .../src/vue.d.ts | 0 .../tsconfig.json | 0 .../vite.config.ts | 0 .../eslint.config.js | 0 .../index.html | 2 +- .../package.json | 2 +- .../src/App.vue | 134 +- .../src/ShortcutListItem.vue | 14 +- .../src/index.css | 0 .../src/index.ts | 0 .../src/vue.d.ts | 0 .../tsconfig.json | 0 .../vite.config.ts | 0 examples/vue/useHotkeyRecorder/src/App.vue | 4 +- .../eslint.config.js | 0 .../index.html | 2 +- .../package.json | 2 +- .../src/App.vue | 100 +- .../src/index.css | 0 .../src/index.ts | 0 .../src/vue.d.ts | 0 .../tsconfig.json | 0 .../vite.config.ts | 0 examples/vue/useHotkeys/src/App.vue | 54 +- .../alpine-hotkeys/src/createAppHotkeys.ts | 45 + packages/alpine-hotkeys/src/createHotkey.ts | 12 +- .../alpine-hotkeys/src/createHotkeyHint.ts | 29 +- .../src/createHotkeyRecorder.ts | 25 +- .../src/createHotkeyRegistrations.ts | 24 +- .../src/createHotkeySequence.ts | 22 - .../src/createHotkeySequenceRecorder.ts | 35 - .../src/createHotkeySequences.ts | 22 - packages/alpine-hotkeys/src/createHotkeys.ts | 30 +- .../alpine-hotkeys/src/createHotkeysScope.ts | 37 +- packages/alpine-hotkeys/src/index.ts | 4 - packages/alpine-hotkeys/src/scope.ts | 19 + packages/alpine-hotkeys/src/types.ts | 175 +- .../tests/alpine-hotkeys.test.ts | 45 +- .../alpine-hotkeys/tests/app-hotkeys.test.ts | 102 + .../tests/reactive-options.test.ts | 7 +- .../angular-hotkeys/src/hotkeys-provider.ts | 109 +- packages/angular-hotkeys/src/index.ts | 4 +- .../angular-hotkeys/src/injectAppHotkeys.ts | 102 + packages/angular-hotkeys/src/injectHotkey.ts | 221 +- .../angular-hotkeys/src/injectHotkeyHint.ts | 50 +- .../src/injectHotkeyRecorder.ts | 58 +- .../src/injectHotkeyRegistrations.ts | 61 +- .../src/injectHotkeySequence.ts | 139 - .../src/injectHotkeySequenceRecorder.ts | 102 - .../src/injectHotkeySequences.ts | 175 -- packages/angular-hotkeys/src/injectHotkeys.ts | 211 +- .../angular-hotkeys/tests/appHotkeys.test.ts | 141 + .../tests/reactive-options.test.ts | 8 +- packages/angular-hotkeys/tsconfig.docs.json | 11 +- packages/ember-hotkeys/src/bindings.ts | 83 + .../ember-hotkeys/src/createHotkeysScope.ts | 46 +- packages/ember-hotkeys/src/index.ts | 8 +- packages/ember-hotkeys/src/onHotkey.ts | 39 +- packages/ember-hotkeys/src/onHotkeys.ts | 43 +- packages/ember-hotkeys/src/types.ts | 94 +- packages/ember-hotkeys/src/useAppHotkeys.ts | 63 + packages/ember-hotkeys/src/useHotkey.ts | 45 +- packages/ember-hotkeys/src/useHotkeyHint.ts | 37 +- .../ember-hotkeys/src/useHotkeyRecorder.ts | 46 +- .../src/useHotkeyRegistrations.ts | 14 +- .../ember-hotkeys/src/useHotkeySequence.ts | 43 - .../src/useHotkeySequenceRecorder.ts | 38 - .../ember-hotkeys/src/useHotkeySequences.ts | 45 - packages/ember-hotkeys/src/useHotkeys.ts | 46 +- packages/ember-hotkeys/src/utils.ts | 23 + .../tests/integration/app-hotkeys.test.gts | 101 + .../tests/integration/hotkeys.test.gts | 54 +- .../tests/integration/modifiers.test.gts | 4 +- .../integration/reactive-options.test.gts | 18 +- .../src/HotkeysContextProvider.tsx | 65 +- .../hotkeys-devtools/src/_registrations.ts | 21 +- .../src/components/ActionButtons.tsx | 22 +- .../src/components/DetailsPanel.tsx | 136 +- .../src/components/HotkeyList.tsx | 365 ++- .../hotkeys-devtools/src/components/Shell.tsx | 17 +- .../hotkeys-devtools/src/sequence-progress.ts | 14 +- .../hotkeys-devtools/src/styles/use-styles.ts | 9 + .../tests/registrations.test.tsx | 56 +- packages/hotkeys/src/_match.ts | 4 +- packages/hotkeys/src/_recorder-chord.ts | 4 +- packages/hotkeys/src/adapter.ts | 392 ++- packages/hotkeys/src/app-hotkeys.ts | 576 ++++ packages/hotkeys/src/conflicts.ts | 44 +- packages/hotkeys/src/format.ts | 99 +- packages/hotkeys/src/hint.ts | 7 +- packages/hotkeys/src/hotkey-manager.ts | 719 +++-- packages/hotkeys/src/hotkey-recorder.ts | 418 +-- .../hotkeys/src/hotkey-sequence-recorder.ts | 419 --- packages/hotkeys/src/hotkey.types.ts | 43 +- packages/hotkeys/src/index.ts | 3 +- packages/hotkeys/src/match.ts | 136 +- packages/hotkeys/src/parse.ts | 55 +- packages/hotkeys/src/recorder-options.ts | 10 +- packages/hotkeys/src/sequence-manager.ts | 769 ----- packages/hotkeys/src/validate.ts | 55 +- packages/hotkeys/tests/adapter.test.ts | 22 +- packages/hotkeys/tests/app-hotkeys.test.ts | 241 ++ packages/hotkeys/tests/format.test.ts | 44 +- packages/hotkeys/tests/hotkey-manager.test.ts | 3 + ....test.ts => hotkey-recorder-steps.test.ts} | 56 +- .../hotkeys/tests/hotkey-recorder.test.ts | 4 +- packages/hotkeys/tests/key-coverage.test.ts | 19 +- packages/hotkeys/tests/match.test.ts | 68 +- .../hotkeys/tests/parsed-identity.test.ts | 13 +- packages/hotkeys/tests/physical-key.test.ts | 6 +- packages/hotkeys/tests/public-api.test.ts | 4 +- .../hotkeys/tests/recorder-options.test.ts | 18 +- .../tests/recording-and-display.test.ts | 35 +- .../hotkeys/tests/review-regressions.test.ts | 38 +- .../hotkeys/tests/sequence-manager.test.ts | 137 +- .../tests/sequence-regressions.test.ts | 37 +- .../hotkeys/tests/unified-hotkeys.test.ts | 139 + packages/lit-hotkeys/src/constants.ts | 23 - .../src/controllers/_app-hotkeys.ts | 9 + .../src/controllers/app-hotkeys.ts | 112 + .../src/controllers/hotkey-hint.ts | 46 +- .../src/controllers/hotkey-recorder.ts | 84 +- .../src/controllers/hotkey-registrations.ts | 78 +- .../controllers/hotkey-sequence-recorder.ts | 147 - .../src/controllers/hotkey-sequence.ts | 72 - .../lit-hotkeys/src/controllers/hotkey.ts | 108 +- packages/lit-hotkeys/src/controllers/index.ts | 3 +- .../lit-hotkeys/src/controllers/key-hold.ts | 4 +- .../src/decorators/hotkey-sequence.ts | 68 - packages/lit-hotkeys/src/decorators/hotkey.ts | 43 +- packages/lit-hotkeys/src/decorators/index.ts | 1 - .../lit-hotkeys/tests/app-hotkeys.test.ts | 114 + .../lit-hotkeys/tests/hotkey-sequence.spec.ts | 52 +- .../tests/reactive-options.test.ts | 19 +- packages/lit-hotkeys/tsconfig.docs.json | 11 +- .../octane-hotkeys/src/HotkeysProvider.ts | 95 +- packages/octane-hotkeys/src/index.ts | 8 +- packages/octane-hotkeys/src/internal.ts | 17 +- packages/octane-hotkeys/src/types.ts | 57 +- packages/octane-hotkeys/src/useAppHotkeys.ts | 67 + packages/octane-hotkeys/src/useHotkey.ts | 71 +- packages/octane-hotkeys/src/useHotkeyHint.ts | 36 +- .../octane-hotkeys/src/useHotkeyRecorder.ts | 27 +- .../src/useHotkeyRegistrations.ts | 16 +- .../octane-hotkeys/src/useHotkeySequence.ts | 32 - .../src/useHotkeySequenceRecorder.ts | 45 - .../octane-hotkeys/src/useHotkeySequences.ts | 37 - packages/octane-hotkeys/src/useHotkeys.ts | 56 +- packages/octane-hotkeys/tests/fixture.tsrx | 67 +- .../tests/octane-hotkeys.test.ts | 58 +- .../octane-hotkeys/tests/ssr/hotkeys.test.ts | 3 +- .../preact-hotkeys/src/HotkeysProvider.tsx | 92 +- .../preact-hotkeys/src/_appHotkeysStore.ts | 15 + packages/preact-hotkeys/src/index.ts | 12 +- packages/preact-hotkeys/src/useAppHotkeys.ts | 117 + packages/preact-hotkeys/src/useHotkey.ts | 234 +- packages/preact-hotkeys/src/useHotkeyHint.ts | 33 +- .../preact-hotkeys/src/useHotkeyRecorder.ts | 96 +- .../src/useHotkeyRegistrations.ts | 70 +- .../preact-hotkeys/src/useHotkeySequence.ts | 188 -- .../src/useHotkeySequenceRecorder.ts | 64 - .../preact-hotkeys/src/useHotkeySequences.ts | 192 -- packages/preact-hotkeys/src/useHotkeys.ts | 191 +- packages/preact-hotkeys/src/utils.ts | 14 +- .../preact-hotkeys/tests/appHotkeys.test.tsx | 377 +++ .../tests/useHotkeyRegistrations.test.tsx | 20 +- ...test.tsx => useHotkeys-sequences.test.tsx} | 66 +- packages/preact-hotkeys/tsconfig.docs.json | 11 +- .../react-hotkeys/src/HotkeysProvider.tsx | 98 +- .../react-hotkeys/src/_appHotkeysStore.ts | 15 + packages/react-hotkeys/src/index.ts | 10 +- packages/react-hotkeys/src/useAppHotkeys.ts | 117 + packages/react-hotkeys/src/useHotkey.ts | 231 +- packages/react-hotkeys/src/useHotkeyHint.ts | 33 +- .../react-hotkeys/src/useHotkeyRecorder.ts | 96 +- .../src/useHotkeyRegistrations.ts | 56 +- .../react-hotkeys/src/useHotkeySequence.ts | 187 -- .../src/useHotkeySequenceRecorder.ts | 70 - .../react-hotkeys/src/useHotkeySequences.ts | 192 -- packages/react-hotkeys/src/useHotkeys.ts | 191 +- packages/react-hotkeys/src/utils.ts | 16 +- .../react-hotkeys/tests/appHotkeys.test.tsx | 375 +++ .../tests/useHotkeyHint.test.tsx | 12 +- .../tests/useHotkeyRegistrations.test.tsx | 178 +- ...test.tsx => useHotkeys-sequences.test.tsx} | 91 +- packages/react-hotkeys/tsconfig.docs.json | 11 +- .../solid-hotkeys/src/HotkeysProvider.tsx | 84 +- packages/solid-hotkeys/src/appHotkeys.ts | 100 + packages/solid-hotkeys/src/createHotkey.ts | 227 +- .../solid-hotkeys/src/createHotkeyHint.ts | 49 +- .../solid-hotkeys/src/createHotkeyRecorder.ts | 109 +- .../src/createHotkeyRegistrations.ts | 52 +- .../solid-hotkeys/src/createHotkeySequence.ts | 154 - .../src/createHotkeySequenceRecorder.ts | 65 - .../src/createHotkeySequences.ts | 180 -- packages/solid-hotkeys/src/createHotkeys.ts | 210 +- packages/solid-hotkeys/src/defaultOptions.ts | 17 + packages/solid-hotkeys/src/index.ts | 10 +- .../solid-hotkeys/tests/appHotkeys.test.tsx | 152 + ...t.tsx => createHotkeys-sequences.test.tsx} | 76 +- .../tests/reactive-options.test.tsx | 23 +- packages/solid-hotkeys/tsconfig.docs.json | 11 +- packages/svelte-hotkeys/src/HotkeysCtx.ts | 89 +- .../svelte-hotkeys/src/HotkeysProvider.svelte | 23 +- .../svelte-hotkeys/src/createHotkey.svelte.ts | 220 +- .../src/createHotkeyRecorder.svelte.ts | 128 +- .../src/createHotkeySequence.svelte.ts | 207 -- .../createHotkeySequenceRecorder.svelte.ts | 97 - .../src/createHotkeySequences.svelte.ts | 273 -- .../src/createHotkeys.svelte.ts | 341 +-- .../src/getAppHotkeys.svelte.ts | 113 + .../src/getHotkeyHint.svelte.ts | 58 +- .../src/getHotkeyRegistrations.svelte.ts | 47 +- packages/svelte-hotkeys/src/index.ts | 10 +- .../svelte-hotkeys/src/internal.svelte.ts | 13 + .../tests/AppHotkeysChild.svelte | 49 + .../tests/AppHotkeysHarness.svelte | 27 + .../tests/ReactiveRecorders.svelte | 13 +- .../tests/RecorderHarness.svelte | 9 +- .../svelte-hotkeys/tests/appHotkeys.test.ts | 89 + .../tests/reactive-options.test.ts | 3 +- .../svelte-hotkeys/tests/recorders.test.ts | 5 +- packages/svelte-hotkeys/tsconfig.docs.json | 11 +- packages/vue-hotkeys/src/HotkeysProvider.tsx | 34 +- .../vue-hotkeys/src/HotkeysProviderContext.ts | 91 +- packages/vue-hotkeys/src/defaultOptions.ts | 18 + packages/vue-hotkeys/src/index.ts | 4 +- packages/vue-hotkeys/src/useAppHotkeys.ts | 99 + packages/vue-hotkeys/src/useHotkey.ts | 268 +- packages/vue-hotkeys/src/useHotkeyHint.ts | 39 +- packages/vue-hotkeys/src/useHotkeyRecorder.ts | 131 +- .../vue-hotkeys/src/useHotkeyRegistrations.ts | 58 +- packages/vue-hotkeys/src/useHotkeySequence.ts | 187 -- .../src/useHotkeySequenceRecorder.ts | 75 - .../vue-hotkeys/src/useHotkeySequences.ts | 221 -- packages/vue-hotkeys/src/useHotkeys.ts | 280 +- packages/vue-hotkeys/tests/appHotkeys.test.ts | 197 ++ .../tests/reactive-options.test.ts | 25 +- .../vue-hotkeys/tests/vue-hotkeys.test.ts | 9 +- pnpm-lock.yaml | 804 +++++- 1002 files changed, 29658 insertions(+), 28840 deletions(-) create mode 100644 .changeset/app-hotkeys.md create mode 100644 docs/framework/alpine/guides/app-hotkeys.md delete mode 100644 docs/framework/alpine/guides/sequence-recording.md create mode 100644 docs/framework/alpine/reference/interfaces/AlpineCreateHotkey.md create mode 100644 docs/framework/alpine/reference/interfaces/AlpineCreateHotkeyRecorder.md create mode 100644 docs/framework/alpine/reference/interfaces/AlpineCustomHotkeys.md delete mode 100644 docs/framework/alpine/reference/interfaces/AlpineHotkeySequenceRecorder.md create mode 100644 docs/framework/alpine/reference/type-aliases/AlpineAppHotkeyOptions.md create mode 100644 docs/framework/alpine/reference/type-aliases/AlpineHotkeyCommonOptions.md create mode 100644 docs/framework/alpine/reference/type-aliases/AlpineHotkeyDefinition.md create mode 100644 docs/framework/alpine/reference/type-aliases/AlpineHotkeyOptionsFor.md create mode 100644 docs/framework/alpine/reference/type-aliases/AlpineHotkeyTarget.md create mode 100644 docs/framework/angular/guides/app-hotkeys.md delete mode 100644 docs/framework/angular/guides/sequence-recording.md create mode 100644 docs/framework/angular/reference/functions/injectAppHotkeyList.md create mode 100644 docs/framework/angular/reference/functions/injectAppHotkeys.md create mode 100644 docs/framework/angular/reference/functions/injectCustomHotkeys.md delete mode 100644 docs/framework/angular/reference/functions/injectHotkeySequence.md delete mode 100644 docs/framework/angular/reference/functions/injectHotkeySequenceRecorder.md delete mode 100644 docs/framework/angular/reference/functions/injectHotkeySequences.md delete mode 100644 docs/framework/angular/reference/interfaces/AngularHotkeySequenceRecorder.md delete mode 100644 docs/framework/angular/reference/interfaces/HotkeyRegistrationsResult.md create mode 100644 docs/framework/angular/reference/interfaces/InjectCustomHotkeysResult.md delete mode 100644 docs/framework/angular/reference/interfaces/InjectHotkeyDefinition.md delete mode 100644 docs/framework/angular/reference/interfaces/InjectHotkeyOptions.md delete mode 100644 docs/framework/angular/reference/interfaces/InjectHotkeySequenceDefinition.md delete mode 100644 docs/framework/angular/reference/interfaces/InjectHotkeySequenceOptions.md create mode 100644 docs/framework/angular/reference/interfaces/ProvideHotkeysOptions.md create mode 100644 docs/framework/angular/reference/type-aliases/InjectAppHotkeyOptions.md create mode 100644 docs/framework/angular/reference/type-aliases/InjectHotkeyChordOptions.md create mode 100644 docs/framework/angular/reference/type-aliases/InjectHotkeyCommonOptions.md create mode 100644 docs/framework/angular/reference/type-aliases/InjectHotkeyDefinition.md create mode 100644 docs/framework/angular/reference/type-aliases/InjectHotkeyOptions.md create mode 100644 docs/framework/angular/reference/type-aliases/InjectHotkeyOptionsFor.md create mode 100644 docs/framework/angular/reference/type-aliases/InjectHotkeySequenceOptions.md create mode 100644 docs/framework/angular/reference/type-aliases/InjectHotkeyTarget.md create mode 100644 docs/framework/ember/guides/app-hotkeys.md delete mode 100644 docs/framework/ember/guides/sequence-recording.md delete mode 100644 docs/framework/ember/reference/classes/useHotkeySequence.md delete mode 100644 docs/framework/ember/reference/classes/useHotkeySequences.md create mode 100644 docs/framework/ember/reference/functions/useAppHotkeyList.md create mode 100644 docs/framework/ember/reference/functions/useAppHotkeys.md create mode 100644 docs/framework/ember/reference/functions/useCustomHotkeys.md delete mode 100644 docs/framework/ember/reference/functions/useHotkeySequenceRecorder.md create mode 100644 docs/framework/ember/reference/interfaces/EmberCustomHotkeys.md delete mode 100644 docs/framework/ember/reference/interfaces/EmberHotkeySequenceRecorder.md create mode 100644 docs/framework/ember/reference/type-aliases/EmberAppHotkeyList.md create mode 100644 docs/framework/ember/reference/type-aliases/EmberHotkeyCommonOptions.md create mode 100644 docs/framework/ember/reference/type-aliases/EmberHotkeyDefinition.md create mode 100644 docs/framework/ember/reference/type-aliases/EmberHotkeyTarget.md create mode 100644 docs/framework/lit/guides/app-hotkeys.md delete mode 100644 docs/framework/lit/guides/sequence-recording.md create mode 100644 docs/framework/lit/reference/classes/AppHotkeysController.md delete mode 100644 docs/framework/lit/reference/classes/HotkeySequenceController.md delete mode 100644 docs/framework/lit/reference/classes/HotkeySequenceRecorderController.md delete mode 100644 docs/framework/lit/reference/functions/hotkeySequence.md create mode 100644 docs/framework/lit/reference/type-aliases/AppHotkeyControllerOptions.md create mode 100644 docs/framework/lit/reference/type-aliases/HotkeyControllerChordOptions.md create mode 100644 docs/framework/lit/reference/type-aliases/HotkeyControllerCommonOptions.md create mode 100644 docs/framework/lit/reference/type-aliases/HotkeyControllerOptions.md create mode 100644 docs/framework/lit/reference/type-aliases/HotkeyControllerOptionsFor.md create mode 100644 docs/framework/lit/reference/type-aliases/HotkeyControllerSequenceOptions.md create mode 100644 docs/framework/lit/reference/type-aliases/HotkeyControllerTarget.md create mode 100644 docs/framework/octane/guides/app-hotkeys.md delete mode 100644 docs/framework/octane/guides/sequence-recording.md create mode 100644 docs/framework/octane/reference/functions/useAppHotkeyList.md create mode 100644 docs/framework/octane/reference/functions/useAppHotkeys.md create mode 100644 docs/framework/octane/reference/functions/useCustomHotkeys.md delete mode 100644 docs/framework/octane/reference/functions/useHotkeySequence.md delete mode 100644 docs/framework/octane/reference/functions/useHotkeySequenceRecorder.md delete mode 100644 docs/framework/octane/reference/functions/useHotkeySequences.md delete mode 100644 docs/framework/octane/reference/interfaces/OctaneHotkeySequenceRecorder.md create mode 100644 docs/framework/octane/reference/interfaces/UseCustomHotkeysResult.md create mode 100644 docs/framework/octane/reference/type-aliases/UseAppHotkeyOptions.md create mode 100644 docs/framework/octane/reference/type-aliases/UseHotkeyChordOptions.md create mode 100644 docs/framework/octane/reference/type-aliases/UseHotkeyCommonOptions.md create mode 100644 docs/framework/octane/reference/type-aliases/UseHotkeyDefinition.md create mode 100644 docs/framework/octane/reference/type-aliases/UseHotkeyOptions.md create mode 100644 docs/framework/octane/reference/type-aliases/UseHotkeyOptionsFor.md create mode 100644 docs/framework/octane/reference/type-aliases/UseHotkeySequenceOptions.md create mode 100644 docs/framework/octane/reference/type-aliases/UseHotkeyTarget.md create mode 100644 docs/framework/preact/guides/app-hotkeys.md delete mode 100644 docs/framework/preact/guides/sequence-recording.md create mode 100644 docs/framework/preact/quick-start.md create mode 100644 docs/framework/preact/reference/functions/useAppHotkeyList.md create mode 100644 docs/framework/preact/reference/functions/useAppHotkeys.md create mode 100644 docs/framework/preact/reference/functions/useCustomHotkeys.md delete mode 100644 docs/framework/preact/reference/functions/useHotkeySequence.md delete mode 100644 docs/framework/preact/reference/functions/useHotkeySequenceRecorder.md delete mode 100644 docs/framework/preact/reference/functions/useHotkeySequences.md delete mode 100644 docs/framework/preact/reference/interfaces/HotkeyRegistrationsResult.md delete mode 100644 docs/framework/preact/reference/interfaces/PreactHotkeySequenceRecorder.md create mode 100644 docs/framework/preact/reference/interfaces/UseCustomHotkeysResult.md delete mode 100644 docs/framework/preact/reference/interfaces/UseHotkeyDefinition.md delete mode 100644 docs/framework/preact/reference/interfaces/UseHotkeyOptions.md delete mode 100644 docs/framework/preact/reference/interfaces/UseHotkeySequenceDefinition.md delete mode 100644 docs/framework/preact/reference/interfaces/UseHotkeySequenceOptions.md create mode 100644 docs/framework/preact/reference/type-aliases/UseAppHotkeyOptions.md create mode 100644 docs/framework/preact/reference/type-aliases/UseHotkeyChordOptions.md create mode 100644 docs/framework/preact/reference/type-aliases/UseHotkeyCommonOptions.md create mode 100644 docs/framework/preact/reference/type-aliases/UseHotkeyDefinition.md create mode 100644 docs/framework/preact/reference/type-aliases/UseHotkeyOptions.md create mode 100644 docs/framework/preact/reference/type-aliases/UseHotkeyOptionsFor.md create mode 100644 docs/framework/preact/reference/type-aliases/UseHotkeySequenceOptions.md create mode 100644 docs/framework/preact/reference/type-aliases/UseHotkeyTarget.md create mode 100644 docs/framework/react/guides/app-hotkeys.md delete mode 100644 docs/framework/react/guides/sequence-recording.md create mode 100644 docs/framework/react/reference/functions/useAppHotkeyList.md create mode 100644 docs/framework/react/reference/functions/useAppHotkeys.md create mode 100644 docs/framework/react/reference/functions/useCustomHotkeys.md delete mode 100644 docs/framework/react/reference/functions/useHotkeySequence.md delete mode 100644 docs/framework/react/reference/functions/useHotkeySequenceRecorder.md delete mode 100644 docs/framework/react/reference/functions/useHotkeySequences.md delete mode 100644 docs/framework/react/reference/interfaces/HotkeyRegistrationsResult.md delete mode 100644 docs/framework/react/reference/interfaces/ReactHotkeySequenceRecorder.md create mode 100644 docs/framework/react/reference/interfaces/UseCustomHotkeysResult.md delete mode 100644 docs/framework/react/reference/interfaces/UseHotkeyDefinition.md delete mode 100644 docs/framework/react/reference/interfaces/UseHotkeyOptions.md delete mode 100644 docs/framework/react/reference/interfaces/UseHotkeySequenceDefinition.md delete mode 100644 docs/framework/react/reference/interfaces/UseHotkeySequenceOptions.md create mode 100644 docs/framework/react/reference/type-aliases/UseAppHotkeyOptions.md create mode 100644 docs/framework/react/reference/type-aliases/UseHotkeyChordOptions.md create mode 100644 docs/framework/react/reference/type-aliases/UseHotkeyCommonOptions.md create mode 100644 docs/framework/react/reference/type-aliases/UseHotkeyDefinition.md create mode 100644 docs/framework/react/reference/type-aliases/UseHotkeyOptions.md create mode 100644 docs/framework/react/reference/type-aliases/UseHotkeyOptionsFor.md create mode 100644 docs/framework/react/reference/type-aliases/UseHotkeySequenceOptions.md create mode 100644 docs/framework/react/reference/type-aliases/UseHotkeyTarget.md create mode 100644 docs/framework/solid/guides/app-hotkeys.md delete mode 100644 docs/framework/solid/guides/sequence-recording.md create mode 100644 docs/framework/solid/quick-start.md create mode 100644 docs/framework/solid/reference/functions/createAppHotkeyList.md delete mode 100644 docs/framework/solid/reference/functions/createHotkeySequence.md delete mode 100644 docs/framework/solid/reference/functions/createHotkeySequenceRecorder.md delete mode 100644 docs/framework/solid/reference/functions/createHotkeySequences.md create mode 100644 docs/framework/solid/reference/functions/useAppHotkeys.md create mode 100644 docs/framework/solid/reference/functions/useCustomHotkeys.md delete mode 100644 docs/framework/solid/reference/interfaces/CreateHotkeyDefinition.md delete mode 100644 docs/framework/solid/reference/interfaces/CreateHotkeyOptions.md delete mode 100644 docs/framework/solid/reference/interfaces/CreateHotkeySequenceDefinition.md delete mode 100644 docs/framework/solid/reference/interfaces/CreateHotkeySequenceOptions.md delete mode 100644 docs/framework/solid/reference/interfaces/HotkeyRegistrationsResult.md delete mode 100644 docs/framework/solid/reference/interfaces/SolidHotkeySequenceRecorder.md create mode 100644 docs/framework/solid/reference/interfaces/UseCustomHotkeysResult.md create mode 100644 docs/framework/solid/reference/type-aliases/CreateAppHotkeyOptions.md create mode 100644 docs/framework/solid/reference/type-aliases/CreateHotkeyChordOptions.md create mode 100644 docs/framework/solid/reference/type-aliases/CreateHotkeyCommonOptions.md create mode 100644 docs/framework/solid/reference/type-aliases/CreateHotkeyDefinition.md create mode 100644 docs/framework/solid/reference/type-aliases/CreateHotkeyOptions.md create mode 100644 docs/framework/solid/reference/type-aliases/CreateHotkeyOptionsFor.md create mode 100644 docs/framework/solid/reference/type-aliases/CreateHotkeySequenceOptions.md create mode 100644 docs/framework/solid/reference/type-aliases/CreateHotkeyTarget.md create mode 100644 docs/framework/svelte/guides/app-hotkeys.md delete mode 100644 docs/framework/svelte/guides/sequence-recording.md delete mode 100644 docs/framework/svelte/reference/functions/createHotkeySequence.md delete mode 100644 docs/framework/svelte/reference/functions/createHotkeySequenceAttachment.md delete mode 100644 docs/framework/svelte/reference/functions/createHotkeySequenceRecorder.md delete mode 100644 docs/framework/svelte/reference/functions/createHotkeySequences.md delete mode 100644 docs/framework/svelte/reference/functions/createHotkeySequencesAttachment.md create mode 100644 docs/framework/svelte/reference/functions/getAppHotkeyList.md create mode 100644 docs/framework/svelte/reference/functions/getAppHotkeys.md create mode 100644 docs/framework/svelte/reference/functions/getCustomHotkeys.md delete mode 100644 docs/framework/svelte/reference/interfaces/CreateHotkeyDefinition.md delete mode 100644 docs/framework/svelte/reference/interfaces/CreateHotkeyOptions.md delete mode 100644 docs/framework/svelte/reference/interfaces/CreateHotkeySequenceDefinition.md delete mode 100644 docs/framework/svelte/reference/interfaces/CreateHotkeySequenceOptions.md create mode 100644 docs/framework/svelte/reference/interfaces/HotkeysContextOptions.md create mode 100644 docs/framework/svelte/reference/interfaces/SvelteAppHotkeyList.md create mode 100644 docs/framework/svelte/reference/interfaces/SvelteAppHotkeys.md create mode 100644 docs/framework/svelte/reference/interfaces/SvelteCustomHotkeys.md delete mode 100644 docs/framework/svelte/reference/interfaces/SvelteHotkeySequenceRecorder.md create mode 100644 docs/framework/svelte/reference/type-aliases/CreateAppHotkeyOptions.md create mode 100644 docs/framework/svelte/reference/type-aliases/CreateHotkeyChordOptions.md create mode 100644 docs/framework/svelte/reference/type-aliases/CreateHotkeyCommonOptions.md create mode 100644 docs/framework/svelte/reference/type-aliases/CreateHotkeyDefinition.md create mode 100644 docs/framework/svelte/reference/type-aliases/CreateHotkeyOptions.md create mode 100644 docs/framework/svelte/reference/type-aliases/CreateHotkeyOptionsFor.md create mode 100644 docs/framework/svelte/reference/type-aliases/CreateHotkeySequenceOptions.md create mode 100644 docs/framework/svelte/reference/type-aliases/CreateHotkeyTarget.md create mode 100644 docs/framework/vue/guides/app-hotkeys.md delete mode 100644 docs/framework/vue/guides/sequence-recording.md create mode 100644 docs/framework/vue/reference/functions/useAppHotkeyList.md create mode 100644 docs/framework/vue/reference/functions/useAppHotkeys.md create mode 100644 docs/framework/vue/reference/functions/useCustomHotkeys.md delete mode 100644 docs/framework/vue/reference/functions/useHotkeySequence.md delete mode 100644 docs/framework/vue/reference/functions/useHotkeySequenceRecorder.md delete mode 100644 docs/framework/vue/reference/functions/useHotkeySequences.md delete mode 100644 docs/framework/vue/reference/interfaces/HotkeyRegistrationsResult.md create mode 100644 docs/framework/vue/reference/interfaces/ProvideHotkeysContextOptions.md create mode 100644 docs/framework/vue/reference/interfaces/UseCustomHotkeysResult.md delete mode 100644 docs/framework/vue/reference/interfaces/UseHotkeyDefinition.md delete mode 100644 docs/framework/vue/reference/interfaces/UseHotkeyOptions.md delete mode 100644 docs/framework/vue/reference/interfaces/UseHotkeySequenceDefinition.md delete mode 100644 docs/framework/vue/reference/interfaces/UseHotkeySequenceOptions.md delete mode 100644 docs/framework/vue/reference/interfaces/VueHotkeySequenceRecorder.md create mode 100644 docs/framework/vue/reference/type-aliases/UseAppHotkeyOptions.md create mode 100644 docs/framework/vue/reference/type-aliases/UseHotkeyChordOptions.md create mode 100644 docs/framework/vue/reference/type-aliases/UseHotkeyCommonOptions.md create mode 100644 docs/framework/vue/reference/type-aliases/UseHotkeyDefinition.md create mode 100644 docs/framework/vue/reference/type-aliases/UseHotkeyOptions.md create mode 100644 docs/framework/vue/reference/type-aliases/UseHotkeyOptionsFor.md create mode 100644 docs/framework/vue/reference/type-aliases/UseHotkeySequenceOptions.md create mode 100644 docs/framework/vue/reference/type-aliases/UseHotkeyTarget.md delete mode 100644 docs/reference/adapter/functions/createHotkeySequenceBindings.md create mode 100644 docs/reference/adapter/functions/prepareHotkeyDefinitions.md create mode 100644 docs/reference/adapter/functions/subscribePublishedAppHotkeys.md delete mode 100644 docs/reference/adapter/interfaces/HotkeyDefinition.md delete mode 100644 docs/reference/adapter/interfaces/HotkeySequenceDefinition.md create mode 100644 docs/reference/adapter/interfaces/PrepareHotkeyDefinitionsOptions.md create mode 100644 docs/reference/adapter/type-aliases/AdapterAppHotkeyOptions.md create mode 100644 docs/reference/adapter/type-aliases/AdapterChordOptions.md create mode 100644 docs/reference/adapter/type-aliases/AdapterCommonOptions.md create mode 100644 docs/reference/adapter/type-aliases/AdapterHotkeyDefinition.md create mode 100644 docs/reference/adapter/type-aliases/AdapterHotkeyOptions.md create mode 100644 docs/reference/adapter/type-aliases/AdapterOptionsFor.md create mode 100644 docs/reference/adapter/type-aliases/AdapterSequenceOptions.md create mode 100644 docs/reference/adapter/type-aliases/HotkeyBindingsCommonOptions.md create mode 100644 docs/reference/adapter/type-aliases/HotkeyDefinition.md create mode 100644 docs/reference/adapter/type-aliases/WithTarget.md create mode 100644 docs/reference/classes/AppHotkeysStore.md delete mode 100644 docs/reference/classes/HotkeySequenceRecorder.md delete mode 100644 docs/reference/classes/SequenceManager.md create mode 100644 docs/reference/functions/defineAppHotkeys.md delete mode 100644 docs/reference/functions/formatHotkeySequence.md delete mode 100644 docs/reference/functions/formatWithLabels.md create mode 100644 docs/reference/functions/getAppHotkeyRegistrationOptions.md delete mode 100644 docs/reference/functions/getSequenceManager.md create mode 100644 docs/reference/functions/isAppHotkey.md create mode 100644 docs/reference/functions/isCustomHotkey.md create mode 100644 docs/reference/functions/isHotkeySequence.md create mode 100644 docs/reference/functions/provideAppHotkeys.md create mode 100644 docs/reference/functions/publishAppHotkeysStore.md create mode 100644 docs/reference/functions/resolveAppHotkey.md create mode 100644 docs/reference/functions/simplifyHotkey.md create mode 100644 docs/reference/interfaces/AppHotkey.md create mode 100644 docs/reference/interfaces/AppHotkeyInput.md create mode 100644 docs/reference/interfaces/AppHotkeyListItem.md create mode 100644 docs/reference/interfaces/AppHotkeysState.md create mode 100644 docs/reference/interfaces/AppHotkeysStoreOptions.md create mode 100644 docs/reference/interfaces/HotkeyChordOptions.md rename docs/reference/interfaces/{HotkeyRecorderOptions.md => HotkeyChordRecorderOptions.md} (54%) create mode 100644 docs/reference/interfaces/HotkeyChordRegistration.md create mode 100644 docs/reference/interfaces/HotkeyChordRegistrationView.md create mode 100644 docs/reference/interfaces/HotkeyConflict.md delete mode 100644 docs/reference/interfaces/HotkeyRegistration.md delete mode 100644 docs/reference/interfaces/HotkeyRegistrationView.md rename docs/reference/interfaces/{SequenceOptions.md => HotkeySequenceOptions.md} (61%) delete mode 100644 docs/reference/interfaces/HotkeySequenceRecorderState.md delete mode 100644 docs/reference/interfaces/HotkeySequenceRecorderValidationContext.md create mode 100644 docs/reference/interfaces/HotkeySequenceRegistration.md create mode 100644 docs/reference/interfaces/HotkeySequenceRegistrationView.md create mode 100644 docs/reference/interfaces/Register.md delete mode 100644 docs/reference/interfaces/SequenceRegistration.md delete mode 100644 docs/reference/interfaces/SequenceRegistrationHandle.md delete mode 100644 docs/reference/interfaces/SequenceRegistrationView.md create mode 100644 docs/reference/type-aliases/AppHotkeyChordOptions.md create mode 100644 docs/reference/type-aliases/AppHotkeyDefaultKind.md create mode 100644 docs/reference/type-aliases/AppHotkeyId.md create mode 100644 docs/reference/type-aliases/AppHotkeyOptions.md create mode 100644 docs/reference/type-aliases/AppHotkeySequenceOptions.md create mode 100644 docs/reference/type-aliases/AppHotkeyStatus.md create mode 100644 docs/reference/type-aliases/AppHotkeys.md create mode 100644 docs/reference/type-aliases/CustomHotkeys.md create mode 100644 docs/reference/type-aliases/DefinedAppHotkeys.md create mode 100644 docs/reference/type-aliases/DisplayHotkeyChord.md create mode 100644 docs/reference/type-aliases/HotkeyChord.md delete mode 100644 docs/reference/type-aliases/HotkeyConflict.md create mode 100644 docs/reference/type-aliases/HotkeyOptionsFor.md create mode 100644 docs/reference/type-aliases/HotkeyRecorderCommitKeys.md create mode 100644 docs/reference/type-aliases/HotkeyRecorderOptions.md create mode 100644 docs/reference/type-aliases/HotkeyRegistration.md create mode 100644 docs/reference/type-aliases/HotkeyRegistrationView.md delete mode 100644 docs/reference/type-aliases/HotkeySequenceRecorderCommitKeys.md create mode 100644 docs/reference/type-aliases/RegisterableHotkeyChord.md create mode 100644 docs/reference/type-aliases/RegisteredAppHotkeys.md create mode 100644 docs/reference/type-aliases/ValidatableHotkey.md rename examples/alpine/{createHotkeySequence => createHotkey-sequences}/index.html (96%) rename examples/alpine/{createHotkeySequences => createHotkey-sequences}/package.json (100%) rename examples/alpine/{createHotkeySequence => createHotkey-sequences}/src/index.css (100%) rename examples/alpine/{createHotkeySequence => createHotkey-sequences}/src/main.ts (60%) rename examples/alpine/{createHotkeySequence => createHotkey-sequences}/tsconfig.json (100%) rename examples/alpine/{createHotkeySequenceRecorder => createHotkeyRecorder-sequences}/index.html (82%) rename examples/alpine/{createHotkeySequenceRecorder => createHotkeyRecorder-sequences}/package.json (85%) rename examples/alpine/{createHotkeySequenceRecorder => createHotkeyRecorder-sequences}/src/index.css (100%) rename examples/alpine/{createHotkeySequenceRecorder => createHotkeyRecorder-sequences}/src/main.ts (78%) rename examples/alpine/{createHotkeySequenceRecorder => createHotkeyRecorder-sequences}/tsconfig.json (100%) rename examples/alpine/{createHotkeySequences => createHotkeys-sequences}/index.html (78%) rename examples/alpine/{createHotkeySequence => createHotkeys-sequences}/package.json (86%) rename examples/alpine/{createHotkeySequences => createHotkeys-sequences}/src/index.css (100%) rename examples/alpine/{createHotkeySequences => createHotkeys-sequences}/src/main.ts (81%) rename examples/alpine/{createHotkeySequences => createHotkeys-sequences}/tsconfig.json (100%) rename examples/angular/{injectHotkeySequences => injectHotkey-sequences}/angular.json (90%) rename examples/angular/{injectHotkeySequences => injectHotkey-sequences}/package.json (100%) rename examples/angular/{injectHotkeySequence => injectHotkey-sequences}/src/app/app.component.css (100%) rename examples/angular/{injectHotkeySequence => injectHotkey-sequences}/src/app/app.component.html (90%) rename examples/angular/{injectHotkeySequence => injectHotkey-sequences}/src/app/app.component.ts (64%) rename examples/angular/{injectHotkeySequence => injectHotkey-sequences}/src/app/app.config.ts (100%) rename examples/angular/{injectHotkeySequences => injectHotkey-sequences}/src/index.html (74%) rename examples/angular/{injectHotkeySequence => injectHotkey-sequences}/src/main.ts (100%) rename examples/angular/{injectHotkeySequence => injectHotkey-sequences}/src/styles.css (100%) rename examples/angular/{injectHotkeySequence => injectHotkey-sequences}/tsconfig.json (100%) rename examples/angular/{injectHotkeySequenceRecorder => injectHotkeyRecorder-sequences}/angular.json (86%) rename examples/angular/{injectHotkeySequenceRecorder => injectHotkeyRecorder-sequences}/package.json (96%) rename examples/angular/{injectHotkeySequenceRecorder => injectHotkeyRecorder-sequences}/src/app/app.component.css (100%) rename examples/angular/{injectHotkeySequenceRecorder => injectHotkeyRecorder-sequences}/src/app/app.component.html (75%) rename examples/angular/{injectHotkeySequenceRecorder => injectHotkeyRecorder-sequences}/src/app/app.component.ts (76%) rename examples/angular/{injectHotkeySequenceRecorder => injectHotkeyRecorder-sequences}/src/app/app.config.ts (100%) rename examples/angular/{injectHotkeySequenceRecorder => injectHotkeyRecorder-sequences}/src/app/shortcut-list-item.component.css (100%) rename examples/angular/{injectHotkeySequenceRecorder => injectHotkeyRecorder-sequences}/src/app/shortcut-list-item.component.html (88%) rename examples/angular/{injectHotkeySequenceRecorder => injectHotkeyRecorder-sequences}/src/app/shortcut-list-item.component.ts (81%) rename examples/angular/{injectHotkeySequenceRecorder => injectHotkeyRecorder-sequences}/src/index.html (78%) rename examples/angular/{injectHotkeySequenceRecorder => injectHotkeyRecorder-sequences}/src/main.ts (100%) rename examples/angular/{injectHotkeySequenceRecorder => injectHotkeyRecorder-sequences}/src/styles.css (100%) rename examples/angular/{injectHotkeySequenceRecorder => injectHotkeyRecorder-sequences}/tsconfig.json (100%) rename examples/angular/{injectHotkeySequence => injectHotkeys-sequences}/angular.json (87%) rename examples/angular/{injectHotkeySequence => injectHotkeys-sequences}/package.json (96%) rename examples/angular/{injectHotkeySequences => injectHotkeys-sequences}/src/app/app.component.css (100%) rename examples/angular/{injectHotkeySequences => injectHotkeys-sequences}/src/app/app.component.html (77%) rename examples/angular/{injectHotkeySequences => injectHotkeys-sequences}/src/app/app.component.ts (83%) rename examples/angular/{injectHotkeySequences => injectHotkeys-sequences}/src/app/app.config.ts (96%) rename examples/angular/{injectHotkeySequence => injectHotkeys-sequences}/src/index.html (74%) rename examples/angular/{injectHotkeySequences => injectHotkeys-sequences}/src/main.ts (100%) rename examples/angular/{injectHotkeySequences => injectHotkeys-sequences}/src/styles.css (100%) rename examples/angular/{injectHotkeySequences => injectHotkeys-sequences}/tsconfig.json (100%) delete mode 100644 examples/angular/kitchen-sink/README.md rename examples/ember/{useHotkeySequence => useHotkey-sequences}/app/app.css (100%) rename examples/ember/{useHotkeySequence => useHotkey-sequences}/app/app.ts (100%) rename examples/ember/{useHotkeySequence => useHotkey-sequences}/app/config.ts (100%) rename examples/ember/{useHotkeySequence => useHotkey-sequences}/app/router.ts (100%) rename examples/ember/{useHotkeySequence => useHotkey-sequences}/app/templates/application.gts (88%) rename examples/ember/{useHotkeySequence => useHotkey-sequences}/babel.config.js (100%) rename examples/ember/{useHotkeySequence => useHotkey-sequences}/index.html (85%) rename examples/ember/{useHotkeySequences => useHotkey-sequences}/package.json (100%) rename examples/ember/{useHotkeySequence => useHotkey-sequences}/tsconfig.json (100%) rename examples/ember/{useHotkeySequence => useHotkey-sequences}/vite.config.mjs (100%) rename examples/ember/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/app/app.css (100%) rename examples/ember/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/app/app.ts (100%) rename examples/ember/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/app/config.ts (100%) rename examples/ember/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/app/router.ts (100%) rename examples/ember/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/app/templates/application.gts (78%) rename examples/ember/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/babel.config.js (100%) rename examples/ember/{useHotkeySequences => useHotkeyRecorder-sequences}/index.html (85%) rename examples/ember/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/package.json (95%) rename examples/ember/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/tsconfig.json (100%) rename examples/ember/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/vite.config.mjs (100%) rename examples/ember/{useHotkeySequences => useHotkeys-sequences}/app/app.css (100%) rename examples/ember/{useHotkeySequences => useHotkeys-sequences}/app/app.ts (100%) rename examples/ember/{useHotkeySequences => useHotkeys-sequences}/app/config.ts (100%) rename examples/ember/{useHotkeySequences => useHotkeys-sequences}/app/router.ts (100%) rename examples/ember/{useHotkeySequences => useHotkeys-sequences}/app/templates/application.gts (81%) rename examples/ember/{useHotkeySequences => useHotkeys-sequences}/babel.config.js (100%) rename examples/ember/{useHotkeySequenceRecorder => useHotkeys-sequences}/index.html (86%) rename examples/ember/{useHotkeySequence => useHotkeys-sequences}/package.json (95%) rename examples/ember/{useHotkeySequences => useHotkeys-sequences}/tsconfig.json (100%) rename examples/ember/{useHotkeySequences => useHotkeys-sequences}/vite.config.mjs (100%) rename examples/lit/{hotkey-sequence-recorder => hotkey-recorder-sequences}/index.html (77%) rename examples/lit/{hotkey-sequence-recorder => hotkey-recorder-sequences}/package.json (85%) rename examples/lit/{hotkey-sequence-recorder => hotkey-recorder-sequences}/src/app.ts (78%) rename examples/lit/{hotkey-sequence-recorder => hotkey-recorder-sequences}/src/index.css (100%) rename examples/lit/{hotkey-sequence-recorder => hotkey-recorder-sequences}/tsconfig.json (100%) rename examples/lit/{hotkey-sequence => hotkey-sequences}/.gitignore (100%) rename examples/lit/{hotkey-sequence => hotkey-sequences}/index.html (79%) rename examples/lit/{hotkey-sequence => hotkey-sequences}/package.json (86%) rename examples/lit/{hotkey-sequence => hotkey-sequences}/src/app.ts (89%) rename examples/lit/{hotkey-sequence => hotkey-sequences}/src/index.css (100%) rename examples/lit/{hotkey-sequence => hotkey-sequences}/tsconfig.json (100%) delete mode 100644 examples/lit/kitchen-sink/README.md rename examples/octane/{useHotkeySequences => useHotkey-sequences}/index.html (79%) rename examples/octane/{useHotkeySequences => useHotkey-sequences}/package.json (100%) rename examples/octane/{useHotkeySequence => useHotkey-sequences}/src/index.css (100%) rename examples/octane/{useHotkeySequence => useHotkey-sequences}/src/main.tsrx (87%) rename examples/octane/{useHotkeySequence => useHotkey-sequences}/tsconfig.json (100%) rename examples/octane/{useHotkeySequence => useHotkey-sequences}/vite.config.ts (100%) rename examples/octane/{useHotkeySequence => useHotkeyRecorder-sequences}/index.html (80%) rename examples/octane/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/package.json (85%) rename examples/octane/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/src/index.css (100%) rename examples/octane/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/src/main.tsrx (77%) rename examples/octane/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/tsconfig.json (100%) rename examples/octane/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/vite.config.ts (100%) rename examples/octane/{useHotkeySequenceRecorder => useHotkeys-sequences}/index.html (81%) rename examples/octane/{useHotkeySequence => useHotkeys-sequences}/package.json (87%) rename examples/octane/{useHotkeySequences => useHotkeys-sequences}/src/index.css (100%) rename examples/octane/{useHotkeySequences => useHotkeys-sequences}/src/main.tsrx (80%) rename examples/octane/{useHotkeySequences => useHotkeys-sequences}/tsconfig.json (100%) rename examples/octane/{useHotkeySequences => useHotkeys-sequences}/vite.config.ts (100%) delete mode 100644 examples/preact/kitchen-sink/README.md rename examples/preact/{useHotkeySequence => useHotkey-sequences}/eslint.config.js (100%) rename examples/preact/{useHotkeySequences => useHotkey-sequences}/index.html (83%) rename examples/preact/{useHotkeySequences => useHotkey-sequences}/package.json (100%) rename examples/preact/{useHotkeySequence => useHotkey-sequences}/src/index.css (100%) rename examples/preact/{useHotkeySequence => useHotkey-sequences}/src/index.tsx (87%) rename examples/preact/{useHotkeySequence => useHotkey-sequences}/tsconfig.json (100%) rename examples/preact/{useHotkeySequence => useHotkey-sequences}/vite.config.ts (100%) rename examples/preact/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/eslint.config.js (100%) rename examples/preact/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/index.html (100%) rename examples/preact/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/package.json (87%) rename examples/preact/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/src/index.css (100%) rename examples/preact/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/src/index.tsx (78%) rename examples/preact/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/tsconfig.json (100%) rename examples/preact/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/vite.config.ts (100%) rename examples/preact/{useHotkeySequences => useHotkeys-sequences}/eslint.config.js (100%) rename examples/preact/{useHotkeySequence => useHotkeys-sequences}/index.html (83%) rename examples/preact/{useHotkeySequence => useHotkeys-sequences}/package.json (88%) rename examples/preact/{useHotkeySequences => useHotkeys-sequences}/src/index.css (100%) rename examples/preact/{useHotkeySequences => useHotkeys-sequences}/src/index.tsx (81%) rename examples/preact/{useHotkeySequences => useHotkeys-sequences}/tsconfig.json (100%) rename examples/preact/{useHotkeySequences => useHotkeys-sequences}/vite.config.ts (100%) delete mode 100644 examples/react/kitchen-sink/README.md create mode 100644 examples/react/kitchen-sink/src/app-hotkeys.ts create mode 100644 examples/react/kitchen-sink/src/routes/editor.tsx create mode 100644 examples/react/kitchen-sink/src/routes/formatting.tsx create mode 100644 examples/react/kitchen-sink/src/routes/sequences.tsx create mode 100644 examples/react/kitchen-sink/src/routes/shortcuts.tsx create mode 100644 examples/react/kitchen-sink/src/routes/tickets.tsx create mode 100644 examples/react/kitchen-sink/src/shared.tsx rename examples/react/{useHotkeySequence => useHotkey-sequences}/eslint.config.js (100%) rename examples/react/{useHotkeySequences => useHotkey-sequences}/index.html (85%) rename examples/react/{useHotkeySequences => useHotkey-sequences}/package.json (100%) rename examples/react/{useHotkeySequence => useHotkey-sequences}/src/index.css (100%) rename examples/react/{useHotkeySequence => useHotkey-sequences}/src/index.tsx (86%) rename examples/react/{useHotkeySequence => useHotkey-sequences}/tsconfig.json (100%) rename examples/react/{useHotkeySequence => useHotkey-sequences}/vite.config.ts (100%) rename examples/react/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/eslint.config.js (100%) rename examples/react/{useHotkeySequence => useHotkeyRecorder-sequences}/index.html (86%) rename examples/react/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/package.json (89%) rename examples/react/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/src/index.css (100%) rename examples/react/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/src/index.tsx (81%) rename examples/react/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/tsconfig.json (100%) rename examples/react/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/vite.config.ts (100%) rename examples/react/{useHotkeySequences => useHotkeys-sequences}/eslint.config.js (100%) rename examples/react/{useHotkeySequenceRecorder => useHotkeys-sequences}/index.html (85%) rename examples/react/{useHotkeySequence => useHotkeys-sequences}/package.json (90%) rename examples/react/{useHotkeySequences => useHotkeys-sequences}/src/index.css (100%) rename examples/react/{useHotkeySequences => useHotkeys-sequences}/src/index.tsx (80%) rename examples/react/{useHotkeySequences => useHotkeys-sequences}/tsconfig.json (100%) rename examples/react/{useHotkeySequences => useHotkeys-sequences}/vite.config.ts (100%) rename examples/solid/{createHotkeySequences => createHotkey-sequences}/index.html (70%) rename examples/solid/{createHotkeySequences => createHotkey-sequences}/package.json (100%) rename examples/solid/{createHotkeySequence => createHotkey-sequences}/src/index.css (100%) rename examples/solid/{createHotkeySequence => createHotkey-sequences}/src/index.tsx (87%) rename examples/solid/{createHotkeySequence => createHotkey-sequences}/tsconfig.json (100%) rename examples/solid/{createHotkeySequence => createHotkey-sequences}/vite.config.ts (100%) rename examples/solid/{createHotkeySequence => createHotkeyRecorder-sequences}/index.html (72%) rename examples/solid/{createHotkeySequenceRecorder => createHotkeyRecorder-sequences}/package.json (84%) rename examples/solid/{createHotkeySequenceRecorder => createHotkeyRecorder-sequences}/src/index.css (100%) rename examples/solid/{createHotkeySequenceRecorder => createHotkeyRecorder-sequences}/src/index.tsx (78%) rename examples/solid/{createHotkeySequenceRecorder => createHotkeyRecorder-sequences}/tsconfig.json (100%) rename examples/solid/{createHotkeySequenceRecorder => createHotkeyRecorder-sequences}/vite.config.ts (100%) rename examples/solid/{createHotkeySequenceRecorder => createHotkeys-sequences}/index.html (70%) rename examples/solid/{createHotkeySequence => createHotkeys-sequences}/package.json (85%) rename examples/solid/{createHotkeySequences => createHotkeys-sequences}/src/index.css (100%) rename examples/solid/{createHotkeySequences => createHotkeys-sequences}/src/index.tsx (81%) rename examples/solid/{createHotkeySequences => createHotkeys-sequences}/tsconfig.json (100%) rename examples/solid/{createHotkeySequences => createHotkeys-sequences}/vite.config.ts (100%) delete mode 100644 examples/solid/kitchen-sink/README.md rename examples/svelte/{create-hotkey-sequence-recorder => create-hotkey-recorder-sequences}/.gitignore (100%) rename examples/svelte/{create-hotkey-sequence-recorder => create-hotkey-recorder-sequences}/.npmrc (100%) rename examples/svelte/{create-hotkey-sequence-recorder => create-hotkey-recorder-sequences}/index.html (83%) rename examples/svelte/{create-hotkey-sequence-recorder => create-hotkey-recorder-sequences}/package.json (83%) rename examples/svelte/{create-hotkey-sequence-recorder => create-hotkey-recorder-sequences}/src/App.svelte (71%) rename examples/svelte/{create-hotkey-sequence-recorder => create-hotkey-recorder-sequences}/src/Root.svelte (100%) rename examples/svelte/{create-hotkey-sequence-recorder => create-hotkey-recorder-sequences}/src/ShortcutListItem.svelte (88%) rename examples/svelte/{create-hotkey-sequence-recorder => create-hotkey-recorder-sequences}/src/index.css (100%) rename examples/svelte/{create-hotkey-sequence-recorder => create-hotkey-recorder-sequences}/src/main.ts (100%) rename examples/svelte/{create-hotkey-sequence-recorder => create-hotkey-recorder-sequences}/static/robots.txt (100%) rename examples/svelte/{create-hotkey-sequence-recorder => create-hotkey-recorder-sequences}/svelte.config.js (100%) rename examples/svelte/{create-hotkey-sequence-recorder => create-hotkey-recorder-sequences}/tsconfig.json (100%) rename examples/svelte/{create-hotkey-sequence-recorder => create-hotkey-recorder-sequences}/vite.config.ts (100%) delete mode 100644 examples/svelte/create-hotkey-sequence/README.md delete mode 100644 examples/svelte/create-hotkey-sequence/src/App.svelte delete mode 100644 examples/svelte/create-hotkey-sequences/README.md rename examples/svelte/{create-hotkey-sequence => create-hotkeys-sequences}/.gitignore (100%) rename examples/svelte/{create-hotkey-sequence => create-hotkeys-sequences}/.npmrc (100%) rename examples/svelte/{create-hotkey-sequence => create-hotkeys-sequences}/index.html (80%) rename examples/svelte/{create-hotkey-sequence => create-hotkeys-sequences}/package.json (84%) create mode 100644 examples/svelte/create-hotkeys-sequences/src/App.svelte rename examples/svelte/{create-hotkey-sequence => create-hotkeys-sequences}/src/Root.svelte (100%) rename examples/svelte/{create-hotkey-sequence => create-hotkeys-sequences}/src/index.css (77%) rename examples/svelte/{create-hotkey-sequence => create-hotkeys-sequences}/src/main.ts (100%) rename examples/svelte/{create-hotkey-sequence => create-hotkeys-sequences}/static/robots.txt (100%) rename examples/svelte/{create-hotkey-sequence => create-hotkeys-sequences}/svelte.config.js (100%) rename examples/svelte/{create-hotkey-sequence => create-hotkeys-sequences}/tsconfig.json (100%) rename examples/svelte/{create-hotkey-sequence => create-hotkeys-sequences}/vite.config.ts (100%) delete mode 100644 examples/svelte/kitchen-sink/README.md delete mode 100644 examples/vue/kitchen-sink/README.md rename examples/vue/{useHotkeySequence => useHotkey-sequences}/eslint.config.js (100%) rename examples/vue/{useHotkeySequenceRecorder => useHotkey-sequences}/index.html (77%) rename examples/vue/{useHotkeySequences => useHotkey-sequences}/package.json (100%) rename examples/vue/{useHotkeySequence => useHotkey-sequences}/src/App.vue (85%) rename examples/vue/{useHotkeySequence => useHotkey-sequences}/src/index.css (100%) rename examples/vue/{useHotkeySequence => useHotkey-sequences}/src/index.ts (100%) rename examples/vue/{useHotkeySequence => useHotkey-sequences}/src/vue.d.ts (100%) rename examples/vue/{useHotkeySequence => useHotkey-sequences}/tsconfig.json (100%) rename examples/vue/{useHotkeySequence => useHotkey-sequences}/vite.config.ts (100%) rename examples/vue/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/eslint.config.js (100%) rename examples/vue/{useHotkeySequences => useHotkeyRecorder-sequences}/index.html (79%) rename examples/vue/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/package.json (89%) rename examples/vue/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/src/App.vue (71%) rename examples/vue/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/src/ShortcutListItem.vue (90%) rename examples/vue/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/src/index.css (100%) rename examples/vue/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/src/index.ts (100%) rename examples/vue/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/src/vue.d.ts (100%) rename examples/vue/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/tsconfig.json (100%) rename examples/vue/{useHotkeySequenceRecorder => useHotkeyRecorder-sequences}/vite.config.ts (100%) rename examples/vue/{useHotkeySequences => useHotkeys-sequences}/eslint.config.js (100%) rename examples/vue/{useHotkeySequence => useHotkeys-sequences}/index.html (77%) rename examples/vue/{useHotkeySequence => useHotkeys-sequences}/package.json (90%) rename examples/vue/{useHotkeySequences => useHotkeys-sequences}/src/App.vue (78%) rename examples/vue/{useHotkeySequences => useHotkeys-sequences}/src/index.css (100%) rename examples/vue/{useHotkeySequences => useHotkeys-sequences}/src/index.ts (100%) rename examples/vue/{useHotkeySequences => useHotkeys-sequences}/src/vue.d.ts (100%) rename examples/vue/{useHotkeySequences => useHotkeys-sequences}/tsconfig.json (100%) rename examples/vue/{useHotkeySequences => useHotkeys-sequences}/vite.config.ts (100%) create mode 100644 packages/alpine-hotkeys/src/createAppHotkeys.ts delete mode 100644 packages/alpine-hotkeys/src/createHotkeySequence.ts delete mode 100644 packages/alpine-hotkeys/src/createHotkeySequenceRecorder.ts delete mode 100644 packages/alpine-hotkeys/src/createHotkeySequences.ts create mode 100644 packages/alpine-hotkeys/tests/app-hotkeys.test.ts create mode 100644 packages/angular-hotkeys/src/injectAppHotkeys.ts delete mode 100644 packages/angular-hotkeys/src/injectHotkeySequence.ts delete mode 100644 packages/angular-hotkeys/src/injectHotkeySequenceRecorder.ts delete mode 100644 packages/angular-hotkeys/src/injectHotkeySequences.ts create mode 100644 packages/angular-hotkeys/tests/appHotkeys.test.ts create mode 100644 packages/ember-hotkeys/src/bindings.ts create mode 100644 packages/ember-hotkeys/src/useAppHotkeys.ts delete mode 100644 packages/ember-hotkeys/src/useHotkeySequence.ts delete mode 100644 packages/ember-hotkeys/src/useHotkeySequenceRecorder.ts delete mode 100644 packages/ember-hotkeys/src/useHotkeySequences.ts create mode 100644 packages/ember-hotkeys/tests/integration/app-hotkeys.test.gts create mode 100644 packages/hotkeys/src/app-hotkeys.ts delete mode 100644 packages/hotkeys/src/hotkey-sequence-recorder.ts delete mode 100644 packages/hotkeys/src/sequence-manager.ts create mode 100644 packages/hotkeys/tests/app-hotkeys.test.ts rename packages/hotkeys/tests/{hotkey-sequence-recorder.test.ts => hotkey-recorder-steps.test.ts} (84%) create mode 100644 packages/hotkeys/tests/unified-hotkeys.test.ts delete mode 100644 packages/lit-hotkeys/src/constants.ts create mode 100644 packages/lit-hotkeys/src/controllers/_app-hotkeys.ts create mode 100644 packages/lit-hotkeys/src/controllers/app-hotkeys.ts delete mode 100644 packages/lit-hotkeys/src/controllers/hotkey-sequence-recorder.ts delete mode 100644 packages/lit-hotkeys/src/controllers/hotkey-sequence.ts delete mode 100644 packages/lit-hotkeys/src/decorators/hotkey-sequence.ts create mode 100644 packages/lit-hotkeys/tests/app-hotkeys.test.ts create mode 100644 packages/octane-hotkeys/src/useAppHotkeys.ts delete mode 100644 packages/octane-hotkeys/src/useHotkeySequence.ts delete mode 100644 packages/octane-hotkeys/src/useHotkeySequenceRecorder.ts delete mode 100644 packages/octane-hotkeys/src/useHotkeySequences.ts create mode 100644 packages/preact-hotkeys/src/_appHotkeysStore.ts create mode 100644 packages/preact-hotkeys/src/useAppHotkeys.ts delete mode 100644 packages/preact-hotkeys/src/useHotkeySequence.ts delete mode 100644 packages/preact-hotkeys/src/useHotkeySequenceRecorder.ts delete mode 100644 packages/preact-hotkeys/src/useHotkeySequences.ts create mode 100644 packages/preact-hotkeys/tests/appHotkeys.test.tsx rename packages/preact-hotkeys/tests/{useHotkeySequences.test.tsx => useHotkeys-sequences.test.tsx} (72%) create mode 100644 packages/react-hotkeys/src/_appHotkeysStore.ts create mode 100644 packages/react-hotkeys/src/useAppHotkeys.ts delete mode 100644 packages/react-hotkeys/src/useHotkeySequence.ts delete mode 100644 packages/react-hotkeys/src/useHotkeySequenceRecorder.ts delete mode 100644 packages/react-hotkeys/src/useHotkeySequences.ts create mode 100644 packages/react-hotkeys/tests/appHotkeys.test.tsx rename packages/react-hotkeys/tests/{useHotkeySequences.test.tsx => useHotkeys-sequences.test.tsx} (70%) create mode 100644 packages/solid-hotkeys/src/appHotkeys.ts delete mode 100644 packages/solid-hotkeys/src/createHotkeySequence.ts delete mode 100644 packages/solid-hotkeys/src/createHotkeySequenceRecorder.ts delete mode 100644 packages/solid-hotkeys/src/createHotkeySequences.ts create mode 100644 packages/solid-hotkeys/tests/appHotkeys.test.tsx rename packages/solid-hotkeys/tests/{createHotkeySequences.test.tsx => createHotkeys-sequences.test.tsx} (64%) delete mode 100644 packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts delete mode 100644 packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts delete mode 100644 packages/svelte-hotkeys/src/createHotkeySequences.svelte.ts create mode 100644 packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts create mode 100644 packages/svelte-hotkeys/tests/AppHotkeysChild.svelte create mode 100644 packages/svelte-hotkeys/tests/AppHotkeysHarness.svelte create mode 100644 packages/svelte-hotkeys/tests/appHotkeys.test.ts create mode 100644 packages/vue-hotkeys/src/useAppHotkeys.ts delete mode 100644 packages/vue-hotkeys/src/useHotkeySequence.ts delete mode 100644 packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts delete mode 100644 packages/vue-hotkeys/src/useHotkeySequences.ts create mode 100644 packages/vue-hotkeys/tests/appHotkeys.test.ts diff --git a/.changeset/app-hotkeys.md b/.changeset/app-hotkeys.md new file mode 100644 index 00000000..6379fe80 --- /dev/null +++ b/.changeset/app-hotkeys.md @@ -0,0 +1,44 @@ +--- +'@tanstack/hotkeys': minor +'@tanstack/hotkeys-devtools': minor +'@tanstack/react-hotkeys': minor +'@tanstack/preact-hotkeys': minor +'@tanstack/solid-hotkeys': minor +'@tanstack/vue-hotkeys': minor +'@tanstack/svelte-hotkeys': minor +'@tanstack/angular-hotkeys': minor +'@tanstack/lit-hotkeys': minor +'@tanstack/alpine-hotkeys': minor +'@tanstack/ember-hotkeys': minor +'@tanstack/octane-hotkeys': minor +--- + +Hotkeys now include sequences everywhere, and app hotkeys can be declared up front and customized by users. + +- A hotkey is a chord (`'Mod+S'`) or a sequence (`['G', 'G']`). Every adapter's hotkey and hotkeys APIs, `HotkeyManager.register`, `formatForDisplay`, `areHotkeysEqual`, `validateHotkey`, and hints accept both. A one-step sequence is its chord. Chords accept `requireReset`; sequences accept `timeout`, enforced by types. +- Every adapter's recorder records sequences with `maxSteps` (default 1). +- `defineAppHotkeys` declares every app hotkey. Pass them to the adapter's provider as `appHotkeys`, with optional `customHotkeys` and `onCustomHotkeysChange` to persist user changes. Each adapter can read and change them through its app hotkeys, app hotkey list, and custom hotkeys APIs (for example, React's `useAppHotkeys`, `useAppHotkeyList`, and `useCustomHotkeys`). Lit, Alpine, and Ember, which have no provider, use `provideAppHotkeys`. Devtools list declared hotkeys that are not registered. + +Breaking changes (React names shown; other adapters follow their own naming, such as `createHotkey`, `injectHotkey`, or `HotkeyController`): + +| Before | After | +| ------------------------------------------------------------------------ | --------------------------------------------------------------- | +| `Hotkey` (one chord) | `HotkeyChord`; `Hotkey` is now a chord or a sequence | +| `SequenceManager`, `getSequenceManager()` | `HotkeyManager`, `getHotkeyManager()` | +| `useHotkeySequence(sequence, cb, options)` | `useHotkey(sequence, cb, options)` | +| `useHotkeySequences([{ sequence, callback }])` | `useHotkeys([{ hotkey: sequence, callback }])` | +| `useHotkeySequenceRecorder(options)`, `HotkeySequenceRecorder` | `useHotkeyRecorder({ maxSteps, ...options })`, `HotkeyRecorder` | +| Lit `@hotkeySequence`, `HotkeySequenceController` | `@hotkey`, `HotkeyController` | +| Lit `HotkeySequenceRecorderController` | `HotkeyRecorderController` with `maxSteps` | +| `useHotkeyRegistrations()` → `{ hotkeys, sequences }` | One list of chords and sequences; narrow with `kind` | +| `SequenceRegistrationView.sequence` | `HotkeySequenceRegistrationView.hotkey` | +| `HotkeyConflict.type` | `HotkeyConflict.registration.kind` | +| Sequence callback `context.hotkey` (`'G G'`) | `context.hotkey` (`['G', 'G']`) | +| `defaultOptions.hotkeySequence`, `defaultOptions.hotkeySequenceRecorder` | `defaultOptions.hotkey`, `defaultOptions.hotkeyRecorder` | +| Vue `provideHotkeysContext(defaultOptions)` | `provideHotkeysContext({ defaultOptions, appHotkeys, ... })` | +| Svelte `setHotkeysContext(defaultOptions)` | `setHotkeysContext({ defaultOptions, appHotkeys, ... })` | +| Angular `provideHotkeys(defaultOptions)` | `provideHotkeys({ defaultOptions, appHotkeys, ... })` | +| `HotkeyDefinition` (core) | Each adapter's definition type, such as `UseHotkeyDefinition` | +| `createHotkeySequenceBindings` (adapter) | `createHotkeyBindings` | +| `formatHotkeySequence`, `formatWithLabels` | `formatForDisplay` | +| `SequenceManager.resetAll()` | `HotkeyManager.resetSequenceProgress()` | diff --git a/docs/config.json b/docs/config.json index fe558033..e535d7e1 100644 --- a/docs/config.json +++ b/docs/config.json @@ -36,8 +36,8 @@ "label": "preact", "children": [ { - "label": "Preact Hooks", - "to": "framework/preact/reference/index" + "label": "Quick Start", + "to": "framework/preact/quick-start" } ] }, @@ -45,8 +45,8 @@ "label": "solid", "children": [ { - "label": "Solid Primitives", - "to": "framework/solid/reference/index" + "label": "Quick Start", + "to": "framework/solid/quick-start" } ] }, @@ -131,12 +131,12 @@ "to": "framework/react/guides/sequences" }, { - "label": "Hotkey Recording", - "to": "framework/react/guides/hotkey-recording" + "label": "App Hotkeys", + "to": "framework/react/guides/app-hotkeys" }, { - "label": "Hotkey Sequence Recording", - "to": "framework/react/guides/sequence-recording" + "label": "Hotkey Recording", + "to": "framework/react/guides/hotkey-recording" }, { "label": "Key State Tracking", @@ -160,12 +160,12 @@ "to": "framework/preact/guides/sequences" }, { - "label": "Hotkey Recording", - "to": "framework/preact/guides/hotkey-recording" + "label": "App Hotkeys", + "to": "framework/preact/guides/app-hotkeys" }, { - "label": "Hotkey Sequence Recording", - "to": "framework/preact/guides/sequence-recording" + "label": "Hotkey Recording", + "to": "framework/preact/guides/hotkey-recording" }, { "label": "Key State Tracking", @@ -189,12 +189,12 @@ "to": "framework/solid/guides/sequences" }, { - "label": "Hotkey Recording", - "to": "framework/solid/guides/hotkey-recording" + "label": "App Hotkeys", + "to": "framework/solid/guides/app-hotkeys" }, { - "label": "Hotkey Sequence Recording", - "to": "framework/solid/guides/sequence-recording" + "label": "Hotkey Recording", + "to": "framework/solid/guides/hotkey-recording" }, { "label": "Key State Tracking", @@ -218,12 +218,12 @@ "to": "framework/angular/guides/sequences" }, { - "label": "Hotkey Recording", - "to": "framework/angular/guides/hotkey-recording" + "label": "App Hotkeys", + "to": "framework/angular/guides/app-hotkeys" }, { - "label": "Hotkey Sequence Recording", - "to": "framework/angular/guides/sequence-recording" + "label": "Hotkey Recording", + "to": "framework/angular/guides/hotkey-recording" }, { "label": "Key State Tracking", @@ -247,12 +247,12 @@ "to": "framework/vue/guides/sequences" }, { - "label": "Hotkey Recording", - "to": "framework/vue/guides/hotkey-recording" + "label": "App Hotkeys", + "to": "framework/vue/guides/app-hotkeys" }, { - "label": "Hotkey Sequence Recording", - "to": "framework/vue/guides/sequence-recording" + "label": "Hotkey Recording", + "to": "framework/vue/guides/hotkey-recording" }, { "label": "Key State Tracking", @@ -276,12 +276,12 @@ "to": "framework/lit/guides/sequences" }, { - "label": "Hotkey Recording", - "to": "framework/lit/guides/hotkey-recording" + "label": "App Hotkeys", + "to": "framework/lit/guides/app-hotkeys" }, { - "label": "Hotkey Sequence Recording", - "to": "framework/lit/guides/sequence-recording" + "label": "Hotkey Recording", + "to": "framework/lit/guides/hotkey-recording" }, { "label": "Key State Tracking", @@ -305,12 +305,12 @@ "to": "framework/svelte/guides/sequences" }, { - "label": "Hotkey Recording", - "to": "framework/svelte/guides/hotkey-recording" + "label": "App Hotkeys", + "to": "framework/svelte/guides/app-hotkeys" }, { - "label": "Hotkey Sequence Recording", - "to": "framework/svelte/guides/sequence-recording" + "label": "Hotkey Recording", + "to": "framework/svelte/guides/hotkey-recording" }, { "label": "Key State Tracking", @@ -334,12 +334,12 @@ "to": "framework/alpine/guides/sequences" }, { - "label": "Hotkey Recording", - "to": "framework/alpine/guides/hotkey-recording" + "label": "App Hotkeys", + "to": "framework/alpine/guides/app-hotkeys" }, { - "label": "Hotkey Sequence Recording", - "to": "framework/alpine/guides/sequence-recording" + "label": "Hotkey Recording", + "to": "framework/alpine/guides/hotkey-recording" }, { "label": "Key State Tracking", @@ -363,12 +363,12 @@ "to": "framework/ember/guides/sequences" }, { - "label": "Hotkey Recording", - "to": "framework/ember/guides/hotkey-recording" + "label": "App Hotkeys", + "to": "framework/ember/guides/app-hotkeys" }, { - "label": "Hotkey Sequence Recording", - "to": "framework/ember/guides/sequence-recording" + "label": "Hotkey Recording", + "to": "framework/ember/guides/hotkey-recording" }, { "label": "Key State Tracking", @@ -392,12 +392,12 @@ "to": "framework/octane/guides/sequences" }, { - "label": "Hotkey Recording", - "to": "framework/octane/guides/hotkey-recording" + "label": "App Hotkeys", + "to": "framework/octane/guides/app-hotkeys" }, { - "label": "Hotkey Sequence Recording", - "to": "framework/octane/guides/sequence-recording" + "label": "Hotkey Recording", + "to": "framework/octane/guides/hotkey-recording" }, { "label": "Key State Tracking", @@ -417,6 +417,10 @@ { "label": "Core API", "to": "reference/index" + }, + { + "label": "Adapter API", + "to": "reference/adapter/index" } ], "frameworks": [ @@ -513,9 +517,9 @@ ] }, { + "label": "Hotkeys API Reference", "collapsible": true, "defaultCollapsed": true, - "label": "Hotkeys API Reference", "children": [ { "label": "HotkeyManager", @@ -525,18 +529,66 @@ "label": "getHotkeyManager", "to": "reference/functions/getHotkeyManager" }, + { + "label": "Hotkey", + "to": "reference/type-aliases/Hotkey" + }, + { + "label": "HotkeyChord", + "to": "reference/type-aliases/HotkeyChord" + }, + { + "label": "HotkeySequence", + "to": "reference/type-aliases/HotkeySequence" + }, + { + "label": "RegisterableHotkey", + "to": "reference/type-aliases/RegisterableHotkey" + }, + { + "label": "RegisterableHotkeyChord", + "to": "reference/type-aliases/RegisterableHotkeyChord" + }, + { + "label": "RawHotkey", + "to": "reference/type-aliases/RawHotkey" + }, { "label": "HotkeyOptions", "to": "reference/interfaces/HotkeyOptions" }, { - "label": "HotkeyRegistration", - "to": "reference/interfaces/HotkeyRegistration" + "label": "HotkeyChordOptions", + "to": "reference/interfaces/HotkeyChordOptions" + }, + { + "label": "HotkeySequenceOptions", + "to": "reference/interfaces/HotkeySequenceOptions" + }, + { + "label": "HotkeyOptionsFor", + "to": "reference/type-aliases/HotkeyOptionsFor" }, { "label": "HotkeyRegistrationHandle", "to": "reference/interfaces/HotkeyRegistrationHandle" }, + { + "label": "HotkeyRegistrationView", + "to": "reference/type-aliases/HotkeyRegistrationView" + }, + { + "label": "HotkeyChordRegistrationView", + "to": "reference/interfaces/HotkeyChordRegistrationView" + }, + { + "label": "HotkeySequenceRegistrationView", + "to": "reference/interfaces/HotkeySequenceRegistrationView" + }, + { + "label": "toHotkeyRegistrationView", + "to": "reference/functions/toHotkeyRegistrationView" + }, { "label": "HotkeyCallback", "to": "reference/type-aliases/HotkeyCallback" @@ -550,30 +602,46 @@ "to": "reference/interfaces/HotkeyMeta" }, { - "label": "HotkeyRegistrationView", - "to": "reference/interfaces/HotkeyRegistrationView" + "label": "ConflictBehavior", + "to": "reference/type-aliases/ConflictBehavior" }, { - "label": "toHotkeyRegistrationView", - "to": "reference/functions/toHotkeyRegistrationView" + "label": "isHotkeySequence", + "to": "reference/functions/isHotkeySequence" }, { - "label": "ConflictBehavior", - "to": "reference/type-aliases/ConflictBehavior" + "label": "simplifyHotkey", + "to": "reference/functions/simplifyHotkey" + }, + { + "label": "createSequenceMatcher", + "to": "reference/functions/createSequenceMatcher" }, { - "label": "HotkeyDefinition", - "to": "reference/adapter/interfaces/HotkeyDefinition" + "label": "DEFAULT_SEQUENCE_TIMEOUT", + "to": "reference/variables/DEFAULT_SEQUENCE_TIMEOUT" } ], "frameworks": [ { "label": "react", "children": [ + { + "label": "HotkeysProvider", + "to": "framework/react/reference/functions/HotkeysProvider" + }, + { + "label": "useDefaultHotkeysOptions", + "to": "framework/react/reference/functions/useDefaultHotkeysOptions" + }, { "label": "useHotkey", "to": "framework/react/reference/functions/useHotkey" }, + { + "label": "useHotkeyRegistrations", + "to": "framework/react/reference/functions/useHotkeyRegistrations" + }, { "label": "useHotkeys", "to": "framework/react/reference/functions/useHotkeys" @@ -583,46 +651,62 @@ "to": "framework/react/reference/functions/useHotkeysContext" }, { - "label": "UseHotkeyOptions", - "to": "framework/react/reference/interfaces/UseHotkeyOptions" + "label": "HotkeysProviderOptions", + "to": "framework/react/reference/interfaces/HotkeysProviderOptions" }, { - "label": "UseHotkeyDefinition", - "to": "framework/react/reference/interfaces/UseHotkeyDefinition" + "label": "HotkeysProviderProps", + "to": "framework/react/reference/interfaces/HotkeysProviderProps" }, { - "label": "HotkeysProvider", - "to": "framework/react/reference/functions/HotkeysProvider" + "label": "UseHotkeyChordOptions", + "to": "framework/react/reference/type-aliases/UseHotkeyChordOptions" }, { - "label": "HotkeysProviderProps", - "to": "framework/react/reference/interfaces/HotkeysProviderProps" + "label": "UseHotkeyCommonOptions", + "to": "framework/react/reference/type-aliases/UseHotkeyCommonOptions" }, { - "label": "HotkeysProviderOptions", - "to": "framework/react/reference/interfaces/HotkeysProviderOptions" + "label": "UseHotkeyDefinition", + "to": "framework/react/reference/type-aliases/UseHotkeyDefinition" }, { - "label": "useDefaultHotkeysOptions", - "to": "framework/react/reference/functions/useDefaultHotkeysOptions" + "label": "UseHotkeyOptions", + "to": "framework/react/reference/type-aliases/UseHotkeyOptions" }, { - "label": "useHotkeyRegistrations", - "to": "framework/react/reference/functions/useHotkeyRegistrations" + "label": "UseHotkeyOptionsFor", + "to": "framework/react/reference/type-aliases/UseHotkeyOptionsFor" }, { - "label": "HotkeyRegistrationsResult", - "to": "framework/react/reference/interfaces/HotkeyRegistrationsResult" + "label": "UseHotkeySequenceOptions", + "to": "framework/react/reference/type-aliases/UseHotkeySequenceOptions" + }, + { + "label": "UseHotkeyTarget", + "to": "framework/react/reference/type-aliases/UseHotkeyTarget" } ] }, { "label": "preact", "children": [ + { + "label": "HotkeysProvider", + "to": "framework/preact/reference/functions/HotkeysProvider" + }, + { + "label": "useDefaultHotkeysOptions", + "to": "framework/preact/reference/functions/useDefaultHotkeysOptions" + }, { "label": "useHotkey", "to": "framework/preact/reference/functions/useHotkey" }, + { + "label": "useHotkeyRegistrations", + "to": "framework/preact/reference/functions/useHotkeyRegistrations" + }, { "label": "useHotkeys", "to": "framework/preact/reference/functions/useHotkeys" @@ -632,36 +716,40 @@ "to": "framework/preact/reference/functions/useHotkeysContext" }, { - "label": "UseHotkeyOptions", - "to": "framework/preact/reference/interfaces/UseHotkeyOptions" + "label": "HotkeysProviderOptions", + "to": "framework/preact/reference/interfaces/HotkeysProviderOptions" }, { - "label": "UseHotkeyDefinition", - "to": "framework/preact/reference/interfaces/UseHotkeyDefinition" + "label": "HotkeysProviderProps", + "to": "framework/preact/reference/interfaces/HotkeysProviderProps" }, { - "label": "HotkeysProvider", - "to": "framework/preact/reference/functions/HotkeysProvider" + "label": "UseHotkeyChordOptions", + "to": "framework/preact/reference/type-aliases/UseHotkeyChordOptions" }, { - "label": "HotkeysProviderProps", - "to": "framework/preact/reference/interfaces/HotkeysProviderProps" + "label": "UseHotkeyCommonOptions", + "to": "framework/preact/reference/type-aliases/UseHotkeyCommonOptions" }, { - "label": "HotkeysProviderOptions", - "to": "framework/preact/reference/interfaces/HotkeysProviderOptions" + "label": "UseHotkeyDefinition", + "to": "framework/preact/reference/type-aliases/UseHotkeyDefinition" }, { - "label": "useDefaultHotkeysOptions", - "to": "framework/preact/reference/functions/useDefaultHotkeysOptions" + "label": "UseHotkeyOptions", + "to": "framework/preact/reference/type-aliases/UseHotkeyOptions" }, { - "label": "useHotkeyRegistrations", - "to": "framework/preact/reference/functions/useHotkeyRegistrations" + "label": "UseHotkeyOptionsFor", + "to": "framework/preact/reference/type-aliases/UseHotkeyOptionsFor" }, { - "label": "HotkeyRegistrationsResult", - "to": "framework/preact/reference/interfaces/HotkeyRegistrationsResult" + "label": "UseHotkeySequenceOptions", + "to": "framework/preact/reference/type-aliases/UseHotkeySequenceOptions" + }, + { + "label": "UseHotkeyTarget", + "to": "framework/preact/reference/type-aliases/UseHotkeyTarget" } ] }, @@ -672,43 +760,79 @@ "label": "createHotkey", "to": "framework/solid/reference/functions/createHotkey" }, + { + "label": "createHotkeyRegistrations", + "to": "framework/solid/reference/functions/createHotkeyRegistrations" + }, { "label": "createHotkeys", "to": "framework/solid/reference/functions/createHotkeys" }, + { + "label": "useDefaultHotkeysOptions", + "to": "framework/solid/reference/functions/useDefaultHotkeysOptions" + }, { "label": "useHotkeysContext", "to": "framework/solid/reference/functions/useHotkeysContext" }, { - "label": "CreateHotkeyOptions", - "to": "framework/solid/reference/interfaces/CreateHotkeyOptions" + "label": "HotkeysProviderOptions", + "to": "framework/solid/reference/interfaces/HotkeysProviderOptions" + }, + { + "label": "HotkeysProviderProps", + "to": "framework/solid/reference/interfaces/HotkeysProviderProps" + }, + { + "label": "CreateHotkeyChordOptions", + "to": "framework/solid/reference/type-aliases/CreateHotkeyChordOptions" + }, + { + "label": "CreateHotkeyCommonOptions", + "to": "framework/solid/reference/type-aliases/CreateHotkeyCommonOptions" }, { "label": "CreateHotkeyDefinition", - "to": "framework/solid/reference/interfaces/CreateHotkeyDefinition" + "to": "framework/solid/reference/type-aliases/CreateHotkeyDefinition" }, { - "label": "useDefaultHotkeysOptions", - "to": "framework/solid/reference/functions/useDefaultHotkeysOptions" + "label": "CreateHotkeyOptions", + "to": "framework/solid/reference/type-aliases/CreateHotkeyOptions" }, { - "label": "createHotkeyRegistrations", - "to": "framework/solid/reference/functions/createHotkeyRegistrations" + "label": "CreateHotkeyOptionsFor", + "to": "framework/solid/reference/type-aliases/CreateHotkeyOptionsFor" }, { - "label": "HotkeyRegistrationsResult", - "to": "framework/solid/reference/interfaces/HotkeyRegistrationsResult" + "label": "CreateHotkeySequenceOptions", + "to": "framework/solid/reference/type-aliases/CreateHotkeySequenceOptions" + }, + { + "label": "CreateHotkeyTarget", + "to": "framework/solid/reference/type-aliases/CreateHotkeyTarget" + }, + { + "label": "HotkeysProvider", + "to": "framework/solid/reference/variables/HotkeysProvider" } ] }, { "label": "angular", "children": [ + { + "label": "injectDefaultHotkeysOptions", + "to": "framework/angular/reference/functions/injectDefaultHotkeysOptions" + }, { "label": "injectHotkey", "to": "framework/angular/reference/functions/injectHotkey" }, + { + "label": "injectHotkeyRegistrations", + "to": "framework/angular/reference/functions/injectHotkeyRegistrations" + }, { "label": "injectHotkeys", "to": "framework/angular/reference/functions/injectHotkeys" @@ -718,42 +842,74 @@ "to": "framework/angular/reference/functions/injectHotkeysContext" }, { - "label": "InjectHotkeyOptions", - "to": "framework/angular/reference/interfaces/InjectHotkeyOptions" + "label": "provideHotkeys", + "to": "framework/angular/reference/functions/provideHotkeys" + }, + { + "label": "HotkeysContextValue", + "to": "framework/angular/reference/interfaces/HotkeysContextValue" + }, + { + "label": "HotkeysProviderOptions", + "to": "framework/angular/reference/interfaces/HotkeysProviderOptions" + }, + { + "label": "ProvideHotkeysOptions", + "to": "framework/angular/reference/interfaces/ProvideHotkeysOptions" + }, + { + "label": "InjectHotkeyChordOptions", + "to": "framework/angular/reference/type-aliases/InjectHotkeyChordOptions" + }, + { + "label": "InjectHotkeyCommonOptions", + "to": "framework/angular/reference/type-aliases/InjectHotkeyCommonOptions" }, { "label": "InjectHotkeyDefinition", - "to": "framework/angular/reference/interfaces/InjectHotkeyDefinition" + "to": "framework/angular/reference/type-aliases/InjectHotkeyDefinition" }, { - "label": "provideHotkeys", - "to": "framework/angular/reference/functions/provideHotkeys" + "label": "InjectHotkeyOptions", + "to": "framework/angular/reference/type-aliases/InjectHotkeyOptions" }, { - "label": "HotkeysProviderOptions", - "to": "framework/angular/reference/interfaces/HotkeysProviderOptions" + "label": "InjectHotkeyOptionsFor", + "to": "framework/angular/reference/type-aliases/InjectHotkeyOptionsFor" }, { - "label": "injectDefaultHotkeysOptions", - "to": "framework/angular/reference/functions/injectDefaultHotkeysOptions" + "label": "InjectHotkeySequenceOptions", + "to": "framework/angular/reference/type-aliases/InjectHotkeySequenceOptions" }, { - "label": "injectHotkeyRegistrations", - "to": "framework/angular/reference/functions/injectHotkeyRegistrations" + "label": "InjectHotkeyTarget", + "to": "framework/angular/reference/type-aliases/InjectHotkeyTarget" }, { - "label": "HotkeyRegistrationsResult", - "to": "framework/angular/reference/interfaces/HotkeyRegistrationsResult" + "label": "HOTKEYS_INJECTION_TOKEN", + "to": "framework/angular/reference/variables/HOTKEYS_INJECTION_TOKEN" } ] }, { "label": "vue", "children": [ + { + "label": "provideHotkeysContext", + "to": "framework/vue/reference/functions/provideHotkeysContext" + }, + { + "label": "useDefaultHotkeysOptions", + "to": "framework/vue/reference/functions/useDefaultHotkeysOptions" + }, { "label": "useHotkey", "to": "framework/vue/reference/functions/useHotkey" }, + { + "label": "useHotkeyRegistrations", + "to": "framework/vue/reference/functions/useHotkeyRegistrations" + }, { "label": "useHotkeys", "to": "framework/vue/reference/functions/useHotkeys" @@ -763,34 +919,46 @@ "to": "framework/vue/reference/functions/useHotkeysContext" }, { - "label": "UseHotkeyOptions", - "to": "framework/vue/reference/interfaces/UseHotkeyOptions" + "label": "HotkeysProviderOptions", + "to": "framework/vue/reference/interfaces/HotkeysProviderOptions" }, { - "label": "UseHotkeyDefinition", - "to": "framework/vue/reference/interfaces/UseHotkeyDefinition" + "label": "ProvideHotkeysContextOptions", + "to": "framework/vue/reference/interfaces/ProvideHotkeysContextOptions" }, { - "label": "provideHotkeysContext", - "to": "framework/vue/reference/functions/provideHotkeysContext" + "label": "UseHotkeyChordOptions", + "to": "framework/vue/reference/type-aliases/UseHotkeyChordOptions" }, { - "label": "HotkeysProviderOptions", - "to": "framework/vue/reference/interfaces/HotkeysProviderOptions" + "label": "UseHotkeyCommonOptions", + "to": "framework/vue/reference/type-aliases/UseHotkeyCommonOptions" }, { - "label": "useDefaultHotkeysOptions", - "to": "framework/vue/reference/functions/useDefaultHotkeysOptions" + "label": "UseHotkeyDefinition", + "to": "framework/vue/reference/type-aliases/UseHotkeyDefinition" }, { - "label": "useHotkeyRegistrations", - "to": "framework/vue/reference/functions/useHotkeyRegistrations" + "label": "UseHotkeyOptions", + "to": "framework/vue/reference/type-aliases/UseHotkeyOptions" }, { - "label": "HotkeyRegistrationsResult", - "to": "framework/vue/reference/interfaces/HotkeyRegistrationsResult" - } - ] + "label": "UseHotkeyOptionsFor", + "to": "framework/vue/reference/type-aliases/UseHotkeyOptionsFor" + }, + { + "label": "UseHotkeySequenceOptions", + "to": "framework/vue/reference/type-aliases/UseHotkeySequenceOptions" + }, + { + "label": "UseHotkeyTarget", + "to": "framework/vue/reference/type-aliases/UseHotkeyTarget" + }, + { + "label": "HotkeysProvider", + "to": "framework/vue/reference/variables/HotkeysProvider" + } + ] }, { "label": "lit", @@ -806,6 +974,30 @@ { "label": "HotkeyRegistrationsController", "to": "framework/lit/reference/classes/HotkeyRegistrationsController" + }, + { + "label": "HotkeyControllerChordOptions", + "to": "framework/lit/reference/type-aliases/HotkeyControllerChordOptions" + }, + { + "label": "HotkeyControllerCommonOptions", + "to": "framework/lit/reference/type-aliases/HotkeyControllerCommonOptions" + }, + { + "label": "HotkeyControllerOptions", + "to": "framework/lit/reference/type-aliases/HotkeyControllerOptions" + }, + { + "label": "HotkeyControllerOptionsFor", + "to": "framework/lit/reference/type-aliases/HotkeyControllerOptionsFor" + }, + { + "label": "HotkeyControllerSequenceOptions", + "to": "framework/lit/reference/type-aliases/HotkeyControllerSequenceOptions" + }, + { + "label": "HotkeyControllerTarget", + "to": "framework/lit/reference/type-aliases/HotkeyControllerTarget" } ] }, @@ -816,29 +1008,37 @@ "label": "createHotkey", "to": "framework/svelte/reference/functions/createHotkey" }, - { - "label": "createHotkeys", - "to": "framework/svelte/reference/functions/createHotkeys" - }, { "label": "createHotkeyAttachment", "to": "framework/svelte/reference/functions/createHotkeyAttachment" }, + { + "label": "createHotkeys", + "to": "framework/svelte/reference/functions/createHotkeys" + }, { "label": "createHotkeysAttachment", "to": "framework/svelte/reference/functions/createHotkeysAttachment" }, { - "label": "CreateHotkeyOptions", - "to": "framework/svelte/reference/interfaces/CreateHotkeyOptions" + "label": "getDefaultHotkeysOptions", + "to": "framework/svelte/reference/functions/getDefaultHotkeysOptions" }, { - "label": "CreateHotkeyDefinition", - "to": "framework/svelte/reference/interfaces/CreateHotkeyDefinition" + "label": "getHotkeyRegistrations", + "to": "framework/svelte/reference/functions/getHotkeyRegistrations" }, { - "label": "HotkeysProvider", - "to": "framework/svelte/reference/variables/HotkeysProvider" + "label": "getHotkeysContext", + "to": "framework/svelte/reference/functions/getHotkeysContext" + }, + { + "label": "setHotkeysContext", + "to": "framework/svelte/reference/functions/setHotkeysContext" + }, + { + "label": "HotkeysContextOptions", + "to": "framework/svelte/reference/interfaces/HotkeysContextOptions" }, { "label": "HotkeysProviderOptions", @@ -849,28 +1049,44 @@ "to": "framework/svelte/reference/interfaces/HotkeysProviderProps" }, { - "label": "DEFAULT_OPTIONS", - "to": "framework/svelte/reference/variables/DEFAULT_OPTIONS" + "label": "SvelteHotkeyRegistrations", + "to": "framework/svelte/reference/interfaces/SvelteHotkeyRegistrations" }, { - "label": "getDefaultHotkeysOptions", - "to": "framework/svelte/reference/functions/getDefaultHotkeysOptions" + "label": "CreateHotkeyChordOptions", + "to": "framework/svelte/reference/type-aliases/CreateHotkeyChordOptions" }, { - "label": "getHotkeysContext", - "to": "framework/svelte/reference/functions/getHotkeysContext" + "label": "CreateHotkeyCommonOptions", + "to": "framework/svelte/reference/type-aliases/CreateHotkeyCommonOptions" }, { - "label": "setHotkeysContext", - "to": "framework/svelte/reference/functions/setHotkeysContext" + "label": "CreateHotkeyDefinition", + "to": "framework/svelte/reference/type-aliases/CreateHotkeyDefinition" }, { - "label": "getHotkeyRegistrations", - "to": "framework/svelte/reference/functions/getHotkeyRegistrations" + "label": "CreateHotkeyOptions", + "to": "framework/svelte/reference/type-aliases/CreateHotkeyOptions" }, { - "label": "SvelteHotkeyRegistrations", - "to": "framework/svelte/reference/interfaces/SvelteHotkeyRegistrations" + "label": "CreateHotkeyOptionsFor", + "to": "framework/svelte/reference/type-aliases/CreateHotkeyOptionsFor" + }, + { + "label": "CreateHotkeySequenceOptions", + "to": "framework/svelte/reference/type-aliases/CreateHotkeySequenceOptions" + }, + { + "label": "CreateHotkeyTarget", + "to": "framework/svelte/reference/type-aliases/CreateHotkeyTarget" + }, + { + "label": "DEFAULT_OPTIONS", + "to": "framework/svelte/reference/variables/DEFAULT_OPTIONS" + }, + { + "label": "HotkeysProvider", + "to": "framework/svelte/reference/variables/HotkeysProvider" } ] }, @@ -886,34 +1102,58 @@ "to": "framework/alpine/reference/functions/hotkeysPlugin" }, { - "label": "AlpineHotkeys", - "to": "framework/alpine/reference/interfaces/AlpineHotkeys" + "label": "AlpineCreateHotkey", + "to": "framework/alpine/reference/interfaces/AlpineCreateHotkey" }, { - "label": "createHotkey", - "to": "framework/alpine/reference/interfaces/AlpineHotkeys#createhotkey" + "label": "AlpineHotkeys", + "to": "framework/alpine/reference/interfaces/AlpineHotkeys" }, { - "label": "createHotkeys", - "to": "framework/alpine/reference/interfaces/AlpineHotkeys#createhotkeys" + "label": "AlpineHotkeyState", + "to": "framework/alpine/reference/interfaces/AlpineHotkeyState" }, { - "label": "createHotkeyRegistrations", - "to": "framework/alpine/reference/interfaces/AlpineHotkeys#createhotkeyregistrations" + "label": "DefaultHotkeysOptions", + "to": "framework/alpine/reference/interfaces/DefaultHotkeysOptions" }, { "label": "HotkeyRegistrationsResult", "to": "framework/alpine/reference/interfaces/HotkeyRegistrationsResult" }, { - "label": "DefaultHotkeysOptions", - "to": "framework/alpine/reference/interfaces/DefaultHotkeysOptions" + "label": "AlpineHotkeyCommonOptions", + "to": "framework/alpine/reference/type-aliases/AlpineHotkeyCommonOptions" + }, + { + "label": "AlpineHotkeyDefinition", + "to": "framework/alpine/reference/type-aliases/AlpineHotkeyDefinition" + }, + { + "label": "AlpineHotkeyOptionsFor", + "to": "framework/alpine/reference/type-aliases/AlpineHotkeyOptionsFor" + }, + { + "label": "AlpineHotkeyTarget", + "to": "framework/alpine/reference/type-aliases/AlpineHotkeyTarget" + }, + { + "label": "MaybeGetter", + "to": "framework/alpine/reference/type-aliases/MaybeGetter" } ] }, { "label": "ember", "children": [ + { + "label": "createHotkeysScope", + "to": "framework/ember/reference/functions/createHotkeysScope" + }, + { + "label": "useHotkeyRegistrations", + "to": "framework/ember/reference/functions/useHotkeyRegistrations" + }, { "label": "onHotkey", "to": "framework/ember/reference/classes/onHotkey" @@ -922,10 +1162,6 @@ "label": "onHotkeys", "to": "framework/ember/reference/classes/onHotkeys" }, - { - "label": "ElementHotkeyOptions", - "to": "framework/ember/reference/type-aliases/ElementHotkeyOptions" - }, { "label": "useHotkey", "to": "framework/ember/reference/classes/useHotkey" @@ -935,24 +1171,40 @@ "to": "framework/ember/reference/classes/useHotkeys" }, { - "label": "useHotkeyRegistrations", - "to": "framework/ember/reference/functions/useHotkeyRegistrations" + "label": "DefaultHotkeysOptions", + "to": "framework/ember/reference/interfaces/DefaultHotkeysOptions" + }, + { + "label": "EmberHotkeysScope", + "to": "framework/ember/reference/interfaces/EmberHotkeysScope" + }, + { + "label": "EmberHotkeyState", + "to": "framework/ember/reference/interfaces/EmberHotkeyState" }, { "label": "HotkeyRegistrationsResult", "to": "framework/ember/reference/interfaces/HotkeyRegistrationsResult" }, { - "label": "createHotkeysScope", - "to": "framework/ember/reference/functions/createHotkeysScope" + "label": "ElementHotkeyOptions", + "to": "framework/ember/reference/type-aliases/ElementHotkeyOptions" }, { - "label": "EmberHotkeysScope", - "to": "framework/ember/reference/interfaces/EmberHotkeysScope" + "label": "EmberHotkeyCommonOptions", + "to": "framework/ember/reference/type-aliases/EmberHotkeyCommonOptions" }, { - "label": "DefaultHotkeysOptions", - "to": "framework/ember/reference/interfaces/DefaultHotkeysOptions" + "label": "EmberHotkeyDefinition", + "to": "framework/ember/reference/type-aliases/EmberHotkeyDefinition" + }, + { + "label": "EmberHotkeyTarget", + "to": "framework/ember/reference/type-aliases/EmberHotkeyTarget" + }, + { + "label": "MaybeGetter", + "to": "framework/ember/reference/type-aliases/MaybeGetter" } ] }, @@ -960,36 +1212,36 @@ "label": "octane", "children": [ { - "label": "useHotkey", - "to": "framework/octane/reference/functions/useHotkey" + "label": "HotkeysProvider", + "to": "framework/octane/reference/functions/HotkeysProvider" }, { - "label": "useHotkeys", - "to": "framework/octane/reference/functions/useHotkeys" + "label": "useDefaultHotkeysOptions", + "to": "framework/octane/reference/functions/useDefaultHotkeysOptions" + }, + { + "label": "useHotkey", + "to": "framework/octane/reference/functions/useHotkey" }, { "label": "useHotkeyRegistrations", "to": "framework/octane/reference/functions/useHotkeyRegistrations" }, { - "label": "HotkeyRegistrationsResult", - "to": "framework/octane/reference/interfaces/HotkeyRegistrationsResult" + "label": "useHotkeys", + "to": "framework/octane/reference/functions/useHotkeys" }, { - "label": "HotkeysProvider", - "to": "framework/octane/reference/functions/HotkeysProvider" + "label": "useHotkeysContext", + "to": "framework/octane/reference/functions/useHotkeysContext" }, { "label": "DefaultHotkeysOptions", "to": "framework/octane/reference/interfaces/DefaultHotkeysOptions" }, { - "label": "useDefaultHotkeysOptions", - "to": "framework/octane/reference/functions/useDefaultHotkeysOptions" - }, - { - "label": "useHotkeysContext", - "to": "framework/octane/reference/functions/useHotkeysContext" + "label": "HotkeyRegistrationsResult", + "to": "framework/octane/reference/interfaces/HotkeyRegistrationsResult" }, { "label": "HotkeysProviderProps", @@ -998,47 +1250,115 @@ { "label": "HotkeysProviderOptions", "to": "framework/octane/reference/type-aliases/HotkeysProviderOptions" + }, + { + "label": "UseHotkeyChordOptions", + "to": "framework/octane/reference/type-aliases/UseHotkeyChordOptions" + }, + { + "label": "UseHotkeyCommonOptions", + "to": "framework/octane/reference/type-aliases/UseHotkeyCommonOptions" + }, + { + "label": "UseHotkeyDefinition", + "to": "framework/octane/reference/type-aliases/UseHotkeyDefinition" + }, + { + "label": "UseHotkeyOptions", + "to": "framework/octane/reference/type-aliases/UseHotkeyOptions" + }, + { + "label": "UseHotkeyOptionsFor", + "to": "framework/octane/reference/type-aliases/UseHotkeyOptionsFor" + }, + { + "label": "UseHotkeySequenceOptions", + "to": "framework/octane/reference/type-aliases/UseHotkeySequenceOptions" + }, + { + "label": "UseHotkeyTarget", + "to": "framework/octane/reference/type-aliases/UseHotkeyTarget" } ] } ] }, { + "label": "App Hotkeys API Reference", "collapsible": true, "defaultCollapsed": true, - "label": "Hotkey Sequence API Reference", "children": [ { - "label": "SequenceManager", - "to": "reference/classes/SequenceManager" + "label": "defineAppHotkeys", + "to": "reference/functions/defineAppHotkeys" }, { - "label": "getSequenceManager", - "to": "reference/functions/getSequenceManager" + "label": "AppHotkeysStore", + "to": "reference/classes/AppHotkeysStore" }, { - "label": "SequenceOptions", - "to": "reference/interfaces/SequenceOptions" + "label": "provideAppHotkeys", + "to": "reference/functions/provideAppHotkeys" }, { - "label": "createSequenceMatcher", - "to": "reference/functions/createSequenceMatcher" + "label": "publishAppHotkeysStore", + "to": "reference/functions/publishAppHotkeysStore" }, { - "label": "HotkeySequence", - "to": "reference/type-aliases/HotkeySequence" + "label": "AppHotkey", + "to": "reference/interfaces/AppHotkey" + }, + { + "label": "AppHotkeyInput", + "to": "reference/interfaces/AppHotkeyInput" + }, + { + "label": "AppHotkeyOptions", + "to": "reference/type-aliases/AppHotkeyOptions" + }, + { + "label": "AppHotkeyListItem", + "to": "reference/interfaces/AppHotkeyListItem" + }, + { + "label": "AppHotkeyStatus", + "to": "reference/type-aliases/AppHotkeyStatus" + }, + { + "label": "AppHotkeysState", + "to": "reference/interfaces/AppHotkeysState" + }, + { + "label": "AppHotkeysStoreOptions", + "to": "reference/interfaces/AppHotkeysStoreOptions" + }, + { + "label": "CustomHotkeys", + "to": "reference/type-aliases/CustomHotkeys" + }, + { + "label": "Register", + "to": "reference/interfaces/Register" + }, + { + "label": "AppHotkeyId", + "to": "reference/type-aliases/AppHotkeyId" + }, + { + "label": "RegisteredAppHotkeys", + "to": "reference/type-aliases/RegisteredAppHotkeys" }, { - "label": "SequenceRegistrationHandle", - "to": "reference/interfaces/SequenceRegistrationHandle" + "label": "isAppHotkey", + "to": "reference/functions/isAppHotkey" }, { - "label": "SequenceRegistrationView", - "to": "reference/interfaces/SequenceRegistrationView" + "label": "isCustomHotkey", + "to": "reference/functions/isCustomHotkey" }, { - "label": "HotkeySequenceDefinition", - "to": "reference/adapter/interfaces/HotkeySequenceDefinition" + "label": "resolveAppHotkey", + "to": "reference/functions/resolveAppHotkey" } ], "frameworks": [ @@ -1046,20 +1366,24 @@ "label": "react", "children": [ { - "label": "useHotkeySequence", - "to": "framework/react/reference/functions/useHotkeySequence" + "label": "useAppHotkeyList", + "to": "framework/react/reference/functions/useAppHotkeyList" }, { - "label": "UseHotkeySequenceOptions", - "to": "framework/react/reference/interfaces/UseHotkeySequenceOptions" + "label": "useAppHotkeys", + "to": "framework/react/reference/functions/useAppHotkeys" + }, + { + "label": "useCustomHotkeys", + "to": "framework/react/reference/functions/useCustomHotkeys" }, { - "label": "useHotkeySequences", - "to": "framework/react/reference/functions/useHotkeySequences" + "label": "UseCustomHotkeysResult", + "to": "framework/react/reference/interfaces/UseCustomHotkeysResult" }, { - "label": "UseHotkeySequenceDefinition", - "to": "framework/react/reference/interfaces/UseHotkeySequenceDefinition" + "label": "UseAppHotkeyOptions", + "to": "framework/react/reference/type-aliases/UseAppHotkeyOptions" } ] }, @@ -1067,20 +1391,24 @@ "label": "preact", "children": [ { - "label": "useHotkeySequence", - "to": "framework/preact/reference/functions/useHotkeySequence" + "label": "useAppHotkeyList", + "to": "framework/preact/reference/functions/useAppHotkeyList" }, { - "label": "UseHotkeySequenceOptions", - "to": "framework/preact/reference/interfaces/UseHotkeySequenceOptions" + "label": "useAppHotkeys", + "to": "framework/preact/reference/functions/useAppHotkeys" + }, + { + "label": "useCustomHotkeys", + "to": "framework/preact/reference/functions/useCustomHotkeys" }, { - "label": "useHotkeySequences", - "to": "framework/preact/reference/functions/useHotkeySequences" + "label": "UseCustomHotkeysResult", + "to": "framework/preact/reference/interfaces/UseCustomHotkeysResult" }, { - "label": "UseHotkeySequenceDefinition", - "to": "framework/preact/reference/interfaces/UseHotkeySequenceDefinition" + "label": "UseAppHotkeyOptions", + "to": "framework/preact/reference/type-aliases/UseAppHotkeyOptions" } ] }, @@ -1088,20 +1416,24 @@ "label": "solid", "children": [ { - "label": "createHotkeySequence", - "to": "framework/solid/reference/functions/createHotkeySequence" + "label": "createAppHotkeyList", + "to": "framework/solid/reference/functions/createAppHotkeyList" }, { - "label": "CreateHotkeySequenceOptions", - "to": "framework/solid/reference/interfaces/CreateHotkeySequenceOptions" + "label": "useAppHotkeys", + "to": "framework/solid/reference/functions/useAppHotkeys" }, { - "label": "createHotkeySequences", - "to": "framework/solid/reference/functions/createHotkeySequences" + "label": "useCustomHotkeys", + "to": "framework/solid/reference/functions/useCustomHotkeys" }, { - "label": "CreateHotkeySequenceDefinition", - "to": "framework/solid/reference/interfaces/CreateHotkeySequenceDefinition" + "label": "UseCustomHotkeysResult", + "to": "framework/solid/reference/interfaces/UseCustomHotkeysResult" + }, + { + "label": "CreateAppHotkeyOptions", + "to": "framework/solid/reference/type-aliases/CreateAppHotkeyOptions" } ] }, @@ -1109,20 +1441,24 @@ "label": "angular", "children": [ { - "label": "injectHotkeySequence", - "to": "framework/angular/reference/functions/injectHotkeySequence" + "label": "injectAppHotkeyList", + "to": "framework/angular/reference/functions/injectAppHotkeyList" }, { - "label": "InjectHotkeySequenceOptions", - "to": "framework/angular/reference/interfaces/InjectHotkeySequenceOptions" + "label": "injectAppHotkeys", + "to": "framework/angular/reference/functions/injectAppHotkeys" + }, + { + "label": "injectCustomHotkeys", + "to": "framework/angular/reference/functions/injectCustomHotkeys" }, { - "label": "injectHotkeySequences", - "to": "framework/angular/reference/functions/injectHotkeySequences" + "label": "InjectCustomHotkeysResult", + "to": "framework/angular/reference/interfaces/InjectCustomHotkeysResult" }, { - "label": "InjectHotkeySequenceDefinition", - "to": "framework/angular/reference/interfaces/InjectHotkeySequenceDefinition" + "label": "InjectAppHotkeyOptions", + "to": "framework/angular/reference/type-aliases/InjectAppHotkeyOptions" } ] }, @@ -1130,20 +1466,24 @@ "label": "vue", "children": [ { - "label": "useHotkeySequence", - "to": "framework/vue/reference/functions/useHotkeySequence" + "label": "useAppHotkeyList", + "to": "framework/vue/reference/functions/useAppHotkeyList" }, { - "label": "UseHotkeySequenceOptions", - "to": "framework/vue/reference/interfaces/UseHotkeySequenceOptions" + "label": "useAppHotkeys", + "to": "framework/vue/reference/functions/useAppHotkeys" + }, + { + "label": "useCustomHotkeys", + "to": "framework/vue/reference/functions/useCustomHotkeys" }, { - "label": "useHotkeySequences", - "to": "framework/vue/reference/functions/useHotkeySequences" + "label": "UseCustomHotkeysResult", + "to": "framework/vue/reference/interfaces/UseCustomHotkeysResult" }, { - "label": "UseHotkeySequenceDefinition", - "to": "framework/vue/reference/interfaces/UseHotkeySequenceDefinition" + "label": "UseAppHotkeyOptions", + "to": "framework/vue/reference/type-aliases/UseAppHotkeyOptions" } ] }, @@ -1151,12 +1491,12 @@ "label": "lit", "children": [ { - "label": "hotkeySequence", - "to": "framework/lit/reference/functions/hotkeySequence" + "label": "AppHotkeysController", + "to": "framework/lit/reference/classes/AppHotkeysController" }, { - "label": "HotkeySequenceController", - "to": "framework/lit/reference/classes/HotkeySequenceController" + "label": "AppHotkeyControllerOptions", + "to": "framework/lit/reference/type-aliases/AppHotkeyControllerOptions" } ] }, @@ -1164,28 +1504,32 @@ "label": "svelte", "children": [ { - "label": "createHotkeySequence", - "to": "framework/svelte/reference/functions/createHotkeySequence" + "label": "getAppHotkeyList", + "to": "framework/svelte/reference/functions/getAppHotkeyList" }, { - "label": "createHotkeySequenceAttachment", - "to": "framework/svelte/reference/functions/createHotkeySequenceAttachment" + "label": "getAppHotkeys", + "to": "framework/svelte/reference/functions/getAppHotkeys" }, { - "label": "CreateHotkeySequenceOptions", - "to": "framework/svelte/reference/interfaces/CreateHotkeySequenceOptions" + "label": "getCustomHotkeys", + "to": "framework/svelte/reference/functions/getCustomHotkeys" + }, + { + "label": "SvelteAppHotkeyList", + "to": "framework/svelte/reference/interfaces/SvelteAppHotkeyList" }, { - "label": "createHotkeySequences", - "to": "framework/svelte/reference/functions/createHotkeySequences" + "label": "SvelteAppHotkeys", + "to": "framework/svelte/reference/interfaces/SvelteAppHotkeys" }, { - "label": "createHotkeySequencesAttachment", - "to": "framework/svelte/reference/functions/createHotkeySequencesAttachment" + "label": "SvelteCustomHotkeys", + "to": "framework/svelte/reference/interfaces/SvelteCustomHotkeys" }, { - "label": "CreateHotkeySequenceDefinition", - "to": "framework/svelte/reference/interfaces/CreateHotkeySequenceDefinition" + "label": "CreateAppHotkeyOptions", + "to": "framework/svelte/reference/type-aliases/CreateAppHotkeyOptions" } ] }, @@ -1193,12 +1537,12 @@ "label": "alpine", "children": [ { - "label": "createHotkeySequence", - "to": "framework/alpine/reference/interfaces/AlpineHotkeys#createhotkeysequence" + "label": "AlpineCustomHotkeys", + "to": "framework/alpine/reference/interfaces/AlpineCustomHotkeys" }, { - "label": "createHotkeySequences", - "to": "framework/alpine/reference/interfaces/AlpineHotkeys#createhotkeysequences" + "label": "AlpineAppHotkeyOptions", + "to": "framework/alpine/reference/type-aliases/AlpineAppHotkeyOptions" } ] }, @@ -1206,12 +1550,24 @@ "label": "ember", "children": [ { - "label": "useHotkeySequence", - "to": "framework/ember/reference/classes/useHotkeySequence" + "label": "useAppHotkeyList", + "to": "framework/ember/reference/functions/useAppHotkeyList" + }, + { + "label": "useAppHotkeys", + "to": "framework/ember/reference/functions/useAppHotkeys" + }, + { + "label": "useCustomHotkeys", + "to": "framework/ember/reference/functions/useCustomHotkeys" + }, + { + "label": "EmberCustomHotkeys", + "to": "framework/ember/reference/interfaces/EmberCustomHotkeys" }, { - "label": "useHotkeySequences", - "to": "framework/ember/reference/classes/useHotkeySequences" + "label": "EmberAppHotkeyList", + "to": "framework/ember/reference/type-aliases/EmberAppHotkeyList" } ] }, @@ -1219,21 +1575,33 @@ "label": "octane", "children": [ { - "label": "useHotkeySequence", - "to": "framework/octane/reference/functions/useHotkeySequence" + "label": "useAppHotkeyList", + "to": "framework/octane/reference/functions/useAppHotkeyList" + }, + { + "label": "useAppHotkeys", + "to": "framework/octane/reference/functions/useAppHotkeys" + }, + { + "label": "useCustomHotkeys", + "to": "framework/octane/reference/functions/useCustomHotkeys" + }, + { + "label": "UseCustomHotkeysResult", + "to": "framework/octane/reference/interfaces/UseCustomHotkeysResult" }, { - "label": "useHotkeySequences", - "to": "framework/octane/reference/functions/useHotkeySequences" + "label": "UseAppHotkeyOptions", + "to": "framework/octane/reference/type-aliases/UseAppHotkeyOptions" } ] } ] }, { + "label": "Key hold & held keys API Reference", "collapsible": true, "defaultCollapsed": true, - "label": "Key hold & held keys API Reference", "children": [ { "label": "KeyStateTracker", @@ -1265,20 +1633,20 @@ "label": "react", "children": [ { - "label": "useKeyHold", - "to": "framework/react/reference/functions/useKeyHold" + "label": "useHeldKeyCodes", + "to": "framework/react/reference/functions/useHeldKeyCodes" }, { "label": "useHeldKeys", "to": "framework/react/reference/functions/useHeldKeys" }, - { - "label": "useHeldKeyCodes", - "to": "framework/react/reference/functions/useHeldKeyCodes" - }, { "label": "useHotkeyHint", "to": "framework/react/reference/functions/useHotkeyHint" + }, + { + "label": "useKeyHold", + "to": "framework/react/reference/functions/useKeyHold" } ] }, @@ -1286,41 +1654,41 @@ "label": "preact", "children": [ { - "label": "useKeyHold", - "to": "framework/preact/reference/functions/useKeyHold" + "label": "useHeldKeyCodes", + "to": "framework/preact/reference/functions/useHeldKeyCodes" }, { "label": "useHeldKeys", "to": "framework/preact/reference/functions/useHeldKeys" }, - { - "label": "useHeldKeyCodes", - "to": "framework/preact/reference/functions/useHeldKeyCodes" - }, { "label": "useHotkeyHint", "to": "framework/preact/reference/functions/useHotkeyHint" - } - ] - }, + }, + { + "label": "useKeyHold", + "to": "framework/preact/reference/functions/useKeyHold" + } + ] + }, { "label": "solid", "children": [ { - "label": "createKeyHold", - "to": "framework/solid/reference/functions/createKeyHold" + "label": "createHeldKeyCodes", + "to": "framework/solid/reference/functions/createHeldKeyCodes" }, { "label": "createHeldKeys", "to": "framework/solid/reference/functions/createHeldKeys" }, - { - "label": "createHeldKeyCodes", - "to": "framework/solid/reference/functions/createHeldKeyCodes" - }, { "label": "createHotkeyHint", "to": "framework/solid/reference/functions/createHotkeyHint" + }, + { + "label": "createKeyHold", + "to": "framework/solid/reference/functions/createKeyHold" } ] }, @@ -1328,20 +1696,20 @@ "label": "angular", "children": [ { - "label": "injectKeyHold", - "to": "framework/angular/reference/functions/injectKeyHold" + "label": "injectHeldKeyCodes", + "to": "framework/angular/reference/functions/injectHeldKeyCodes" }, { "label": "injectHeldKeys", "to": "framework/angular/reference/functions/injectHeldKeys" }, - { - "label": "injectHeldKeyCodes", - "to": "framework/angular/reference/functions/injectHeldKeyCodes" - }, { "label": "injectHotkeyHint", "to": "framework/angular/reference/functions/injectHotkeyHint" + }, + { + "label": "injectKeyHold", + "to": "framework/angular/reference/functions/injectKeyHold" } ] }, @@ -1349,20 +1717,20 @@ "label": "vue", "children": [ { - "label": "useKeyHold", - "to": "framework/vue/reference/functions/useKeyHold" + "label": "useHeldKeyCodes", + "to": "framework/vue/reference/functions/useHeldKeyCodes" }, { "label": "useHeldKeys", "to": "framework/vue/reference/functions/useHeldKeys" }, - { - "label": "useHeldKeyCodes", - "to": "framework/vue/reference/functions/useHeldKeyCodes" - }, { "label": "useHotkeyHint", "to": "framework/vue/reference/functions/useHotkeyHint" + }, + { + "label": "useKeyHold", + "to": "framework/vue/reference/functions/useKeyHold" } ] }, @@ -1370,20 +1738,20 @@ "label": "lit", "children": [ { - "label": "KeyHoldController", - "to": "framework/lit/reference/classes/KeyHoldController" + "label": "HeldKeyCodesController", + "to": "framework/lit/reference/classes/HeldKeyCodesController" }, { "label": "HeldKeysController", "to": "framework/lit/reference/classes/HeldKeysController" }, - { - "label": "HeldKeyCodesController", - "to": "framework/lit/reference/classes/HeldKeyCodesController" - }, { "label": "HotkeyHintController", "to": "framework/lit/reference/classes/HotkeyHintController" + }, + { + "label": "KeyHoldController", + "to": "framework/lit/reference/classes/KeyHoldController" } ] }, @@ -1391,57 +1759,36 @@ "label": "svelte", "children": [ { - "label": "getIsKeyHeld", - "to": "framework/svelte/reference/functions/getIsKeyHeld" - }, - { - "label": "SvelteHeldKeyState", - "to": "framework/svelte/reference/interfaces/SvelteHeldKeyState" + "label": "getHeldKeyCodesMap", + "to": "framework/svelte/reference/functions/getHeldKeyCodesMap" }, { "label": "getHeldKeys", "to": "framework/svelte/reference/functions/getHeldKeys" }, { - "label": "SvelteHeldKeys", - "to": "framework/svelte/reference/interfaces/SvelteHeldKeys" + "label": "getHotkeyHint", + "to": "framework/svelte/reference/functions/getHotkeyHint" }, { - "label": "getHeldKeyCodesMap", - "to": "framework/svelte/reference/functions/getHeldKeyCodesMap" + "label": "getIsKeyHeld", + "to": "framework/svelte/reference/functions/getIsKeyHeld" }, { "label": "SvelteHeldKeyCodesMap", "to": "framework/svelte/reference/interfaces/SvelteHeldKeyCodesMap" }, { - "label": "getHotkeyHint", - "to": "framework/svelte/reference/functions/getHotkeyHint" - } - ] - }, - { - "label": "alpine", - "children": [ - { - "label": "createKeyHold", - "to": "framework/alpine/reference/interfaces/AlpineHotkeys#createkeyhold" - }, - { - "label": "createHeldKeys", - "to": "framework/alpine/reference/interfaces/AlpineHotkeys#createheldkeys" - }, - { - "label": "createHeldKeyCodes", - "to": "framework/alpine/reference/interfaces/AlpineHotkeys#createheldkeycodes" + "label": "SvelteHeldKeys", + "to": "framework/svelte/reference/interfaces/SvelteHeldKeys" }, { - "label": "createHotkeyHint", - "to": "framework/alpine/reference/interfaces/AlpineHotkeys#createhotkeyhint" + "label": "SvelteHeldKeyState", + "to": "framework/svelte/reference/interfaces/SvelteHeldKeyState" }, { - "label": "AlpineHotkeyState", - "to": "framework/alpine/reference/interfaces/AlpineHotkeyState" + "label": "SvelteHotkeyHint", + "to": "framework/svelte/reference/interfaces/SvelteHotkeyHint" } ] }, @@ -1449,207 +1796,20 @@ "label": "ember", "children": [ { - "label": "useKeyHold", - "to": "framework/ember/reference/functions/useKeyHold" + "label": "useHeldKeyCodes", + "to": "framework/ember/reference/functions/useHeldKeyCodes" }, { "label": "useHeldKeys", "to": "framework/ember/reference/functions/useHeldKeys" }, - { - "label": "useHeldKeyCodes", - "to": "framework/ember/reference/functions/useHeldKeyCodes" - }, { "label": "useHotkeyHint", "to": "framework/ember/reference/functions/useHotkeyHint" }, - { - "label": "EmberHotkeyState", - "to": "framework/ember/reference/interfaces/EmberHotkeyState" - } - ] - }, - { - "label": "octane", - "children": [ { "label": "useKeyHold", - "to": "framework/octane/reference/functions/useKeyHold" - }, - { - "label": "useHeldKeys", - "to": "framework/octane/reference/functions/useHeldKeys" - }, - { - "label": "useHeldKeyCodes", - "to": "framework/octane/reference/functions/useHeldKeyCodes" - }, - { - "label": "useHotkeyHint", - "to": "framework/octane/reference/functions/useHotkeyHint" - } - ] - } - ] - }, - { - "collapsible": true, - "defaultCollapsed": true, - "label": "Hotkey Recorder API Reference", - "children": [ - { - "label": "HotkeyRecorder", - "to": "reference/classes/HotkeyRecorder" - }, - { - "label": "HotkeyRecorderOptions", - "to": "reference/interfaces/HotkeyRecorderOptions" - }, - { - "label": "HotkeyRecorderState", - "to": "reference/interfaces/HotkeyRecorderState" - }, - { - "label": "findHotkeyConflicts", - "to": "reference/functions/findHotkeyConflicts" - }, - { - "label": "HotkeyConflictOptions", - "to": "reference/interfaces/HotkeyConflictOptions" - }, - { - "label": "HotkeyConflict", - "to": "reference/type-aliases/HotkeyConflict" - }, - { - "label": "RecorderRejection", - "to": "reference/interfaces/RecorderRejection" - }, - { - "label": "RecorderKeyMode", - "to": "reference/type-aliases/RecorderKeyMode" - }, - { - "label": "RecorderOptions", - "to": "reference/interfaces/RecorderOptions" - }, - { - "label": "HotkeyRecorderValidationContext", - "to": "reference/interfaces/HotkeyRecorderValidationContext" - } - ], - "frameworks": [ - { - "label": "react", - "children": [ - { - "label": "useHotkeyRecorder", - "to": "framework/react/reference/functions/useHotkeyRecorder" - }, - { - "label": "ReactHotkeyRecorder", - "to": "framework/react/reference/interfaces/ReactHotkeyRecorder" - } - ] - }, - { - "label": "preact", - "children": [ - { - "label": "useHotkeyRecorder", - "to": "framework/preact/reference/functions/useHotkeyRecorder" - }, - { - "label": "PreactHotkeyRecorder", - "to": "framework/preact/reference/interfaces/PreactHotkeyRecorder" - } - ] - }, - { - "label": "solid", - "children": [ - { - "label": "createHotkeyRecorder", - "to": "framework/solid/reference/functions/createHotkeyRecorder" - }, - { - "label": "SolidHotkeyRecorder", - "to": "framework/solid/reference/interfaces/SolidHotkeyRecorder" - } - ] - }, - { - "label": "angular", - "children": [ - { - "label": "injectHotkeyRecorder", - "to": "framework/angular/reference/functions/injectHotkeyRecorder" - }, - { - "label": "AngularHotkeyRecorder", - "to": "framework/angular/reference/interfaces/AngularHotkeyRecorder" - } - ] - }, - { - "label": "vue", - "children": [ - { - "label": "useHotkeyRecorder", - "to": "framework/vue/reference/functions/useHotkeyRecorder" - }, - { - "label": "VueHotkeyRecorder", - "to": "framework/vue/reference/interfaces/VueHotkeyRecorder" - } - ] - }, - { - "label": "lit", - "children": [ - { - "label": "HotkeyRecorderController", - "to": "framework/lit/reference/classes/HotkeyRecorderController" - } - ] - }, - { - "label": "svelte", - "children": [ - { - "label": "createHotkeyRecorder", - "to": "framework/svelte/reference/functions/createHotkeyRecorder" - }, - { - "label": "SvelteHotkeyRecorder", - "to": "framework/svelte/reference/interfaces/SvelteHotkeyRecorder" - } - ] - }, - { - "label": "alpine", - "children": [ - { - "label": "createHotkeyRecorder", - "to": "framework/alpine/reference/interfaces/AlpineHotkeys#createhotkeyrecorder" - }, - { - "label": "AlpineHotkeyRecorder", - "to": "framework/alpine/reference/interfaces/AlpineHotkeyRecorder" - } - ] - }, - { - "label": "ember", - "children": [ - { - "label": "useHotkeyRecorder", - "to": "framework/ember/reference/functions/useHotkeyRecorder" - }, - { - "label": "EmberHotkeyRecorder", - "to": "framework/ember/reference/interfaces/EmberHotkeyRecorder" + "to": "framework/ember/reference/functions/useKeyHold" } ] }, @@ -1657,41 +1817,81 @@ "label": "octane", "children": [ { - "label": "useHotkeyRecorder", - "to": "framework/octane/reference/functions/useHotkeyRecorder" + "label": "useHeldKeyCodes", + "to": "framework/octane/reference/functions/useHeldKeyCodes" + }, + { + "label": "useHeldKeys", + "to": "framework/octane/reference/functions/useHeldKeys" }, { - "label": "OctaneHotkeyRecorder", - "to": "framework/octane/reference/interfaces/OctaneHotkeyRecorder" + "label": "useHotkeyHint", + "to": "framework/octane/reference/functions/useHotkeyHint" + }, + { + "label": "useKeyHold", + "to": "framework/octane/reference/functions/useKeyHold" } ] } ] }, { + "label": "Hotkey Recorder API Reference", "collapsible": true, "defaultCollapsed": true, - "label": "Hotkey Sequence Recorder API Reference", "children": [ { - "label": "HotkeySequenceRecorder", - "to": "reference/classes/HotkeySequenceRecorder" + "label": "HotkeyRecorder", + "to": "reference/classes/HotkeyRecorder" + }, + { + "label": "HotkeyRecorderOptions", + "to": "reference/type-aliases/HotkeyRecorderOptions" + }, + { + "label": "HotkeyChordRecorderOptions", + "to": "reference/interfaces/HotkeyChordRecorderOptions" }, { "label": "HotkeySequenceRecorderOptions", "to": "reference/interfaces/HotkeySequenceRecorderOptions" }, { - "label": "HotkeySequenceRecorderState", - "to": "reference/interfaces/HotkeySequenceRecorderState" + "label": "HotkeyRecorderState", + "to": "reference/interfaces/HotkeyRecorderState" + }, + { + "label": "HotkeyRecorderCommitKeys", + "to": "reference/type-aliases/HotkeyRecorderCommitKeys" + }, + { + "label": "HotkeyRecorderValidationContext", + "to": "reference/interfaces/HotkeyRecorderValidationContext" + }, + { + "label": "RecorderOptions", + "to": "reference/interfaces/RecorderOptions" + }, + { + "label": "RecorderRejection", + "to": "reference/interfaces/RecorderRejection" + }, + { + "label": "RecorderKeyMode", + "to": "reference/type-aliases/RecorderKeyMode" + }, + { + "label": "findHotkeyConflicts", + "to": "reference/functions/findHotkeyConflicts" }, { - "label": "HotkeySequenceRecorderCommitKeys", - "to": "reference/type-aliases/HotkeySequenceRecorderCommitKeys" + "label": "HotkeyConflict", + "to": "reference/interfaces/HotkeyConflict" }, { - "label": "HotkeySequenceRecorderValidationContext", - "to": "reference/interfaces/HotkeySequenceRecorderValidationContext" + "label": "HotkeyConflictOptions", + "to": "reference/interfaces/HotkeyConflictOptions" } ], "frameworks": [ @@ -1699,12 +1899,12 @@ "label": "react", "children": [ { - "label": "useHotkeySequenceRecorder", - "to": "framework/react/reference/functions/useHotkeySequenceRecorder" + "label": "useHotkeyRecorder", + "to": "framework/react/reference/functions/useHotkeyRecorder" }, { - "label": "ReactHotkeySequenceRecorder", - "to": "framework/react/reference/interfaces/ReactHotkeySequenceRecorder" + "label": "ReactHotkeyRecorder", + "to": "framework/react/reference/interfaces/ReactHotkeyRecorder" } ] }, @@ -1712,12 +1912,12 @@ "label": "preact", "children": [ { - "label": "useHotkeySequenceRecorder", - "to": "framework/preact/reference/functions/useHotkeySequenceRecorder" + "label": "useHotkeyRecorder", + "to": "framework/preact/reference/functions/useHotkeyRecorder" }, { - "label": "PreactHotkeySequenceRecorder", - "to": "framework/preact/reference/interfaces/PreactHotkeySequenceRecorder" + "label": "PreactHotkeyRecorder", + "to": "framework/preact/reference/interfaces/PreactHotkeyRecorder" } ] }, @@ -1725,12 +1925,12 @@ "label": "solid", "children": [ { - "label": "createHotkeySequenceRecorder", - "to": "framework/solid/reference/functions/createHotkeySequenceRecorder" + "label": "createHotkeyRecorder", + "to": "framework/solid/reference/functions/createHotkeyRecorder" }, { - "label": "SolidHotkeySequenceRecorder", - "to": "framework/solid/reference/interfaces/SolidHotkeySequenceRecorder" + "label": "SolidHotkeyRecorder", + "to": "framework/solid/reference/interfaces/SolidHotkeyRecorder" } ] }, @@ -1738,12 +1938,12 @@ "label": "angular", "children": [ { - "label": "injectHotkeySequenceRecorder", - "to": "framework/angular/reference/functions/injectHotkeySequenceRecorder" + "label": "injectHotkeyRecorder", + "to": "framework/angular/reference/functions/injectHotkeyRecorder" }, { - "label": "AngularHotkeySequenceRecorder", - "to": "framework/angular/reference/interfaces/AngularHotkeySequenceRecorder" + "label": "AngularHotkeyRecorder", + "to": "framework/angular/reference/interfaces/AngularHotkeyRecorder" } ] }, @@ -1751,12 +1951,12 @@ "label": "vue", "children": [ { - "label": "useHotkeySequenceRecorder", - "to": "framework/vue/reference/functions/useHotkeySequenceRecorder" + "label": "useHotkeyRecorder", + "to": "framework/vue/reference/functions/useHotkeyRecorder" }, { - "label": "VueHotkeySequenceRecorder", - "to": "framework/vue/reference/interfaces/VueHotkeySequenceRecorder" + "label": "VueHotkeyRecorder", + "to": "framework/vue/reference/interfaces/VueHotkeyRecorder" } ] }, @@ -1764,8 +1964,8 @@ "label": "lit", "children": [ { - "label": "HotkeySequenceRecorderController", - "to": "framework/lit/reference/classes/HotkeySequenceRecorderController" + "label": "HotkeyRecorderController", + "to": "framework/lit/reference/classes/HotkeyRecorderController" } ] }, @@ -1773,12 +1973,12 @@ "label": "svelte", "children": [ { - "label": "createHotkeySequenceRecorder", - "to": "framework/svelte/reference/functions/createHotkeySequenceRecorder" + "label": "createHotkeyRecorder", + "to": "framework/svelte/reference/functions/createHotkeyRecorder" }, { - "label": "SvelteHotkeySequenceRecorder", - "to": "framework/svelte/reference/interfaces/SvelteHotkeySequenceRecorder" + "label": "SvelteHotkeyRecorder", + "to": "framework/svelte/reference/interfaces/SvelteHotkeyRecorder" } ] }, @@ -1786,12 +1986,12 @@ "label": "alpine", "children": [ { - "label": "createHotkeySequenceRecorder", - "to": "framework/alpine/reference/interfaces/AlpineHotkeys#createhotkeysequencerecorder" + "label": "AlpineCreateHotkeyRecorder", + "to": "framework/alpine/reference/interfaces/AlpineCreateHotkeyRecorder" }, { - "label": "AlpineHotkeySequenceRecorder", - "to": "framework/alpine/reference/interfaces/AlpineHotkeySequenceRecorder" + "label": "AlpineHotkeyRecorder", + "to": "framework/alpine/reference/interfaces/AlpineHotkeyRecorder" } ] }, @@ -1799,12 +1999,12 @@ "label": "ember", "children": [ { - "label": "useHotkeySequenceRecorder", - "to": "framework/ember/reference/functions/useHotkeySequenceRecorder" + "label": "useHotkeyRecorder", + "to": "framework/ember/reference/functions/useHotkeyRecorder" }, { - "label": "EmberHotkeySequenceRecorder", - "to": "framework/ember/reference/interfaces/EmberHotkeySequenceRecorder" + "label": "EmberHotkeyRecorder", + "to": "framework/ember/reference/interfaces/EmberHotkeyRecorder" } ] }, @@ -1812,165 +2012,153 @@ "label": "octane", "children": [ { - "label": "useHotkeySequenceRecorder", - "to": "framework/octane/reference/functions/useHotkeySequenceRecorder" + "label": "useHotkeyRecorder", + "to": "framework/octane/reference/functions/useHotkeyRecorder" }, { - "label": "OctaneHotkeySequenceRecorder", - "to": "framework/octane/reference/interfaces/OctaneHotkeySequenceRecorder" + "label": "OctaneHotkeyRecorder", + "to": "framework/octane/reference/interfaces/OctaneHotkeyRecorder" } ] } ] }, { + "label": "Normalization & format API Reference", "collapsible": true, "defaultCollapsed": true, - "label": "Normalization & format API Reference", "children": [ { - "label": "normalizeHotkey", - "to": "reference/functions/normalizeHotkey" - }, - { - "label": "normalizeHotkeyFromParsed", - "to": "reference/functions/normalizeHotkeyFromParsed" + "label": "formatForDisplay", + "to": "reference/functions/formatForDisplay" }, { - "label": "normalizeHotkeyFromEvent", - "to": "reference/functions/normalizeHotkeyFromEvent" + "label": "FormatDisplayOptions", + "to": "reference/interfaces/FormatDisplayOptions" }, { - "label": "normalizeRegisterableHotkey", - "to": "reference/functions/normalizeRegisterableHotkey" + "label": "DisplayHotkey", + "to": "reference/type-aliases/DisplayHotkey" }, { - "label": "formatForDisplay", - "to": "reference/functions/formatForDisplay" + "label": "DisplayHotkeyChord", + "to": "reference/type-aliases/DisplayHotkeyChord" }, { "label": "formatHotkey", "to": "reference/functions/formatHotkey" }, { - "label": "formatWithLabels", - "to": "reference/functions/formatWithLabels" - }, - { - "label": "FormatDisplayOptions", - "to": "reference/interfaces/FormatDisplayOptions" - }, - { - "label": "KEY_DISPLAY_SYMBOLS", - "to": "reference/variables/KEY_DISPLAY_SYMBOLS" + "label": "normalizeHotkey", + "to": "reference/functions/normalizeHotkey" }, { - "label": "PUNCTUATION_KEY_DISPLAY_LABELS", - "to": "reference/variables/PUNCTUATION_KEY_DISPLAY_LABELS" + "label": "normalizeHotkeyFromParsed", + "to": "reference/functions/normalizeHotkeyFromParsed" }, { - "label": "MAC_MODIFIER_SYMBOLS", - "to": "reference/variables/MAC_MODIFIER_SYMBOLS" + "label": "normalizeHotkeyFromEvent", + "to": "reference/functions/normalizeHotkeyFromEvent" }, { - "label": "MAC_MODIFIER_LABELS", - "to": "reference/variables/MAC_MODIFIER_LABELS" + "label": "normalizeRegisterableHotkey", + "to": "reference/functions/normalizeRegisterableHotkey" }, { - "label": "WINDOWS_MODIFIER_LABELS", - "to": "reference/variables/WINDOWS_MODIFIER_LABELS" + "label": "parseHotkey", + "to": "reference/functions/parseHotkey" }, { - "label": "LINUX_MODIFIER_LABELS", - "to": "reference/variables/LINUX_MODIFIER_LABELS" + "label": "parseRegisterableHotkey", + "to": "reference/functions/parseRegisterableHotkey" }, { - "label": "MODIFIER_ALIASES", - "to": "reference/variables/MODIFIER_ALIASES" + "label": "parseKeyboardEvent", + "to": "reference/functions/parseKeyboardEvent" }, { - "label": "MODIFIER_ORDER", - "to": "reference/variables/MODIFIER_ORDER" + "label": "rawHotkeyToParsedHotkey", + "to": "reference/functions/rawHotkeyToParsedHotkey" }, { - "label": "MODIFIER_KEYS", - "to": "reference/variables/MODIFIER_KEYS" + "label": "ParsedHotkey", + "to": "reference/type-aliases/ParsedHotkey" }, { - "label": "parseRegisterableHotkey", - "to": "reference/functions/parseRegisterableHotkey" + "label": "matchesKeyboardEvent", + "to": "reference/functions/matchesKeyboardEvent" }, { "label": "areHotkeysEqual", "to": "reference/functions/areHotkeysEqual" }, { - "label": "PhysicalKey", - "to": "reference/type-aliases/PhysicalKey" + "label": "validateHotkey", + "to": "reference/functions/validateHotkey" }, { - "label": "Hotkey", - "to": "reference/type-aliases/Hotkey" + "label": "checkHotkey", + "to": "reference/functions/checkHotkey" }, { - "label": "RegisterableHotkey", - "to": "reference/type-aliases/RegisterableHotkey" + "label": "assertValidHotkey", + "to": "reference/functions/assertValidHotkey" }, { - "label": "RawHotkey", - "to": "reference/type-aliases/RawHotkey" + "label": "ValidationResult", + "to": "reference/interfaces/ValidationResult" }, { - "label": "ParsedHotkey", - "to": "reference/type-aliases/ParsedHotkey" + "label": "ValidatableHotkey", + "to": "reference/type-aliases/ValidatableHotkey" }, { "label": "LogicalKey", "to": "reference/type-aliases/LogicalKey" }, { - "label": "PhysicalKeyCode", - "to": "reference/type-aliases/PhysicalKeyCode" + "label": "PhysicalKey", + "to": "reference/type-aliases/PhysicalKey" }, { - "label": "DisplayHotkey", - "to": "reference/type-aliases/DisplayHotkey" + "label": "PhysicalKeyCode", + "to": "reference/type-aliases/PhysicalKeyCode" }, { - "label": "formatHotkeySequence", - "to": "reference/functions/formatHotkeySequence" + "label": "KEY_DISPLAY_SYMBOLS", + "to": "reference/variables/KEY_DISPLAY_SYMBOLS" }, { - "label": "parseHotkey", - "to": "reference/functions/parseHotkey" + "label": "PUNCTUATION_KEY_DISPLAY_LABELS", + "to": "reference/variables/PUNCTUATION_KEY_DISPLAY_LABELS" }, { - "label": "rawHotkeyToParsedHotkey", - "to": "reference/functions/rawHotkeyToParsedHotkey" + "label": "MAC_MODIFIER_SYMBOLS", + "to": "reference/variables/MAC_MODIFIER_SYMBOLS" }, { - "label": "parseKeyboardEvent", - "to": "reference/functions/parseKeyboardEvent" + "label": "MAC_MODIFIER_LABELS", + "to": "reference/variables/MAC_MODIFIER_LABELS" }, { - "label": "matchesKeyboardEvent", - "to": "reference/functions/matchesKeyboardEvent" + "label": "WINDOWS_MODIFIER_LABELS", + "to": "reference/variables/WINDOWS_MODIFIER_LABELS" }, { - "label": "validateHotkey", - "to": "reference/functions/validateHotkey" + "label": "LINUX_MODIFIER_LABELS", + "to": "reference/variables/LINUX_MODIFIER_LABELS" }, { - "label": "checkHotkey", - "to": "reference/functions/checkHotkey" + "label": "MODIFIER_ALIASES", + "to": "reference/variables/MODIFIER_ALIASES" }, { - "label": "assertValidHotkey", - "to": "reference/functions/assertValidHotkey" + "label": "MODIFIER_ORDER", + "to": "reference/variables/MODIFIER_ORDER" }, { - "label": "ValidationResult", - "to": "reference/interfaces/ValidationResult" + "label": "MODIFIER_KEYS", + "to": "reference/variables/MODIFIER_KEYS" } ] }, @@ -1999,31 +2187,31 @@ "to": "framework/react/examples/useHotkey" }, { - "label": "useHotkeys", - "to": "framework/react/examples/useHotkeys" + "label": "useHotkey (sequences)", + "to": "framework/react/examples/useHotkey-sequences" }, { - "label": "useHotkeySequence", - "to": "framework/react/examples/useHotkeySequence" + "label": "useHotkeys", + "to": "framework/react/examples/useHotkeys" }, { - "label": "useHotkeySequences", - "to": "framework/react/examples/useHotkeySequences" + "label": "useHotkeys (sequences)", + "to": "framework/react/examples/useHotkeys-sequences" }, { "label": "useHotkeyRecorder", "to": "framework/react/examples/useHotkeyRecorder" }, { - "label": "useHotkeySequenceRecorder", - "to": "framework/react/examples/useHotkeySequenceRecorder" + "label": "useHotkeyRecorder (sequences)", + "to": "framework/react/examples/useHotkeyRecorder-sequences" }, { "label": "useHeldKeys", "to": "framework/react/examples/useHeldKeys" }, { - "label": "useKeyHold", + "label": "useKeyhold", "to": "framework/react/examples/useKeyhold" } ] @@ -2040,31 +2228,31 @@ "to": "framework/preact/examples/useHotkey" }, { - "label": "useHotkeys", - "to": "framework/preact/examples/useHotkeys" + "label": "useHotkey (sequences)", + "to": "framework/preact/examples/useHotkey-sequences" }, { - "label": "useHotkeySequence", - "to": "framework/preact/examples/useHotkeySequence" + "label": "useHotkeys", + "to": "framework/preact/examples/useHotkeys" }, { - "label": "useHotkeySequences", - "to": "framework/preact/examples/useHotkeySequences" + "label": "useHotkeys (sequences)", + "to": "framework/preact/examples/useHotkeys-sequences" }, { "label": "useHotkeyRecorder", "to": "framework/preact/examples/useHotkeyRecorder" }, { - "label": "useHotkeySequenceRecorder", - "to": "framework/preact/examples/useHotkeySequenceRecorder" + "label": "useHotkeyRecorder (sequences)", + "to": "framework/preact/examples/useHotkeyRecorder-sequences" }, { "label": "useHeldKeys", "to": "framework/preact/examples/useHeldKeys" }, { - "label": "useKeyHold", + "label": "useKeyhold", "to": "framework/preact/examples/useKeyhold" } ] @@ -2081,24 +2269,24 @@ "to": "framework/solid/examples/createHotkey" }, { - "label": "createHotkeys", - "to": "framework/solid/examples/createHotkeys" + "label": "createHotkey (sequences)", + "to": "framework/solid/examples/createHotkey-sequences" }, { - "label": "createHotkeySequence", - "to": "framework/solid/examples/createHotkeySequence" + "label": "createHotkeys", + "to": "framework/solid/examples/createHotkeys" }, { - "label": "createHotkeySequences", - "to": "framework/solid/examples/createHotkeySequences" + "label": "createHotkeys (sequences)", + "to": "framework/solid/examples/createHotkeys-sequences" }, { "label": "createHotkeyRecorder", "to": "framework/solid/examples/createHotkeyRecorder" }, { - "label": "createHotkeySequenceRecorder", - "to": "framework/solid/examples/createHotkeySequenceRecorder" + "label": "createHotkeyRecorder (sequences)", + "to": "framework/solid/examples/createHotkeyRecorder-sequences" }, { "label": "createHeldKeys", @@ -2122,24 +2310,24 @@ "to": "framework/angular/examples/injectHotkey" }, { - "label": "injectHotkeys", - "to": "framework/angular/examples/injectHotkeys" + "label": "injectHotkey (sequences)", + "to": "framework/angular/examples/injectHotkey-sequences" }, { - "label": "injectHotkeySequence", - "to": "framework/angular/examples/injectHotkeySequence" + "label": "injectHotkeys", + "to": "framework/angular/examples/injectHotkeys" }, { - "label": "injectHotkeySequences", - "to": "framework/angular/examples/injectHotkeySequences" + "label": "injectHotkeys (sequences)", + "to": "framework/angular/examples/injectHotkeys-sequences" }, { "label": "injectHotkeyRecorder", "to": "framework/angular/examples/injectHotkeyRecorder" }, { - "label": "injectHotkeySequenceRecorder", - "to": "framework/angular/examples/injectHotkeySequenceRecorder" + "label": "injectHotkeyRecorder (sequences)", + "to": "framework/angular/examples/injectHotkeyRecorder-sequences" }, { "label": "injectHeldKeys", @@ -2163,31 +2351,31 @@ "to": "framework/vue/examples/useHotkey" }, { - "label": "useHotkeys", - "to": "framework/vue/examples/useHotkeys" + "label": "useHotkey (sequences)", + "to": "framework/vue/examples/useHotkey-sequences" }, { - "label": "useHotkeySequence", - "to": "framework/vue/examples/useHotkeySequence" + "label": "useHotkeys", + "to": "framework/vue/examples/useHotkeys" }, { - "label": "useHotkeySequences", - "to": "framework/vue/examples/useHotkeySequences" + "label": "useHotkeys (sequences)", + "to": "framework/vue/examples/useHotkeys-sequences" }, { "label": "useHotkeyRecorder", "to": "framework/vue/examples/useHotkeyRecorder" }, { - "label": "useHotkeySequenceRecorder", - "to": "framework/vue/examples/useHotkeySequenceRecorder" + "label": "useHotkeyRecorder (sequences)", + "to": "framework/vue/examples/useHotkeyRecorder-sequences" }, { "label": "useHeldKeys", "to": "framework/vue/examples/useHeldKeys" }, { - "label": "useKeyHold", + "label": "useKeyhold", "to": "framework/vue/examples/useKeyhold" } ] @@ -2204,16 +2392,16 @@ "to": "framework/lit/examples/hotkey" }, { - "label": "hotkey-sequence", - "to": "framework/lit/examples/hotkey-sequence" + "label": "hotkey (sequences)", + "to": "framework/lit/examples/hotkey-sequences" }, { "label": "hotkey-recorder", "to": "framework/lit/examples/hotkey-recorder" }, { - "label": "hotkey-sequence-recorder", - "to": "framework/lit/examples/hotkey-sequence-recorder" + "label": "hotkey-recorder (sequences)", + "to": "framework/lit/examples/hotkey-recorder-sequences" }, { "label": "held-keys", @@ -2233,35 +2421,35 @@ "to": "framework/svelte/examples/kitchen-sink" }, { - "label": "createHotkey", + "label": "create-hotkey", "to": "framework/svelte/examples/create-hotkey" }, { - "label": "createHotkeys", - "to": "framework/svelte/examples/create-hotkeys" + "label": "create-hotkey (sequences)", + "to": "framework/svelte/examples/create-hotkey-sequences" }, { - "label": "createHotkeySequence", - "to": "framework/svelte/examples/create-hotkey-sequence" + "label": "create-hotkeys", + "to": "framework/svelte/examples/create-hotkeys" }, { - "label": "createHotkeySequences", - "to": "framework/svelte/examples/create-hotkey-sequences" + "label": "create-hotkeys (sequences)", + "to": "framework/svelte/examples/create-hotkeys-sequences" }, { - "label": "createHotkeyRecorder", + "label": "create-hotkey-recorder", "to": "framework/svelte/examples/create-hotkey-recorder" }, { - "label": "create-hotkey-sequence-recorder", - "to": "framework/svelte/examples/create-hotkey-sequence-recorder" + "label": "create-hotkey-recorder (sequences)", + "to": "framework/svelte/examples/create-hotkey-recorder-sequences" }, { - "label": "getHeldKeys", + "label": "get-held-keys", "to": "framework/svelte/examples/get-held-keys" }, { - "label": "getIsKeyHeld", + "label": "get-is-key-held", "to": "framework/svelte/examples/get-is-key-held" } ] @@ -2278,24 +2466,24 @@ "to": "framework/alpine/examples/createHotkey" }, { - "label": "createHotkeys", - "to": "framework/alpine/examples/createHotkeys" + "label": "createHotkey (sequences)", + "to": "framework/alpine/examples/createHotkey-sequences" }, { - "label": "createHotkeySequence", - "to": "framework/alpine/examples/createHotkeySequence" + "label": "createHotkeys", + "to": "framework/alpine/examples/createHotkeys" }, { - "label": "createHotkeySequences", - "to": "framework/alpine/examples/createHotkeySequences" + "label": "createHotkeys (sequences)", + "to": "framework/alpine/examples/createHotkeys-sequences" }, { "label": "createHotkeyRecorder", "to": "framework/alpine/examples/createHotkeyRecorder" }, { - "label": "createHotkeySequenceRecorder", - "to": "framework/alpine/examples/createHotkeySequenceRecorder" + "label": "createHotkeyRecorder (sequences)", + "to": "framework/alpine/examples/createHotkeyRecorder-sequences" }, { "label": "createHeldKeys", @@ -2319,24 +2507,24 @@ "to": "framework/ember/examples/useHotkey" }, { - "label": "useHotkeys", - "to": "framework/ember/examples/useHotkeys" + "label": "useHotkey (sequences)", + "to": "framework/ember/examples/useHotkey-sequences" }, { - "label": "useHotkeySequence", - "to": "framework/ember/examples/useHotkeySequence" + "label": "useHotkeys", + "to": "framework/ember/examples/useHotkeys" }, { - "label": "useHotkeySequences", - "to": "framework/ember/examples/useHotkeySequences" + "label": "useHotkeys (sequences)", + "to": "framework/ember/examples/useHotkeys-sequences" }, { "label": "useHotkeyRecorder", "to": "framework/ember/examples/useHotkeyRecorder" }, { - "label": "useHotkeySequenceRecorder", - "to": "framework/ember/examples/useHotkeySequenceRecorder" + "label": "useHotkeyRecorder (sequences)", + "to": "framework/ember/examples/useHotkeyRecorder-sequences" }, { "label": "useHeldKeys", @@ -2360,24 +2548,24 @@ "to": "framework/octane/examples/useHotkey" }, { - "label": "useHotkeys", - "to": "framework/octane/examples/useHotkeys" + "label": "useHotkey (sequences)", + "to": "framework/octane/examples/useHotkey-sequences" }, { - "label": "useHotkeySequence", - "to": "framework/octane/examples/useHotkeySequence" + "label": "useHotkeys", + "to": "framework/octane/examples/useHotkeys" }, { - "label": "useHotkeySequences", - "to": "framework/octane/examples/useHotkeySequences" + "label": "useHotkeys (sequences)", + "to": "framework/octane/examples/useHotkeys-sequences" }, { "label": "useHotkeyRecorder", "to": "framework/octane/examples/useHotkeyRecorder" }, { - "label": "useHotkeySequenceRecorder", - "to": "framework/octane/examples/useHotkeySequenceRecorder" + "label": "useHotkeyRecorder (sequences)", + "to": "framework/octane/examples/useHotkeyRecorder-sequences" }, { "label": "useHeldKeys", diff --git a/docs/devtools.md b/docs/devtools.md index 4ccd0dc0..b7f5f316 100644 --- a/docs/devtools.md +++ b/docs/devtools.md @@ -12,7 +12,9 @@ TanStack Hotkeys ships devtools for debugging and monitoring your registered hot The Hotkeys devtools panel lets you: -- View all currently registered hotkeys with their options and status +- View all currently registered hotkeys, chords and sequences in one list, with their options and status +- Follow sequence progress as steps are pressed +- See declared [app hotkeys](./overview#app-hotkeys) that nothing has registered yet, with their custom hotkeys - See which keys are held down in real time - Trigger hotkey callbacks for testing, without pressing the keys - Inspect individual registrations, including their target, event type, and conflict behavior diff --git a/docs/framework/alpine/guides/app-hotkeys.md b/docs/framework/alpine/guides/app-hotkeys.md new file mode 100644 index 00000000..1f19e83b --- /dev/null +++ b/docs/framework/alpine/guides/app-hotkeys.md @@ -0,0 +1,211 @@ +--- +title: App Hotkeys Guide +id: app-hotkeys +--- + +`scope.createHotkey` and `scope.createHotkeys` register a hotkey while its component is mounted. A cheat sheet built from live registrations therefore misses hotkeys on routes that have not rendered yet, and a settings screen has nowhere to change them. App hotkeys solve both: you declare every hotkey in one place, publish them with `provideAppHotkeys`, and attach callbacks where the work happens. Users can then customize any of them, and your app decides how to save their choices. + +A declaration holds a hotkey's id, default hotkey, metadata, and options. It never holds a callback; `scope.createHotkey` and `scope.createHotkeys` still attach the callbacks. + +## Declare app hotkeys + +Use `defineAppHotkeys` in a module that contains only declarations. Keep it free of components and handlers, because it loads with your app shell: + +```ts +// app-hotkeys.ts +import { defineAppHotkeys } from '@tanstack/alpine-hotkeys' + +export const appHotkeys = defineAppHotkeys({ + 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save', group: 'Document' } }, + 'doc.find': { + hotkey: 'Mod+F', + meta: { name: 'Find', group: 'Document' }, + context: 'editor', + }, + 'nav.home': { + hotkey: ['G', 'H'], + meta: { name: 'Go home', group: 'Navigation' }, + options: { timeout: 800 }, + }, + 'palette.open': { hotkey: null, meta: { name: 'Command palette' } }, +}) + +declare module '@tanstack/alpine-hotkeys' { + interface Register { + appHotkeys: typeof appHotkeys + } +} +``` + +- The object key is the app hotkey's id. Treat ids as a stable contract, because saved custom hotkeys use them as keys. +- `hotkey` is the default: a chord, a sequence, or `null` for an action without a default. An unbound app hotkey registers nothing until the user assigns one. +- `options` describe the hotkey itself: `conflictBehavior`, `eventType`, `ignoreInputs`, `preventDefault`, `stopPropagation`, plus `requireReset` for a chord default or `timeout` for a sequence default. Options that don't match the default's kind are type errors. Pass `target` and `enabled` where you register the hotkey. +- `context` labels where a hotkey applies, such as `'editor'`. App hotkeys with different contexts may share a hotkey. It is used only for conflict checks and does not affect matching. + +The `Register` augmentation types the app hotkeys you read back, the ids accepted when changing custom hotkeys, and the `CustomHotkeys` type. + +## Provide app hotkeys + +```ts +// main.ts, before Alpine.start() +import { provideAppHotkeys } from '@tanstack/alpine-hotkeys' +import { appHotkeys } from './app-hotkeys' + +provideAppHotkeys(appHotkeys) +``` + +Alpine has no provider component. `provideAppHotkeys` creates the shared `AppHotkeysStore` and publishes it for every scope and devtools. Call it once at startup; it returns `{ store, dispose }`. + +## Register app hotkeys + +Pass an app hotkey wherever you pass a hotkey: + +```ts +class Editor { + scope = createHotkeysScope() + dirty = false + + init() { + const hotkeys = this.scope.createAppHotkeys() + this.scope.createHotkey(() => hotkeys.value['doc.save'], () => this.save(), () => ({ + enabled: this.dirty, + })) + this.scope.createHotkeys(() => [ + { hotkey: hotkeys.value['nav.home'], callback: () => this.goHome() }, + { hotkey: 'Escape', callback: () => this.close() }, + ]) + } + + destroy() { + this.scope.destroy() + } +} +``` + +`createAppHotkeys()` returns an object with a reactive `.value`. You can also import an app hotkey directly, such as `scope.createHotkey(appHotkeys['doc.save'], save)`. Without provided app hotkeys, the declared default is used. + +The registration takes `meta` and `options` from the declaration. Options merge in this order: default options, then declared options, then call-site options. `meta` comes only from the declaration, so the cheat sheet and the registration cannot disagree. Call-site options are typed by the declared default's kind, so `timeout` is accepted for `nav.home` but not for `doc.save`. + +When the user changes a hotkey, registered app hotkeys move to the new hotkey immediately, even if it changes from a chord to a sequence. + +## Build a cheat sheet + +`scope.createAppHotkeyList` returns every app hotkey in declaration order, with its live status: + +| Status | Meaning | +| --- | --- | +| `'active'` | At least one enabled registration | +| `'disabled'` | Registered, but every registration has `enabled: false` | +| `'inactive'` | Has a hotkey, but nothing has registered it | +| `'unbound'` | No hotkey assigned | + +```ts +class CheatSheet { + scope = createHotkeysScope() + formatForDisplay = formatForDisplay + items!: AlpineHotkeyState> + + init() { + this.items = this.scope.createAppHotkeyList() + } + + destroy() { + this.scope.destroy() + } +} +``` + +```html + +``` + +An app hotkey registered by many components, such as one per list row, appears once. Its `registrations` array holds each live registration. Devtools also list declared app hotkeys that are not registered. + +## Customize and persist hotkeys + +TanStack Hotkeys does not store anything. Pass the user's saved custom hotkeys in and save them when they change: + +```ts +import { provideAppHotkeys } from '@tanstack/alpine-hotkeys' + +provideAppHotkeys(appHotkeys, { + customHotkeys: JSON.parse(localStorage.getItem('hotkeys') ?? '{}'), + onCustomHotkeysChange: (custom) => + localStorage.setItem('hotkeys', JSON.stringify(custom)), +}) +``` + +- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them. +- `customHotkeys` is read once; afterwards the shared store owns the state. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning. +- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile. + +Recorded hotkeys use `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others. + +### Change hotkeys + +`scope.createCustomHotkeys` reads and changes the user's custom hotkeys: + +```ts +import { createHotkeysScope, formatForDisplay } from '@tanstack/alpine-hotkeys' +import type { + AlpineCustomHotkeys, + AlpineHotkeyRecorder, + AlpineHotkeyState, + AppHotkeyId, + RegisteredAppHotkeys, +} from '@tanstack/alpine-hotkeys' + +class ShortcutRow { + scope = createHotkeysScope() + formatForDisplay = formatForDisplay + hotkeys!: AlpineHotkeyState + custom!: AlpineCustomHotkeys + recorder!: AlpineHotkeyRecorder + + constructor(public id: AppHotkeyId) {} + + init() { + this.hotkeys = this.scope.createAppHotkeys() + this.custom = this.scope.createCustomHotkeys() + this.recorder = this.scope.createHotkeyRecorder({ + maxSteps: 3, + validate: (hotkey) => { + const conflicts = this.custom.findConflicts(this.id, hotkey) + return conflicts.length === 0 || `Used by ${conflicts[0]?.meta?.name}` + }, + onRecord: (hotkey) => this.custom.setCustomHotkey(this.id, hotkey), + onClear: () => this.custom.setCustomHotkey(this.id, null), + }) + } + + get appHotkey() { + return this.hotkeys.value[this.id] + } + + destroy() { + this.scope.destroy() + } +} + +Alpine.data('shortcutRow', (id: AppHotkeyId) => new ShortcutRow(id)) +``` + +| Member | Description | +| --- | --- | +| `value` | The hotkeys that differ from the defaults | +| `setCustomHotkey(id, hotkey)` | Sets a hotkey, or unbinds with `null`. Returns conflicting app hotkeys; when there are any, nothing changes. | +| `findConflicts(id, hotkey)` | App hotkeys that would collide if `id` used `hotkey` | +| `resetCustomHotkey(id)` | Restores one default | +| `resetCustomHotkeys()` | Restores every default | + +Two app hotkeys conflict when their contexts match or either has no `context`, and their hotkeys collide: equal chords, or one sequence starts with the other, including a sequence whose first step equals a chord. Conflicts are checked across every declaration, whether or not it is registered. + +Each app hotkey's `defaultHotkey` holds its declared default, and each cheat sheet item's `isCustom` reports whether the effective hotkey differs from it. diff --git a/docs/framework/alpine/guides/formatting-display.md b/docs/framework/alpine/guides/formatting-display.md index 0b4e1112..e1495149 100644 --- a/docs/framework/alpine/guides/formatting-display.md +++ b/docs/framework/alpine/guides/formatting-display.md @@ -3,7 +3,7 @@ title: Formatting & Display Guide id: formatting-display --- -Use `formatForDisplay` whenever a binding appears in a menu, button, hint, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, and parsed bindings. Store the original binding and format it at render time: the display label is not a registration string. +Use `formatForDisplay` whenever a hotkey appears in a menu, button, hint, cheat sheet, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, parsed bindings, and sequences. Store the original binding and format it at render time: the display label is not a registration string. ## Format a binding @@ -27,21 +27,23 @@ Set `parts: true` to get one label per key instead of a joined string: ```ts const binding = 'Mod+[KeyS]' const parts = formatForDisplay(binding, { platform: 'mac', parts: true }) -// ['⌘', 'S']; render each part in its own +// ['⌘', 'S'] — render each part in its own ``` With `parts` omitted or false, the result is a string. A runtime boolean returns `string | string[]`. Parts preserve literal plus keys and ignore `separatorToken`. -Sequences are arrays of bindings. Format their steps individually: +## Format sequences -```ts -import type { HotkeySequence } from '@tanstack/alpine-hotkeys' +Pass a sequence to format each step and join the steps with `sequenceSeparator`, a space by default: -const sequence: HotkeySequence = ['Mod+[KeyK]', 'C'] -const label = sequence.map((step) => formatForDisplay(step)).join(' → ') +```ts +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac' }) // '⌘ K C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' → ' }) // 'G → G' +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac', parts: true }) +// [['⌘', 'K'], ['C']] — one array of keycaps per step ``` -`formatHotkeySequence` only joins stored strings with spaces. It intentionally retains brackets and code names, so use the code above for user-facing labels. +With `parts: true`, a sequence returns one array per step, so you can render each step as a group of keycaps. ## Choose symbols and separators @@ -49,21 +51,21 @@ const label = sequence.map((step) => formatForDisplay(step)).join(' → ') ```ts formatForDisplay('Shift+[ArrowUp]', { - platform: 'mac', - useSymbols: { modifiers: false, keys: true }, - parts: true, + platform: 'mac', + useSymbols: { modifiers: false, keys: true }, + parts: true, }) // ['Shift', '↑'] formatForDisplay('Mod+[KeyS]', { - platform: 'mac', useSymbols: false, + platform: 'mac', useSymbols: false, }) // 'Cmd+S' formatForDisplay('Control++', { - platform: 'windows', separatorToken: ' · ', + platform: 'windows', separatorToken: ' · ', }) // 'Ctrl · +' ``` -An empty separator joins labels directly. `undefined` or `null` uses the platform default. `formatWithLabels(binding, options)` is the shorthand for `formatForDisplay` with `useSymbols: false`. +An empty separator joins labels directly. `undefined` or `null` uses the platform default. `separatorToken` applies within a chord; `sequenceSeparator` applies between steps. ## Supply layout labels @@ -82,12 +84,16 @@ Use `keyLabels` for explicit labels keyed by physical code or normalized logical ```ts formatForDisplay('Mod+[KeyQ]', { - platform: 'mac', layoutMap, keyLabels: { KeyQ: 'Action' }, + platform: 'mac', layoutMap, keyLabels: { KeyQ: 'Action' }, }) // '⌘ Action' ``` The precedence is `keyLabels`, then a layout entry, then the fallback label. Layout entries receive normal letter casing and key symbols; missing or empty entries fall back. Explicit labels are final. All of these options affect display only. +## Compare hotkeys + +`areHotkeysEqual(a, b)` compares chords or sequences after resolving modifiers, so `Mod+S` equals `Meta+S` on macOS. Sequences compare step by step. `isHotkeySequence` narrows a `Hotkey` to a sequence, and `simplifyHotkey` turns a one-step sequence into its chord. + ## Parse and store bindings `parseHotkey` returns either a logical `key` or a physical `code`, plus resolved modifier flags. Narrow the union before inspecting the identity: @@ -97,7 +103,7 @@ import { parseHotkey, normalizeHotkeyFromParsed } from '@tanstack/alpine-hotkeys const parsed = parseHotkey('Mod+[KeyS]', 'mac') if (parsed.code !== undefined) { - console.log(parsed.code) // 'KeyS'; parsed.key is undefined + console.log(parsed.code) // 'KeyS'; parsed.key is undefined } const stored = normalizeHotkeyFromParsed(parsed, 'mac') // 'Mod+[KeyS]' formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S' @@ -105,6 +111,6 @@ formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S' Parsed modifiers are already resolved. To display a portable `Mod` binding on another platform, serialize with the original platform first, as above. `normalizeRegisterableHotkey` accepts strings or raw objects and preserves the logical/physical distinction. Do not store display labels or put a bracketed code in a logical `key` field. -Use `validateHotkey` when accepting strings from an external source. It returns `valid`, `errors`, and `warnings`; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). +Use `validateHotkey` when accepting a chord or sequence from an external source. It returns `valid`, `errors`, and `warnings`, and names the failing step of a sequence; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). Try these options together in the [vanilla formatter playground](../../../framework/vanilla/examples/formatForDisplay). diff --git a/docs/framework/alpine/guides/hotkey-recording.md b/docs/framework/alpine/guides/hotkey-recording.md index bc210ce8..07cba658 100644 --- a/docs/framework/alpine/guides/hotkey-recording.md +++ b/docs/framework/alpine/guides/hotkey-recording.md @@ -3,161 +3,212 @@ title: Hotkey Recording Guide id: hotkey-recording --- -Use `createHotkeyRecorder` to build a shortcut customization UI. Recording defaults to physical codes, producing values such as `Mod+[KeyS]`. Store that value directly and pass it to `createHotkey`. Use `formatForDisplay` for the label. +`scope.createHotkeyRecorder` is for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the key combination they want, the same way system preferences or IDE shortcut editors work. By default it records one chord; set `maxSteps` to let users record a [sequence](./sequences) too. -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. +Recorders default to physical codes: recording a shortcut stores a string such as `Mod+[KeyS]`. Pass it directly to your hotkey registration and use `formatForDisplay` for the label. Set `recordBy: 'key'` when you intentionally want the produced character instead. -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Create getters in `init()` so they read the reactive component instance. +TanStack Hotkeys automatically suppresses registered hotkey callbacks while any recorder is active. You do not need to disable hotkeys while recording. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. ## Basic usage - ```ts -import Alpine from 'alpinejs' -import { createHotkeysScope, formatForDisplay } from '@tanstack/alpine-hotkeys' -import type { AlpineHotkeyRecorder, Hotkey } from '@tanstack/alpine-hotkeys' - -class ShortcutSettings { - binding: Hotkey = 'Mod+S' - scope = createHotkeysScope() - recorder!: AlpineHotkeyRecorder - - init() { - this.recorder = this.scope.createHotkeyRecorder({ - onRecord: (hotkey) => { this.binding = hotkey }, - onClear: () => { this.binding = 'Mod+S' }, - }) - this.scope.createHotkey(() => this.binding, () => console.log('Saved')) - } - get label() { return formatForDisplay(this.binding) } - destroy() { this.scope.destroy() } +class ShortcutRecorder { + scope = createHotkeysScope() + formatForDisplay = formatForDisplay + recorder!: AlpineHotkeyRecorder + + init() { + this.recorder = this.scope.createHotkeyRecorder({ + onRecord: (hotkey) => console.log('Recorded:', hotkey), // e.g. 'Mod+Shift+[KeyS]' + }) + } + + destroy() { + this.scope.destroy() + } } - -Alpine.data('shortcutSettings', () => new ShortcutSettings()) ``` ```html -
- - - +
+
``` -This example stores the replacement binding in application state and restores `Mod+S` when the user clears it. Cancellation leaves the saved binding unchanged. - -## Return value +## Recorder state and actions -| Property | Type | Meaning | -| --- | --- | --- | -| `isRecording` | `boolean` | Whether a session is active. | -| `recordedHotkey` | `Hotkey \| null` | The recorded binding, or `null` after starting, stopping, or cancelling. | -| `startRecording` | `() => void` | Start a new session. | -| `stopRecording` | `() => void` | Stop and clear recorder state without calling `onRecord` or `onCancel`. | -| `cancelRecording` | `() => void` | Stop, clear recorder state, and call `onCancel`. | +| Member | Description | +| --- | --- | +| `isRecording` | Whether the recorder is currently listening for key presses | +| `steps` | Chords captured so far in a multi-step recording | +| `recordedHotkey` | The last recorded hotkey, or `null` if nothing recorded | +| `startRecording()` | Start listening for key presses | +| `stopRecording()` | Stop listening without calling `onRecord` or `onCancel` | +| `cancelRecording()` | Stop listening and call `onCancel` | +| `commitRecording()` | Commit the current `steps` now (multi-step recordings) | -The state fields are reactive getters. Read `recorder.isRecording` and `recorder.recordedHotkey` where the framework tracks dependencies; destructuring them once captures a snapshot. +State members are reactive getters. Read them from the recorder in templates or effects instead of destructuring them once. ## Options ### `recordBy` -The default is `'code'`. On macOS, Option+S producing `ß` records `Alt+[KeyS]`, and Option+2 producing `™` records `Alt+[Digit2]`. Stored brackets preserve physical identity through serialization and registration. Existing authored strings such as `Mod+S` remain logical. +Recorders default to `recordBy: 'code'`. Option+S producing `ß` on macOS records `Alt+[KeyS]`; Option+2 producing `™` records `Alt+[Digit2]`. The stored brackets preserve physical identity through state, serialization, and registration; `formatForDisplay` produces a readable label. Existing authored strings such as `Mod+S` remain logical hotkeys. -Set `recordBy: 'key'` to record the produced character. Code mode rejects an event without a usable code and never falls back to key mode. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the character without synthetic Control/Alt while retaining Shift. +Set `recordBy: 'key'` to intentionally record the produced logical character. A code-mode event with no usable code is rejected rather than silently switching modes. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the produced character without synthetic Control/Alt while retaining Shift. ### `onRecord` -Receives the recorded `Hotkey` when the user enters a valid chord. A chord can be a single non-modifier key or a key with modifiers. Update your saved binding here. +Called when a recording commits. With the default `maxSteps: 1`, it receives a chord string such as `'Mod+[KeyS]'` as soon as the user presses a valid combination (a modifier + a non-modifier key, or a single non-modifier key). With a larger `maxSteps`, it receives a chord when one step was recorded, or a sequence otherwise. ### `onCancel` -Runs when Escape cancels a session or when you call `cancelRecording()`. Use it to exit an editing state without changing the saved binding. +Called when recording is cancelled, by pressing Escape or calling `cancelRecording()`. ### `onClear` -Runs when the user presses unmodified Backspace or Delete during recording. Clearing calls only `onClear`; it does not call `onRecord`. Your application decides whether to remove the binding or restore an initial value. +Called when the recording is cleared: Backspace or Delete with no modifiers and no recorded steps. Clearing calls only `onClear`; your app decides whether that removes a hotkey or restores a default. -### Shared defaults and changing options +### `ignoreInputs` -Pass defaults to `createHotkeysScope`. The scope accepts `hotkey`, `hotkeySequence`, `hotkeyRecorder`, and `hotkeySequenceRecorder` options. Pass a getter to follow Alpine state. Each component owns and destroys its scope. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. See [shared defaults](../quick-start.md#shared-defaults) for a complete example. +Defaults to `true`. The recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Escape still cancels recording even while an input is focused. Set `ignoreInputs: false` to record from inside input elements. -Pass an options getter to follow changing Alpine state: `scope.createHotkeyRecorder(() => ({ recordBy: this.recordBy, onRecord: this.saveBinding }))`. Read the latest component properties inside callbacks. +Options can also be a getter, such as `scope.createHotkeyRecorder(() => ({ maxSteps: 3, recordBy: this.mode, onRecord }))`, so recording policy can follow Alpine state. -## Recording behavior +### Default options -| Input | Behavior | -| --- | --- | -| Modifier alone | Wait for a non-modifier key. | -| Modifier plus a non-modifier key | Record the chord and finish. | -| Single non-modifier key, such as F1 | Record the key and finish. | -| Escape | Cancel. | -| Unmodified Backspace or Delete | Clear and call `onClear`. | -| Automatic key repeat or IME composition | Do not record a new chord. | +Set defaults for every recorder with `createHotkeysScope`. Per-recorder options override them. -Recording events, repeats, and their key releases do not trigger registered hotkeys or sequences. +```ts +scope = createHotkeysScope({ hotkeyRecorder: { idleTimeoutMs: 2000 } }) +``` -### `ignoreInputs` +## Recording sequences -This defaults to `true`. Normal typing in inputs, textareas, selects, and contentEditable elements passes through. Escape still cancels while an input is focused. Set `ignoreInputs: false` to capture shortcuts from a focused input. +Set `maxSteps` above 1 to record up to that many chords. Each chord adds a step, and the recording commits when the user presses Enter, when it reaches `maxSteps`, or after `idleTimeoutMs` without a new step: -### Mod auto-conversion +```ts +init() { + this.recorder = this.scope.createHotkeyRecorder({ + maxSteps: 3, + onRecord: (hotkey) => console.log('Recorded:', hotkey), // 'Mod+[KeyS]' or ['[KeyG]', '[KeyG]'] + }) +} -On macOS, Command+S becomes `Mod+[KeyS]`. Reusing that binding on Windows resolves `Mod` to Control while preserving the physical key position. Pass a `platform` option when detection must be overridden. +// Live preview while recording +get label() { + return this.recorder.isRecording + ? formatForDisplay(this.recorder.steps) || 'Press chords, then Enter…' + : formatForDisplay(this.recorder.recordedHotkey ?? []) +} +``` + +A one-step recording commits as a chord, so the result can always be passed straight to your hotkey registration. Use `maxSteps: Infinity` for no limit. + +| Option | Description | +| --- | --- | +| `maxSteps` | Maximum chords to record. Reaching it commits. Default `1`. | +| `commitKeys` | `'enter'` (default) commits on plain Enter. With `'none'`, plain Enter can be recorded as a step, and only `commitRecording()`, `idleTimeoutMs`, or `maxSteps` commit. | +| `idleTimeoutMs` | Commit automatically after this many milliseconds without a new step. The timer does not run before the first chord. | + +`commitKeys` and `idleTimeoutMs` are only accepted when `maxSteps` is set. With `maxSteps` above 1, `onRecord` and `validate` receive `Hotkey` (a chord or a sequence) instead of a chord. + +## Recording behavior + +| Key | Behavior | +| --- | --- | +| Modifier only (Shift, Ctrl, etc.) | Waits for a non-modifier key; modifier-only presses don't complete a step | +| Modifier + key (e.g., Ctrl+S) | Records the full combination | +| Single key (e.g., Escape, F1) | Records the single key | +| Enter (multi-step, `commitKeys: 'enter'`) | Commits the recorded steps | +| Escape | Cancels the recording | +| Backspace / Delete | Removes the last step; with no steps, clears and calls `onClear` | + +Repeats and IME composition never add a step. Recorded hotkeys use the portable `Mod` format: Command+S on macOS records `Mod+[KeyS]` rather than `Meta+[KeyS]`, so the primary modifier becomes Control if the hotkey is later used on Windows. ## Validation and conflicts -Supply these options alongside `onRecord`: +The recorder accepts `detectConflicts`, `validate`, and `onReject` alongside `onRecord`: ```ts -import type { HotkeyRecorderOptions } from '@tanstack/alpine-hotkeys' - -const options: HotkeyRecorderOptions = { - onRecord: (hotkey) => console.log('Accepted', hotkey), - detectConflicts: { - // Replace this with the ID from the live registration being edited. - excludeIds: ['registration-being-edited'], - target: document, - eventType: 'keydown', - }, - validate: (_hotkey, { parsedHotkey }) => - parsedHotkey.modifiers.length > 0 || 'Include a modifier.', - onReject: ({ reason, message, conflicts }) => { - console.log(reason, message, conflicts) - }, +const options = { + detectConflicts: { + // Use registration IDs to exclude the hotkey being edited: + excludeIds: [registrationId], + target: document, + eventType: 'keydown', + }, + validate: (_hotkey, { parsedHotkey }) => + parsedHotkey.modifiers.length > 0 || 'Include a modifier.', + onReject: ({ reason, message, conflicts }) => { + // Show feedback; recording stays active. + console.log(reason, message, conflicts) + }, } ``` -`validate` returns `true` to accept, or `false` or a message to reject. Validation runs before commit. A rejected candidate leaves recording active. `onReject` reports `missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`, plus the candidate when available and conflicting views for a conflict. +`validate(hotkey, context)` returns true to accept, or false/a message to reject. The context holds the last chord's `event` and `parsedHotkey`, plus `events` and `parsedSequence` for every step. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the rejected `hotkey` when available, and conflicting registrations for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. A rejected multi-step recording keeps its steps so the user can edit them with Backspace. -`detectConflicts: true` checks enabled live registrations with the intended event type, defaulting to keydown, and overlapping targets, defaulting to document. Document and nested element scopes can conflict; disjoint widgets can reuse a binding. The options object supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. +`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a hotkey. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Chords and sequence prefixes are checked: a recorded chord conflicts with a sequence that starts with it, and a recorded sequence conflicts with a registered chord equal to its first step. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute. -Checks include single bindings and sequence prefixes. Source events detect physical/logical overlap on the recorded layout. This is conservative collision detection: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can change dispatch. - -For checks outside recording, call `findHotkeyConflicts(bindingOrSequence, options)`. Without source `events`, it compares identities and prefixes rather than guessing which logical character a physical position produces. +For checks outside recording, use `findHotkeyConflicts(hotkey, options)`. Without source `events`, it compares hotkey identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. ## Building a shortcut settings UI -For several actions, keep the bindings in an array or object and record the ID of the action being edited. On `onRecord`, replace that action's binding and clear the editing ID. On `onCancel`, clear only the editing ID. Use the plural registration API for the current list. +For an app-wide settings screen, declare the hotkeys with [app hotkeys](./app-hotkeys) and save the recorder's result with `setCustomHotkey`. That keeps cheat sheets, conflicts, and persistence in one place. + +For a single component, ordinary state is enough: ```ts -// Inside init(), after creating this.recorder: -this.scope.createHotkeys(() => this.shortcuts.map((shortcut) => ({ - hotkey: shortcut.hotkey, - callback: () => this.runAction(shortcut.id), - options: { meta: { name: shortcut.name } }, -}))) -``` +import { createHotkeysScope, formatForDisplay } from '@tanstack/alpine-hotkeys' +import type { AlpineHotkeyRecorder, Hotkey } from '@tanstack/alpine-hotkeys' -The [createHotkeyRecorder example](../examples/createHotkeyRecorder) includes multiple actions, editable names and descriptions, create/delete controls, reset and clear behavior, cancellation, and a live registry. The [kitchen sink](../examples/kitchen-sink) also demonstrates conflict feedback and physical versus logical recording. +type Action = 'save' | 'undo' | 'search' + +class ShortcutSettings { + scope = createHotkeysScope() + formatForDisplay = formatForDisplay + shortcuts: Record = { save: 'Mod+S', undo: 'Mod+Z', search: ['G', 'S'] } + editing: Action | null = null + recorder!: AlpineHotkeyRecorder + + init() { + this.recorder = this.scope.createHotkeyRecorder({ + maxSteps: 2, + onRecord: (hotkey) => { + if (this.editing) this.shortcuts = { ...this.shortcuts, [this.editing]: hotkey } + this.editing = null + }, + onCancel: () => (this.editing = null), + }) + this.scope.createHotkeys(() => + Object.entries(this.shortcuts).map(([action, hotkey]) => ({ + hotkey, + callback: () => this.run(action as Action), + })), + ) + } + + edit(action: Action) { + this.editing = action + this.recorder.startRecording() + } + + run(action: Action) { + /* ... */ + } + + destroy() { + this.scope.destroy() + } +} +``` ## Under the hood -Alpine subscribes to the core TanStack Store and destroys the recorder with its scope. The core `HotkeyRecorder` owns the recording listeners. Store the accepted binding in application state rather than relying on recorder session state as your preferences store. +`scope.createHotkeyRecorder` creates a `HotkeyRecorder` from `@tanstack/hotkeys`, exposes its state reactively, and destroys it with the scope. diff --git a/docs/framework/alpine/guides/hotkeys.md b/docs/framework/alpine/guides/hotkeys.md index 4b365c88..8512ac54 100644 --- a/docs/framework/alpine/guides/hotkeys.md +++ b/docs/framework/alpine/guides/hotkeys.md @@ -7,6 +7,18 @@ id: hotkeys Pass getters for bindings and options that can change. A value such as `{ enabled: this.enabled }` captures the value at registration time; `() => ({ enabled: this.enabled })` follows Alpine state. State readers return an object with a reactive `.value` getter. Read it in a template or an Alpine effect instead of destructuring it once. +## Chords and sequences + +A hotkey is either a **chord**, keys pressed together such as `'Mod+S'`, or a **sequence**, chords pressed one after another such as `['G', 'G']`. Every API in this guide accepts both, and a one-step array such as `['Mod+S']` is the chord `'Mod+S'`. + +```ts +this.scope.createHotkey('Mod+S', () => this.save()) // chord +this.scope.createHotkey(['G', 'G'], () => this.scrollToTop(), { timeout: 500 }) // sequence +this.scope.createHotkey('Escape', () => this.close(), { requireReset: true }) // chord-only option +``` + +Most options apply to both kinds. `requireReset` applies only to chords and `timeout` only to sequences, and TypeScript rejects options that don't match the hotkey. When the kind isn't known statically, such as a `Hotkey` loaded from settings, only the shared options are accepted. Defaults apply only to the kinds that support them. See the [Sequences Guide](./sequences) for timing and matching, and the [App Hotkeys Guide](./app-hotkeys) to declare hotkeys up front and let users customize them. + ## Logical keys and physical positions Use a logical binding when the shortcut should follow the character on the active layout. Use a physical binding when it should follow a keyboard position: @@ -134,7 +146,7 @@ By default, Control/Meta shortcuts and Escape work in text inputs, textareas, se ### Shared default options -Pass defaults to `createHotkeysScope`. The scope accepts `hotkey`, `hotkeySequence`, `hotkeyRecorder`, and `hotkeySequenceRecorder` options. Pass a getter to follow Alpine state. Each component owns and destroys its scope. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. See [shared defaults](../quick-start.md#shared-defaults) for a complete example. +Pass defaults to `createHotkeysScope`. The scope accepts `hotkey` and `hotkeyRecorder` options. Pass a getter to follow Alpine state. Each component owns and destroys its scope. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. See [shared defaults](../quick-start.md#shared-defaults) for a complete example. ## Hotkey options @@ -208,7 +220,7 @@ Pass getters for bindings and options that can change. A value such as `{ enable ## Registering multiple hotkeys -Use `createHotkeys` to register a dynamic list. Per-definition options override common options. Removing an entry unregisters it. Each entry has the shared [HotkeyDefinition](../../../reference/adapter/interfaces/HotkeyDefinition) shape. +Use `createHotkeys` to register a dynamic list. Per-definition options override common options. Removing an entry unregisters it. Each entry has the shared [AlpineHotkeyDefinition](../reference/type-aliases/AlpineHotkeyDefinition.md) shape. ```ts this.scope.createHotkeys( @@ -218,9 +230,9 @@ this.scope.createHotkeys( ``` ```ts -import type { HotkeyDefinition } from '@tanstack/alpine-hotkeys' +import type { AlpineHotkeyDefinition } from '@tanstack/alpine-hotkeys' -const shortcuts: Array = [ +const shortcuts: Array = [ { hotkey: 'Mod+S', callback: () => console.log('Save') }, { hotkey: 'Mod+Z', callback: () => console.log('Undo'), options: { enabled: false } }, ] @@ -246,7 +258,7 @@ declare module '@tanstack/hotkeys' { ## Introspecting registrations -Read `hotkeys` and `sequences` from `createHotkeyRegistrations()`. Disabled entries stay listed; destroyed registrations disappear. Both arrays include options and metadata. Hotkey views include enabled state and trigger counts; sequence views include sequence steps and progress. +Use the `createHotkeyRegistrations` scope method to get a live view of every registration, chords and sequences together. It's the building block for shortcut palettes and help dialogs. ```ts // Inside init(): @@ -258,11 +270,13 @@ this.scope.createHotkey('Mod+S', () => this.save(), { ```html ``` -Read `registrations.sequences` in the same way. Format each sequence step with `formatForDisplay` and join the labels with an arrow. Group entries by `registration.options.meta?.group` when building a help panel. +Read the reactive `hotkeys` array in templates or effects. Each view has an `id`, the `hotkey`, its `options` (including `meta`), its `target`, and a `triggerCount`. Narrow with `kind`: chord views add `parsedHotkey`; sequence views add `parsedSequence` and `matchedStepCount`, the steps matched so far in the current attempt. Disabled entries stay listed; unmounted registrations disappear. Format any hotkey with `formatForDisplay`, and group entries by `options.meta?.group` when building a help panel. + +Registrations only exist while their components are mounted. To list hotkeys from routes that have not rendered, declare them with [app hotkeys](./app-hotkeys). ## The hotkey manager diff --git a/docs/framework/alpine/guides/key-state-tracking.md b/docs/framework/alpine/guides/key-state-tracking.md index 554887ea..b7a09426 100644 --- a/docs/framework/alpine/guides/key-state-tracking.md +++ b/docs/framework/alpine/guides/key-state-tracking.md @@ -97,7 +97,7 @@ const hint = scope.createHotkeyHint('Alt+Shift+[KeyK]') // Read hint.value in an Alpine template or effect. ``` -Holding Alt, Shift, or both reveals this hint. Extra Control hides it, as does releasing all modifiers or blurring the window. Nonmodifier keys are ignored. AltGraph never reveals hints. +Holding Alt, Shift, or both reveals this hint. Extra Control hides it, as does releasing all modifiers or blurring the window. Nonmodifier keys are ignored. AltGraph never reveals hints. A sequence reveals its hint from its first step, and an app hotkey uses its effective hotkey (no hint while unbound). Pass `{ exact: true }` to require every binding modifier, or `{ platform: 'mac' }` to resolve `Mod` explicitly. Supply the same platform used for registration. Combine the result with the action's enabled state. The core equivalent is `matchesHeldModifiers(binding, heldKeys, options)`. diff --git a/docs/framework/alpine/guides/sequence-recording.md b/docs/framework/alpine/guides/sequence-recording.md deleted file mode 100644 index 53d2d4c7..00000000 --- a/docs/framework/alpine/guides/sequence-recording.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: Sequence Recording Guide -id: sequence-recording ---- - -Use `createHotkeySequenceRecorder` to record an ordered sequence of chords. Recording defaults to physical codes, such as `['[KeyG]', 'Alt+[KeyS]']`. Use `recordBy: 'key'` for logical characters. Pass the saved array directly to `createHotkeySequence`. - -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Create getters in `init()` so they read the reactive component instance. - -## Basic usage - - -```ts -import Alpine from 'alpinejs' -import { createHotkeysScope, formatForDisplay } from '@tanstack/alpine-hotkeys' -import type { AlpineHotkeySequenceRecorder, HotkeySequence } from '@tanstack/alpine-hotkeys' - -class SequenceSettings { - binding: HotkeySequence = ['G', 'G'] - scope = createHotkeysScope() - recorder!: AlpineHotkeySequenceRecorder - init() { - this.recorder = this.scope.createHotkeySequenceRecorder({ - onRecord: (sequence) => { this.binding = sequence }, - }) - this.scope.createHotkeySequence(() => this.binding, () => console.log('Go to top')) - } - get preview() { - return this.recorder.steps.map((step) => formatForDisplay(step)).join(' → ') - } - destroy() { this.scope.destroy() } -} - -Alpine.data('sequenceSettings', () => new SequenceSettings()) -``` - -```html -
- - -
-``` - -Press and release each chord, then press Enter or click Save. Cancellation leaves the saved binding unchanged. - -## Return value - -| Property | Type | Meaning | -| --- | --- | --- | -| `isRecording` | `boolean` | Whether a session is active. | -| `steps` | `HotkeySequence` | Chords captured in the current session. | -| `recordedSequence` | `HotkeySequence \| null` | The last committed sequence. | -| `startRecording` | `() => void` | Start a new session. | -| `stopRecording` | `() => void` | Stop without calling `onRecord` or `onCancel`. | -| `cancelRecording` | `() => void` | Discard the session and call `onCancel`. | -| `commitRecording` | `() => void` | Commit current steps; do nothing if empty. | - -Read state through the recorder object. Destructuring its reactive getters once captures a snapshot. - -## Options - -- `recordBy`: `'code'` by default, or `'key'` for logical characters. Missing physical codes never fall back to logical recording. -- `onRecord(sequence)`: called when a nonempty sequence is committed. Clearing calls only `onClear`. -- `onCancel` and `onClear`: handle cancellation and removal of a saved binding. -- `commitKeys`: `'enter'` by default. Use `'none'` for manual or idle-timeout commit; plain Enter can then be recorded as a chord. -- `commitOnEnter`: with `commitKeys: 'enter'`, set this to `false` to record Enter as a chord and finish another way. -- `idleTimeoutMs`: commit after this many milliseconds of inactivity following a completed chord. No timer runs while waiting for the first chord. -- `platform`: override platform detection for portable `Mod` conversion. - -### Shared defaults - -Pass defaults to `createHotkeysScope`. The scope accepts `hotkey`, `hotkeySequence`, `hotkeyRecorder`, and `hotkeySequenceRecorder` options. Pass a getter to follow Alpine state. Each component owns and destroys its scope. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. See [shared defaults](../quick-start.md#shared-defaults) for a complete example. Use an options getter for reactive configuration. - -### `ignoreInputs` - -The default is `true`, so normal typing in inputs, textareas, selects, and contentEditable elements passes through. Escape still cancels. Set `ignoreInputs: false` to record in a focused input. - -## Validation and conflicts - -`validate(sequence, { events, parsedSequence })` runs when committing and returns `true`, `false`, or a rejection message. `detectConflicts` checks single bindings and sequence prefixes. A rejected commit keeps the session active and the steps intact, so the user can edit them with Backspace. - -The recorder supports the same `detectConflicts`, `validate`, and `onReject` options as the [single-hotkey recorder](./hotkey-recording.md#validation-and-conflicts). Use live registration IDs to exclude the binding being edited. Physical/logical overlap is based on the captured events, not a guessed keyboard layout. - -## Behavior - -| Input | Behavior | -| --- | --- | -| Valid chord | Append to `steps` and continue listening. | -| Unmodified Enter with default commit settings and nonempty steps | Commit and call `onRecord`. | -| Escape | Cancel and call `onCancel`. | -| Unmodified Backspace or Delete with nonempty steps | Remove the last step without committing. | -| Unmodified Backspace or Delete with empty steps | Stop and call only `onClear`. | -| Modifier-only press, repeat, or IME composition | Do not append a step. | - -Recorded chords use portable `Mod`. Recording events, commit keys, and their releases are isolated from application hotkeys and sequences. Code mode rejects AltGraph character entry; key mode preserves the produced character without synthetic Control/Alt. - -## Under the hood - -The Alpine scope owns the core Store subscription and recorder. Call `scope.destroy()` from component teardown. - -See the [createHotkeySequenceRecorder example](../examples/createHotkeySequenceRecorder) for editable sequence settings and the [kitchen sink](../examples/kitchen-sink) for manual commit, idle timeout, validation, and conflict feedback. diff --git a/docs/framework/alpine/guides/sequences.md b/docs/framework/alpine/guides/sequences.md index 2c9823a5..31c9af7e 100644 --- a/docs/framework/alpine/guides/sequences.md +++ b/docs/framework/alpine/guides/sequences.md @@ -3,145 +3,150 @@ title: Sequences Guide id: sequences --- -A sequence is an ordered list of chords. Use `createHotkeySequence` for one sequence and `createHotkeySequences` for a changing list. A chord can be a logical binding such as `G`, a physical position such as `[KeyG]`, or a modifier combination such as `Mod+[KeyK]`. +A sequence is a hotkey made of chords pressed one after another rather than together. Think Vim-style navigation, cheat codes, or multi-step commands. Sequences are ordinary hotkeys: register them with `scope.createHotkey` and `scope.createHotkeys`, list them with `scope.createHotkeyRegistrations`, record them with `scope.createHotkeyRecorder`, and format them with `formatForDisplay`. -## Reactive options - -Sequence options support the same [property getter patterns](./hotkeys.md#property-getters) as hotkey options, including `enabled`, `timeout`, and `target`. Updates preserve the registration when its sequence, target, and platform stay the same. +Sequence steps use the same syntax as chords. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. ## Basic usage +Pass an array of chords wherever you pass a hotkey: ```ts import Alpine from 'alpinejs' import { createHotkeysScope } from '@tanstack/alpine-hotkeys' -Alpine.data('navigation', () => ({ - enabled: true, - timeout: 1000, - scope: createHotkeysScope(), - init() { - this.scope.createHotkeySequence(['G', 'G'], () => window.scrollTo({ top: 0 }), - () => ({ enabled: this.enabled, timeout: this.timeout })) - this.scope.createHotkeySequences([ - { sequence: ['D', 'D'], callback: () => console.log('Delete line') }, - { sequence: ['D', 'W'], callback: () => console.log('Delete word'), options: { timeout: 500 } }, - ], () => ({ enabled: this.enabled })) - }, - destroy() { this.scope.destroy() }, -})) -``` +class Editor { + scope = createHotkeysScope() + + init() { + // Vim-style: press g then g to scroll to top + this.scope.createHotkey(['G', 'G'], () => { + window.scrollTo({ top: 0, behavior: 'smooth' }) + }) + } -```html -
- -

Press G twice to scroll to the top.

-
+ destroy() { + this.scope.destroy() + } +} + +Alpine.data('editor', () => new Editor()) ``` -The user must press and release each chord in order. The timeout applies between consecutive steps, rather than to the total duration of the sequence. +The user must press the steps in order, each within the timeout window. A one-step array such as `['Escape']` is the chord `'Escape'`. ## Many sequences at once -`createHotkeySequences` accepts an array of [HotkeySequenceDefinition](../../../reference/adapter/interfaces/HotkeySequenceDefinition) entries. Each contains `sequence`, `callback`, and optional `options`. An empty array removes all registrations owned by that call. Per-definition options override common options. Pass a getter for a reactive list and for common options that change. +`scope.createHotkeys` registers chords and sequences together, including dynamic lists: -## Sequence options +```ts +this.scope.createHotkeys([ + { hotkey: ['G', 'G'], callback: () => this.scrollToTop() }, + { hotkey: ['D', 'D'], callback: () => this.deleteLine(), options: { timeout: 500 } }, + { hotkey: 'Mod+S', callback: () => this.save() }, +]) +``` -### `timeout` +## Sequence options -The default is `1000` milliseconds between steps. A shorter timeout, such as `500`, requires faster entry; `2000` gives users more time. Expired sequences reset their progress. +Sequences accept the same options as chords, except `requireReset`, plus `timeout`. -### `enabled` +### `timeout` -The default is `true`. Disabled sequences remain in the registry but do not execute. Pass a reactive flag to change this without discarding the registration. +The maximum time (in milliseconds) allowed between consecutive steps. If the user takes longer than this between any two steps, the sequence resets. Defaults to `1000` (1 second). -### Shared defaults +```ts +// Fast sequence - user must type quickly +this.scope.createHotkey(['D', 'D'], () => this.deleteLine(), { timeout: 500 }) -Pass defaults to `createHotkeysScope`. The scope accepts `hotkey`, `hotkeySequence`, `hotkeyRecorder`, and `hotkeySequenceRecorder` options. Pass a getter to follow Alpine state. Each component owns and destroys its scope. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. See [shared defaults](../quick-start.md#shared-defaults) for a complete example. +// Slow sequence - user has more time between keys +this.scope.createHotkey(['Shift+Z', 'Shift+Z'], () => this.forceQuit(), { timeout: 2000 }) +``` -### Targets, input handling, and propagation +`timeout` is only accepted when the hotkey is a sequence. Set a default for every sequence with `createHotkeysScope`: -Sequences support `target`, `ignoreInputs`, `preventDefault`, `stopPropagation`, `platform`, and `conflictBehavior`. Use a focusable element to scope a sequence to a widget, and `null` while that element is unavailable. When omitted, `ignoreInputs` follows the first step: Control/Meta combinations and Escape work in inputs, while other first steps are ignored there. Set it explicitly to override that behavior. See the [target examples](./hotkeys.md#target). +```ts +scope = createHotkeysScope({ hotkey: { timeout: 1500 } }) +``` -### Metadata +### `enabled` and `meta` -Attach a `meta` object with a name, description, and group. Read it through live registration views to build help panels. Metadata does not change execution scope. +These work exactly as they do for chords. Disabled sequences stay registered and visible in devtools, and `meta` flows to registrations, devtools, and cheat sheets. See the [Hotkeys Guide](./hotkeys). ```ts -this.scope.createHotkeySequence(['G', 'G'], () => window.scrollTo({ top: 0 }), { meta: { name: 'Go to top', group: 'Navigation' } }) +this.scope.createHotkey(['G', 'G'], () => this.scrollToTop(), () => ({ + enabled: this.vimMode, + meta: { name: 'Go to top', description: 'Scroll to the top of the page' }, +})) ``` ## Sequences with modifiers -Use `Mod+K`, then `Mod+C` for an editor-style comment shortcut. Logical and physical steps can be mixed. +Each step in a sequence can include modifiers, and consecutive steps can repeat the same modifier: ```ts -this.scope.createHotkeySequence(['Mod+K', 'Mod+C'], () => console.log("Comment selection")) +// Ctrl+K followed by Ctrl+C (VS Code-style comment) +this.scope.createHotkey(['Mod+K', 'Mod+C'], () => this.commentSelection()) + +// Physical R then T positions, both with Shift +this.scope.createHotkey(['Shift+[KeyR]', 'Shift+[KeyT]'], () => this.doNextAction()) ``` -## Chained modifier chords +Brackets retain physical positions even when the keys produce different letters. While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored. They neither advance the sequence nor reset progress, so a user can tap or hold Shift between `Shift+R` and `Shift+T`. -Physical codes retain the same positions even when the keyboard layout changes. +## Vim-style navigation ```ts -this.scope.createHotkeySequence(['Shift+[KeyR]', 'Shift+[KeyT]'], () => console.log("Next action")) +this.scope.createHotkeys([ + { hotkey: ['G', 'G'], callback: () => this.scrollToTop() }, + { hotkey: ['G', 'Shift+G'], callback: () => this.scrollToBottom() }, + { hotkey: ['D', 'D'], callback: () => this.deleteLine() }, + { hotkey: ['D', 'W'], callback: () => this.deleteWord() }, + { hotkey: ['C', 'I', 'W'], callback: () => this.changeInnerWord() }, +]) ``` -### Modifier-only keys between steps - -Pressing Shift, Control, Alt, or Meta alone neither advances nor resets an in-progress sequence. Users can release and press Shift between the R and T chords. Modifier-only events, automatic repeats, and IME composition also do not refresh the timeout. +## Displaying sequences -## Common sequence patterns - -| Action | Steps | -| --- | --- | -| Go to top | `['G', 'G']` | -| Go to bottom | `['G', 'Shift+G']` | -| Delete line | `['D', 'D']` | -| Delete word | `['D', 'W']` | -| Change inner word | `['C', 'I', 'W']` | -| Open help | `['H', 'E', 'L', 'P']` | - -For a Konami-style sequence, use the full ordered list and a longer timeout: +`formatForDisplay` formats each step and joins them with `sequenceSeparator`, a space by default: ```ts -this.scope.createHotkeySequence(['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', - 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'B', 'A'], () => console.log("Unlocked"), { timeout: 2000 }) +formatForDisplay(['Mod+K', 'Mod+C'], { platform: 'mac' }) // '⌘ K ⌘ C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' then ' }) // 'G then G' ``` -## How sequences work - -The shared `SequenceManager` tracks every registration independently. Matching the next expected step advances progress. Completing all steps invokes the callback. The manager resets progress when the timeout expires and prefers exact key matches over weaker logical-key fallbacks. +Registration views report each sequence's progress in `matchedStepCount`, so a help panel can show how far the user has typed. See the [Hotkeys Guide](./hotkeys). -### Overlapping sequences +## How sequences work -`['D', 'D']`, `['D', 'W']`, and `['D', 'I', 'W']` can share a prefix. Pressing D advances each matching sequence; later steps determine which one completes. +The `HotkeyManager` tracks progress for every sequence registration. When a key is pressed: -### Conflicting registrations +1. It checks whether the key matches the next expected step of each sequence +2. If it matches, that sequence advances +3. If the timeout expires between steps, the sequence resets +4. When all steps are completed, the callback fires -Duplicate detection compares resolved steps on the same target. Modifier aliases, modifier order, and logical letter casing do not create distinct bindings. Physical and logical identities remain distinct. Sharing a prefix alone is not a duplicate registration, but recorder conflict checks also inspect prefixes and physical/logical overlap. +Modifier-only events, IME composition, and automatic keydown repeats neither advance a sequence nor refresh its timeout: holding G does not complete a two-press G sequence. Exact matches take priority over weaker logical-key fallbacks. -### Updates and cleanup +Multiple sequences can share a prefix, such as `['D', 'D']`, `['D', 'W']`, and `['D', 'I', 'W']`. Progress is tracked for each sequence independently, so after pressing `D` the manager waits for the next key to determine which sequence completes. -Create one scope for each Alpine component. Register shortcuts in `init()` so getters and callbacks read the reactive component instance. Call `scope.destroy()` from the component's `destroy()` method. Destruction releases registrations, effects, Store subscriptions, and active recorders. Do not reuse a destroyed scope. +### Conflicting registrations -Pass getters for bindings and options that can change. A value such as `{ enabled: this.enabled }` captures the value at registration time; `() => ({ enabled: this.enabled })` follows Alpine state. State readers return an object with a reactive `.value` getter. Read it in a template or an Alpine effect instead of destructuring it once. +For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate hotkeys. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection and app hotkey conflicts are stricter: there, a sequence also conflicts with a chord equal to its first step and with any sequence it starts. -## The sequence manager +## Standalone matching -Use `getSequenceManager()` for the shared registry. For standalone matching without registration, create your own matcher and remove your event listener when its owner is destroyed. +For sequence matching without the singleton manager, use `createSequenceMatcher`: ```ts import { createSequenceMatcher } from '@tanstack/alpine-hotkeys' const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 }) -const onKeyDown = (event: KeyboardEvent) => { - if (matcher.match(event)) console.log('Sequence completed') - console.log(matcher.getProgress()) -} -document.addEventListener('keydown', onKeyDown) -// During teardown: -document.removeEventListener('keydown', onKeyDown) -``` -Try [createHotkeySequence](../examples/createHotkeySequence) and [createHotkeySequences](../examples/createHotkeySequences). +document.addEventListener('keydown', (e) => { + if (matcher.match(e)) { + console.log('Sequence completed!') + } + console.log('Progress:', matcher.getProgress()) // e.g., 1 +}) +``` diff --git a/docs/framework/alpine/quick-start.md b/docs/framework/alpine/quick-start.md index b73f8240..8e4d45b3 100644 --- a/docs/framework/alpine/quick-start.md +++ b/docs/framework/alpine/quick-start.md @@ -81,7 +81,7 @@ Use Alpine reactive state for the enabled flag. ```ts -this.scope.createHotkeySequence(['G', 'G'], () => window.scrollTo({ top: 0 })) +this.scope.createHotkey(['G', 'G'], () => window.scrollTo({ top: 0 })) ``` Release G between presses. Automatic key repeats do not advance sequences. @@ -120,7 +120,7 @@ Use `formatForDisplay(binding)` for platform-specific labels. Keep the original ## Shared defaults -Pass defaults to `createHotkeysScope`. The scope accepts `hotkey`, `hotkeySequence`, `hotkeyRecorder`, and `hotkeySequenceRecorder` options. Pass a getter to follow Alpine state. Each component owns and destroys its scope. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. +Pass defaults to `createHotkeysScope`. The scope accepts `hotkey` and `hotkeyRecorder` options. `requireReset` applies to chords and `timeout` to sequences. Pass a getter to follow Alpine state. Each component owns and destroys its scope. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. ```ts import Alpine from 'alpinejs' @@ -128,10 +128,8 @@ import { createHotkeysScope } from '@tanstack/alpine-hotkeys' Alpine.data('scopedEditor', () => { const scope = createHotkeysScope({ - hotkey: { requireReset: true }, - hotkeySequence: { timeout: 1500 }, - hotkeyRecorder: { ignoreInputs: false }, - hotkeySequenceRecorder: { idleTimeoutMs: 2000 }, + hotkey: { requireReset: true, timeout: 1500 }, + hotkeyRecorder: { ignoreInputs: false, idleTimeoutMs: 2000 }, }) return { init() { @@ -145,6 +143,10 @@ Alpine.data('scopedEditor', () => { For changing defaults, create the scope in `init()` with `createHotkeysScope(() => ({ hotkey: { enabled: this.enabled } }))`. Creating the getter in `init()` makes `this` refer to Alpine's reactive component instance. Share a defaults object or getter between components when they need the same policy; give each component its own scope for cleanup. The `$hotkeys` magic uses core defaults, so use an explicit scope when supplying shared defaults. +## Declaring app hotkeys + +To list every hotkey in a cheat sheet, including hotkeys whose components haven't mounted, and to let users customize them, declare them once with `defineAppHotkeys` and provide them with `provideAppHotkeys`. See the [App Hotkeys Guide](./guides/app-hotkeys). + ## Optional Alpine magic Install `hotkeysPlugin` before `Alpine.start()` to get an element-owned `$hotkeys` scope: @@ -169,8 +171,8 @@ The plugin destroys that scope when Alpine destroys the owning element. Choose a - [Hotkeys](./guides/hotkeys) - [Sequences](./guides/sequences) +- [App hotkeys](./guides/app-hotkeys) - [Hotkey recording](./guides/hotkey-recording) -- [Sequence recording](./guides/sequence-recording) - [Key state tracking](./guides/key-state-tracking) - [Formatting and display](./guides/formatting-display) - [Kitchen sink](./examples/kitchen-sink) diff --git a/docs/framework/alpine/reference/functions/createHotkeysScope.md b/docs/framework/alpine/reference/functions/createHotkeysScope.md index 24070a59..a8ac9ccd 100644 --- a/docs/framework/alpine/reference/functions/createHotkeysScope.md +++ b/docs/framework/alpine/reference/functions/createHotkeysScope.md @@ -7,9 +7,11 @@ title: createHotkeysScope function createHotkeysScope(defaultOptions?): AlpineHotkeys; ``` -Defined in: [createHotkeysScope.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/createHotkeysScope.ts#L18) +Defined in: [createHotkeysScope.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/createHotkeysScope.ts#L29) -Owns Alpine effects and Store subscriptions, with optional shared defaults. Call destroy from x-data's destroy hook. +Owns Alpine effects and Store subscriptions, with optional shared defaults. +App hotkeys come from `provideAppHotkeys`. Call destroy from x-data's +destroy hook. ## Parameters diff --git a/docs/framework/alpine/reference/index.md b/docs/framework/alpine/reference/index.md index a4eca07e..5eb20b1b 100644 --- a/docs/framework/alpine/reference/index.md +++ b/docs/framework/alpine/reference/index.md @@ -5,15 +5,22 @@ title: "@tanstack/alpine-hotkeys" ## Interfaces +- [AlpineCreateHotkey](interfaces/AlpineCreateHotkey.md) +- [AlpineCreateHotkeyRecorder](interfaces/AlpineCreateHotkeyRecorder.md) +- [AlpineCustomHotkeys](interfaces/AlpineCustomHotkeys.md) - [AlpineHotkeyRecorder](interfaces/AlpineHotkeyRecorder.md) - [AlpineHotkeys](interfaces/AlpineHotkeys.md) -- [AlpineHotkeySequenceRecorder](interfaces/AlpineHotkeySequenceRecorder.md) - [AlpineHotkeyState](interfaces/AlpineHotkeyState.md) - [DefaultHotkeysOptions](interfaces/DefaultHotkeysOptions.md) - [HotkeyRegistrationsResult](interfaces/HotkeyRegistrationsResult.md) ## Type Aliases +- [AlpineAppHotkeyOptions](type-aliases/AlpineAppHotkeyOptions.md) +- [AlpineHotkeyCommonOptions](type-aliases/AlpineHotkeyCommonOptions.md) +- [AlpineHotkeyDefinition](type-aliases/AlpineHotkeyDefinition.md) +- [AlpineHotkeyOptionsFor](type-aliases/AlpineHotkeyOptionsFor.md) +- [AlpineHotkeyTarget](type-aliases/AlpineHotkeyTarget.md) - [MaybeGetter](type-aliases/MaybeGetter.md) ## Functions diff --git a/docs/framework/alpine/reference/interfaces/AlpineCreateHotkey.md b/docs/framework/alpine/reference/interfaces/AlpineCreateHotkey.md new file mode 100644 index 00000000..c193b2e3 --- /dev/null +++ b/docs/framework/alpine/reference/interfaces/AlpineCreateHotkey.md @@ -0,0 +1,82 @@ +--- +id: AlpineCreateHotkey +title: AlpineCreateHotkey +--- + +Defined in: [types.ts:99](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L99) + +Registers a hotkey: a chord, a sequence, or an app hotkey. + +## Call Signature + +```ts +AlpineCreateHotkey( + hotkey, + callback, + options?): void; +``` + +Defined in: [types.ts:100](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L100) + +Registers a hotkey: a chord, a sequence, or an app hotkey. + +### Type Parameters + +#### T + +`T` *extends* `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\> + +### Parameters + +#### hotkey + +[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`T`\> + +#### callback + +`HotkeyCallback` + +#### options? + +[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<[`AlpineAppHotkeyOptions`](../type-aliases/AlpineAppHotkeyOptions.md)\<`T`\>\> + +### Returns + +`void` + +## Call Signature + +```ts +AlpineCreateHotkey( + hotkey, + callback, + options?): void; +``` + +Defined in: [types.ts:105](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L105) + +Registers a hotkey: a chord, a sequence, or an app hotkey. + +### Type Parameters + +#### T + +`T` *extends* `RegisterableHotkey` + +### Parameters + +#### hotkey + +[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`T`\> + +#### callback + +`HotkeyCallback` + +#### options? + +[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`AdapterOptionsFor`\<`T`, [`AlpineHotkeyTarget`](../type-aliases/AlpineHotkeyTarget.md)\>\> + +### Returns + +`void` diff --git a/docs/framework/alpine/reference/interfaces/AlpineCreateHotkeyRecorder.md b/docs/framework/alpine/reference/interfaces/AlpineCreateHotkeyRecorder.md new file mode 100644 index 00000000..2c5a0599 --- /dev/null +++ b/docs/framework/alpine/reference/interfaces/AlpineCreateHotkeyRecorder.md @@ -0,0 +1,108 @@ +--- +id: AlpineCreateHotkeyRecorder +title: AlpineCreateHotkeyRecorder +--- + +Defined in: [types.ts:113](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L113) + +Creates a recorder; `maxSteps` (default 1) selects chords or sequences. + +## Call Signature + +```ts +AlpineCreateHotkeyRecorder(options): AlpineHotkeyRecorder; +``` + +Defined in: [types.ts:114](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L114) + +Creates a recorder; `maxSteps` (default 1) selects chords or sequences. + +### Parameters + +#### options + +`HotkeyChordRecorderOptions` + +### Returns + +[`AlpineHotkeyRecorder`](AlpineHotkeyRecorder.md)\<`HotkeyChord`\> + +## Call Signature + +```ts +AlpineCreateHotkeyRecorder(options): AlpineHotkeyRecorder; +``` + +Defined in: [types.ts:115](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L115) + +Creates a recorder; `maxSteps` (default 1) selects chords or sequences. + +### Parameters + +#### options + +`HotkeySequenceRecorderOptions` + +### Returns + +[`AlpineHotkeyRecorder`](AlpineHotkeyRecorder.md) + +## Call Signature + +```ts +AlpineCreateHotkeyRecorder(options): AlpineHotkeyRecorder; +``` + +Defined in: [types.ts:119](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L119) + +Creates a recorder; `maxSteps` (default 1) selects chords or sequences. + +### Parameters + +#### options + +() => `HotkeySequenceRecorderOptions` + +### Returns + +[`AlpineHotkeyRecorder`](AlpineHotkeyRecorder.md) + +## Call Signature + +```ts +AlpineCreateHotkeyRecorder(options): AlpineHotkeyRecorder; +``` + +Defined in: [types.ts:120](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L120) + +Creates a recorder; `maxSteps` (default 1) selects chords or sequences. + +### Parameters + +#### options + +() => `HotkeyChordRecorderOptions` + +### Returns + +[`AlpineHotkeyRecorder`](AlpineHotkeyRecorder.md)\<`HotkeyChord`\> + +## Call Signature + +```ts +AlpineCreateHotkeyRecorder(options): AlpineHotkeyRecorder; +``` + +Defined in: [types.ts:121](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L121) + +Creates a recorder; `maxSteps` (default 1) selects chords or sequences. + +### Parameters + +#### options + +() => `HotkeyRecorderOptions` + +### Returns + +[`AlpineHotkeyRecorder`](AlpineHotkeyRecorder.md) diff --git a/docs/framework/alpine/reference/interfaces/AlpineCustomHotkeys.md b/docs/framework/alpine/reference/interfaces/AlpineCustomHotkeys.md new file mode 100644 index 00000000..d3ffc2ee --- /dev/null +++ b/docs/framework/alpine/reference/interfaces/AlpineCustomHotkeys.md @@ -0,0 +1,111 @@ +--- +id: AlpineCustomHotkeys +title: AlpineCustomHotkeys +--- + +Defined in: [types.ts:73](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L73) + +Custom hotkey state and actions. + +## Properties + +### findConflicts + +```ts +findConflicts: (id, hotkey) => AppHotkey[]; +``` + +Defined in: [types.ts:89](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L89) + +Other app hotkeys that would collide if `id` used `hotkey`. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] + +*** + +### resetCustomHotkey + +```ts +resetCustomHotkey: (id) => void; +``` + +Defined in: [types.ts:85](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L85) + +Restores the default hotkey for an app hotkey. + +#### Parameters + +##### id + +`string` + +#### Returns + +`void` + +*** + +### resetCustomHotkeys + +```ts +resetCustomHotkeys: () => void; +``` + +Defined in: [types.ts:87](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L87) + +Restores every default hotkey. + +#### Returns + +`void` + +*** + +### setCustomHotkey + +```ts +setCustomHotkey: (id, hotkey) => AppHotkey[]; +``` + +Defined in: [types.ts:80](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L80) + +Sets the user's hotkey for an app hotkey. `null` unbinds it. +Returns conflicting app hotkeys; when any exist, nothing changes. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` \| `null` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] + +*** + +### value + +```ts +readonly value: CustomHotkeys; +``` + +Defined in: [types.ts:75](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L75) + +The user's hotkeys that differ from the defaults. diff --git a/docs/framework/alpine/reference/interfaces/AlpineHotkeyRecorder.md b/docs/framework/alpine/reference/interfaces/AlpineHotkeyRecorder.md index 64f89916..a63238b1 100644 --- a/docs/framework/alpine/reference/interfaces/AlpineHotkeyRecorder.md +++ b/docs/framework/alpine/reference/interfaces/AlpineHotkeyRecorder.md @@ -3,10 +3,16 @@ id: AlpineHotkeyRecorder title: AlpineHotkeyRecorder --- -Defined in: [types.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L26) +Defined in: [types.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L49) Recording state and controls owned by the containing scope. +## Type Parameters + +### THotkey + +`THotkey` *extends* `Hotkey` = `Hotkey` + ## Properties ### cancelRecording @@ -15,7 +21,7 @@ Recording state and controls owned by the containing scope. cancelRecording: () => void; ``` -Defined in: [types.ts:36](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L36) +Defined in: [types.ts:61](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L61) Stop, clear recorder state, and call onCancel. @@ -25,13 +31,29 @@ Stop, clear recorder state, and call onCancel. *** +### commitRecording + +```ts +commitRecording: () => void; +``` + +Defined in: [types.ts:63](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L63) + +Commit the current steps (multi-step recordings). + +#### Returns + +`void` + +*** + ### isRecording ```ts readonly isRecording: boolean; ``` -Defined in: [types.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L28) +Defined in: [types.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L51) Whether the recorder is listening for a shortcut. @@ -40,12 +62,12 @@ Whether the recorder is listening for a shortcut. ### recordedHotkey ```ts -readonly recordedHotkey: Hotkey | null; +readonly recordedHotkey: THotkey | null; ``` -Defined in: [types.ts:30](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L30) +Defined in: [types.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L55) -The last recorded binding, including brackets for physical codes. +The last committed hotkey, including brackets for physical codes. *** @@ -55,7 +77,7 @@ The last recorded binding, including brackets for physical codes. startRecording: () => void; ``` -Defined in: [types.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L32) +Defined in: [types.ts:57](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L57) Start a new recording session. @@ -65,13 +87,25 @@ Start a new recording session. *** +### steps + +```ts +readonly steps: HotkeySequence; +``` + +Defined in: [types.ts:53](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L53) + +Chords captured so far in a multi-step recording. + +*** + ### stopRecording ```ts stopRecording: () => void; ``` -Defined in: [types.ts:34](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L34) +Defined in: [types.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L59) Stop and clear recorder state without calling onRecord or onCancel. diff --git a/docs/framework/alpine/reference/interfaces/AlpineHotkeySequenceRecorder.md b/docs/framework/alpine/reference/interfaces/AlpineHotkeySequenceRecorder.md deleted file mode 100644 index 4827a87c..00000000 --- a/docs/framework/alpine/reference/interfaces/AlpineHotkeySequenceRecorder.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -id: AlpineHotkeySequenceRecorder -title: AlpineHotkeySequenceRecorder ---- - -Defined in: [types.ts:40](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L40) - -Sequence recording state and controls. - -## Properties - -### cancelRecording - -```ts -cancelRecording: () => void; -``` - -Defined in: [types.ts:52](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L52) - -Discard the current session and call onCancel. - -#### Returns - -`void` - -*** - -### commitRecording - -```ts -commitRecording: () => void; -``` - -Defined in: [types.ts:54](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L54) - -Commit the current steps. Does nothing when no steps are recorded. - -#### Returns - -`void` - -*** - -### isRecording - -```ts -readonly isRecording: boolean; -``` - -Defined in: [types.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L42) - -Whether the recorder is listening for chords. - -*** - -### recordedSequence - -```ts -readonly recordedSequence: HotkeySequence | null; -``` - -Defined in: [types.ts:46](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L46) - -The last committed sequence. - -*** - -### startRecording - -```ts -startRecording: () => void; -``` - -Defined in: [types.ts:48](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L48) - -Start a new recording session. - -#### Returns - -`void` - -*** - -### steps - -```ts -readonly steps: HotkeySequence; -``` - -Defined in: [types.ts:44](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L44) - -Chords captured in the current session. - -*** - -### stopRecording - -```ts -stopRecording: () => void; -``` - -Defined in: [types.ts:50](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L50) - -Stop without committing or calling onCancel. - -#### Returns - -`void` diff --git a/docs/framework/alpine/reference/interfaces/AlpineHotkeyState.md b/docs/framework/alpine/reference/interfaces/AlpineHotkeyState.md index 299d9838..247dc720 100644 --- a/docs/framework/alpine/reference/interfaces/AlpineHotkeyState.md +++ b/docs/framework/alpine/reference/interfaces/AlpineHotkeyState.md @@ -3,7 +3,7 @@ id: AlpineHotkeyState title: AlpineHotkeyState --- -Defined in: [types.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L21) +Defined in: [types.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L27) A reactive value. Read `.value` where the framework tracks dependencies. @@ -21,4 +21,4 @@ A reactive value. Read `.value` where the framework tracks dependencies. readonly value: T; ``` -Defined in: [types.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L22) +Defined in: [types.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L28) diff --git a/docs/framework/alpine/reference/interfaces/AlpineHotkeys.md b/docs/framework/alpine/reference/interfaces/AlpineHotkeys.md index 2b5d3b49..fc48cb69 100644 --- a/docs/framework/alpine/reference/interfaces/AlpineHotkeys.md +++ b/docs/framework/alpine/reference/interfaces/AlpineHotkeys.md @@ -3,235 +3,180 @@ id: AlpineHotkeys title: AlpineHotkeys --- -Defined in: [types.ts:67](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L67) +Defined in: [types.ts:125](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L125) Registrations, recorders, and reactive state owned by one Alpine component. ## Properties -### createHeldKeyCodes +### createAppHotkeyList ```ts -createHeldKeyCodes: () => AlpineHotkeyState>; +createAppHotkeyList: () => AlpineHotkeyState>[]>; ``` -Defined in: [types.ts:93](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L93) +Defined in: [types.ts:154](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L154) -Read the mapping of held logical names to physical codes through `.value`. +Read every app hotkey with its live status through `.value`. #### Returns -[`AlpineHotkeyState`](AlpineHotkeyState.md)\<`Record`\<`string`, `string`\>\> +[`AlpineHotkeyState`](AlpineHotkeyState.md)\<`AppHotkeyListItem`\<`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>[]\> *** -### createHeldKeys +### createAppHotkeys ```ts -createHeldKeys: () => AlpineHotkeyState; +createAppHotkeys: () => AlpineHotkeyState>>>; ``` -Defined in: [types.ts:91](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L91) +Defined in: [types.ts:152](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L152) -Read held logical key names through `.value`. +Read the effective app hotkeys from `provideAppHotkeys` through `.value`. #### Returns -[`AlpineHotkeyState`](AlpineHotkeyState.md)\<`string`[]\> +[`AlpineHotkeyState`](AlpineHotkeyState.md)\<`Readonly`\<`Record`\<`string`, `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>\>\> *** -### createHotkey +### createCustomHotkeys ```ts -createHotkey: (hotkey, callback, options?) => void; +createCustomHotkeys: () => AlpineCustomHotkeys; ``` -Defined in: [types.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L69) - -Register one shortcut. Getters track changing bindings and options. - -#### Parameters - -##### hotkey +Defined in: [types.ts:158](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L158) -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`RegisterableHotkey`\> - -##### callback - -`HotkeyCallback` - -##### options? - -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`HotkeyOptions`\> +Read and change the user's custom hotkeys. #### Returns -`void` +[`AlpineCustomHotkeys`](AlpineCustomHotkeys.md) *** -### createHotkeyHint +### createHeldKeyCodes ```ts -createHotkeyHint: (hotkey, options?) => AlpineHotkeyState; +createHeldKeyCodes: () => AlpineHotkeyState>; ``` -Defined in: [types.ts:97](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L97) - -Read whether held modifiers reveal a binding through `.value`. +Defined in: [types.ts:139](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L139) -#### Parameters - -##### hotkey - -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`RegisterableHotkey`\> - -##### options? - -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`HeldModifierOptions`\> +Read the mapping of held logical names to physical codes through `.value`. #### Returns -[`AlpineHotkeyState`](AlpineHotkeyState.md)\<`boolean`\> +[`AlpineHotkeyState`](AlpineHotkeyState.md)\<`Record`\<`string`, `string`\>\> *** -### createHotkeyRecorder +### createHeldKeys ```ts -createHotkeyRecorder: (options) => AlpineHotkeyRecorder; +createHeldKeys: () => AlpineHotkeyState; ``` -Defined in: [types.ts:104](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L104) - -Create a recorder whose options can follow Alpine state. - -#### Parameters - -##### options +Defined in: [types.ts:137](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L137) -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`HotkeyRecorderOptions`\> +Read held logical key names through `.value`. #### Returns -[`AlpineHotkeyRecorder`](AlpineHotkeyRecorder.md) +[`AlpineHotkeyState`](AlpineHotkeyState.md)\<`string`[]\> *** -### createHotkeyRegistrations +### createHotkey ```ts -createHotkeyRegistrations: () => HotkeyRegistrationsResult; +createHotkey: AlpineCreateHotkey; ``` -Defined in: [types.ts:102](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L102) +Defined in: [types.ts:130](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L130) -Read live hotkey and sequence registrations, including disabled entries. - -#### Returns - -[`HotkeyRegistrationsResult`](HotkeyRegistrationsResult.md) +Register one hotkey: a chord such as `'Mod+S'`, a sequence such as +`['G', 'G']`, or an app hotkey. Getters track changing hotkeys and options. *** -### createHotkeys +### createHotkeyHint ```ts -createHotkeys: (definitions, options?) => void; +createHotkeyHint: (hotkey, options?) => AlpineHotkeyState; ``` -Defined in: [types.ts:75](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L75) +Defined in: [types.ts:143](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L143) -Reconcile a list of shortcuts. Definition options override common options. +Read whether held modifiers reveal a hotkey through `.value`. #### Parameters -##### definitions +##### hotkey -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`HotkeyDefinition`[]\> +[`MaybeGetter`](../type-aliases/MaybeGetter.md)\< + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\>\> ##### options? -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`HotkeyOptions`\> +[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`HeldModifierOptions`\> #### Returns -`void` +[`AlpineHotkeyState`](AlpineHotkeyState.md)\<`boolean`\> *** -### createHotkeySequence +### createHotkeyRecorder ```ts -createHotkeySequence: (sequence, callback, options?) => void; +createHotkeyRecorder: AlpineCreateHotkeyRecorder; ``` -Defined in: [types.ts:80](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L80) +Defined in: [types.ts:150](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L150) -Register consecutive chords with an optional timeout and element target. - -#### Parameters - -##### sequence - -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`HotkeySequence`\> - -##### callback - -`HotkeyCallback` - -##### options? - -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`SequenceOptions`\> - -#### Returns - -`void` +Create a recorder whose options can follow Alpine state. *** -### createHotkeySequenceRecorder +### createHotkeyRegistrations ```ts -createHotkeySequenceRecorder: (options) => AlpineHotkeySequenceRecorder; +createHotkeyRegistrations: () => HotkeyRegistrationsResult; ``` -Defined in: [types.ts:108](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L108) - -Create a sequence recorder with live steps and explicit commit controls. - -#### Parameters - -##### options +Defined in: [types.ts:148](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L148) -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`HotkeySequenceRecorderOptions`\> +Read live registrations, including disabled entries. #### Returns -[`AlpineHotkeySequenceRecorder`](AlpineHotkeySequenceRecorder.md) +[`HotkeyRegistrationsResult`](HotkeyRegistrationsResult.md) *** -### createHotkeySequences +### createHotkeys ```ts -createHotkeySequences: (definitions, options?) => void; +createHotkeys: (definitions, options?) => void; ``` -Defined in: [types.ts:86](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L86) +Defined in: [types.ts:132](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L132) -Reconcile a changing list of sequences. +Reconcile a list of hotkeys. Definition options override common options. #### Parameters ##### definitions -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`HotkeySequenceDefinition`[]\> +[`MaybeGetter`](../type-aliases/MaybeGetter.md)\ ##### options? -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`SequenceOptions`\> +[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<[`AlpineHotkeyCommonOptions`](../type-aliases/AlpineHotkeyCommonOptions.md)\> #### Returns @@ -245,7 +190,7 @@ Reconcile a changing list of sequences. createKeyHold: (key) => AlpineHotkeyState; ``` -Defined in: [types.ts:95](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L95) +Defined in: [types.ts:141](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L141) Read whether a key is held through `.value`. @@ -267,7 +212,7 @@ Read whether a key is held through `.value`. destroy: () => void; ``` -Defined in: [types.ts:112](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L112) +Defined in: [types.ts:160](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L160) Release every registration, subscription, effect, and recorder. Idempotent. diff --git a/docs/framework/alpine/reference/interfaces/DefaultHotkeysOptions.md b/docs/framework/alpine/reference/interfaces/DefaultHotkeysOptions.md index 7fab85fd..65b6e7fe 100644 --- a/docs/framework/alpine/reference/interfaces/DefaultHotkeysOptions.md +++ b/docs/framework/alpine/reference/interfaces/DefaultHotkeysOptions.md @@ -3,7 +3,7 @@ id: DefaultHotkeysOptions title: DefaultHotkeysOptions --- -Defined in: [types.ts:116](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L116) +Defined in: [types.ts:164](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L164) Shared defaults; call-specific options take precedence. @@ -12,37 +12,21 @@ Shared defaults; call-specific options take precedence. ### hotkey? ```ts -optional hotkey?: HotkeyOptions; +optional hotkey?: AlpineHotkeyCommonOptions; ``` -Defined in: [types.ts:117](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L117) +Defined in: [types.ts:166](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L166) -*** - -### hotkeyRecorder? - -```ts -optional hotkeyRecorder?: Partial; -``` - -Defined in: [types.ts:119](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L119) +Defaults for every hotkey. `requireReset` applies to chords and `timeout` to sequences. *** -### hotkeySequence? +### hotkeyRecorder? ```ts -optional hotkeySequence?: SequenceOptions; +optional hotkeyRecorder?: Partial>; ``` -Defined in: [types.ts:118](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L118) - -*** - -### hotkeySequenceRecorder? - -```ts -optional hotkeySequenceRecorder?: Partial; -``` +Defined in: [types.ts:168](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L168) -Defined in: [types.ts:120](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L120) +Defaults for every recorder. diff --git a/docs/framework/alpine/reference/interfaces/HotkeyRegistrationsResult.md b/docs/framework/alpine/reference/interfaces/HotkeyRegistrationsResult.md index cbd1a88e..426fca72 100644 --- a/docs/framework/alpine/reference/interfaces/HotkeyRegistrationsResult.md +++ b/docs/framework/alpine/reference/interfaces/HotkeyRegistrationsResult.md @@ -3,9 +3,9 @@ id: HotkeyRegistrationsResult title: HotkeyRegistrationsResult --- -Defined in: [types.ts:58](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L58) +Defined in: [types.ts:67](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L67) -Live registration views from the shared hotkey and sequence managers. +Live registration views from the shared hotkey manager. ## Properties @@ -15,14 +15,6 @@ Live registration views from the shared hotkey and sequence managers. readonly hotkeys: HotkeyRegistrationView[]; ``` -Defined in: [types.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L59) +Defined in: [types.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L69) -*** - -### sequences - -```ts -readonly sequences: SequenceRegistrationView[]; -``` - -Defined in: [types.ts:60](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L60) +Every registration, chords and sequences. Narrow each view with `kind`. diff --git a/docs/framework/alpine/reference/type-aliases/AlpineAppHotkeyOptions.md b/docs/framework/alpine/reference/type-aliases/AlpineAppHotkeyOptions.md new file mode 100644 index 00000000..4ec8db62 --- /dev/null +++ b/docs/framework/alpine/reference/type-aliases/AlpineAppHotkeyOptions.md @@ -0,0 +1,18 @@ +--- +id: AlpineAppHotkeyOptions +title: AlpineAppHotkeyOptions +--- + +```ts +type AlpineAppHotkeyOptions = AdapterAppHotkeyOptions; +``` + +Defined in: [types.ts:45](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L45) + +Call-site options for an app hotkey, typed by its declared default kind. + +## Type Parameters + +### T + +`T` *extends* `AppHotkey` = `AppHotkey` diff --git a/docs/framework/alpine/reference/type-aliases/AlpineHotkeyCommonOptions.md b/docs/framework/alpine/reference/type-aliases/AlpineHotkeyCommonOptions.md new file mode 100644 index 00000000..13c017b3 --- /dev/null +++ b/docs/framework/alpine/reference/type-aliases/AlpineHotkeyCommonOptions.md @@ -0,0 +1,12 @@ +--- +id: AlpineHotkeyCommonOptions +title: AlpineHotkeyCommonOptions +--- + +```ts +type AlpineHotkeyCommonOptions = AdapterCommonOptions; +``` + +Defined in: [types.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L38) + +Options for every kind; each applies to the kinds that support it. diff --git a/docs/framework/alpine/reference/type-aliases/AlpineHotkeyDefinition.md b/docs/framework/alpine/reference/type-aliases/AlpineHotkeyDefinition.md new file mode 100644 index 00000000..011b12da --- /dev/null +++ b/docs/framework/alpine/reference/type-aliases/AlpineHotkeyDefinition.md @@ -0,0 +1,12 @@ +--- +id: AlpineHotkeyDefinition +title: AlpineHotkeyDefinition +--- + +```ts +type AlpineHotkeyDefinition = AdapterHotkeyDefinition; +``` + +Defined in: [types.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L35) + +One hotkey for `createHotkeys`: a chord, a sequence, or an app hotkey. diff --git a/docs/framework/alpine/reference/type-aliases/AlpineHotkeyOptionsFor.md b/docs/framework/alpine/reference/type-aliases/AlpineHotkeyOptionsFor.md new file mode 100644 index 00000000..7f799a75 --- /dev/null +++ b/docs/framework/alpine/reference/type-aliases/AlpineHotkeyOptionsFor.md @@ -0,0 +1,18 @@ +--- +id: AlpineHotkeyOptionsFor +title: AlpineHotkeyOptionsFor +--- + +```ts +type AlpineHotkeyOptionsFor = AdapterOptionsFor; +``` + +Defined in: [types.ts:41](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L41) + +The options type for a hotkey: chord options for chords, sequence options for sequences. + +## Type Parameters + +### T + +`T` *extends* `RegisterableHotkey` diff --git a/docs/framework/alpine/reference/type-aliases/AlpineHotkeyTarget.md b/docs/framework/alpine/reference/type-aliases/AlpineHotkeyTarget.md new file mode 100644 index 00000000..fe78773b --- /dev/null +++ b/docs/framework/alpine/reference/type-aliases/AlpineHotkeyTarget.md @@ -0,0 +1,12 @@ +--- +id: AlpineHotkeyTarget +title: AlpineHotkeyTarget +--- + +```ts +type AlpineHotkeyTarget = Target | null; +``` + +Defined in: [types.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L32) + +The element to listen on. A `null` target skips registration until it is set. diff --git a/docs/framework/alpine/reference/type-aliases/MaybeGetter.md b/docs/framework/alpine/reference/type-aliases/MaybeGetter.md index 387bf384..4c17d0e9 100644 --- a/docs/framework/alpine/reference/type-aliases/MaybeGetter.md +++ b/docs/framework/alpine/reference/type-aliases/MaybeGetter.md @@ -7,7 +7,7 @@ title: MaybeGetter type MaybeGetter = T | (() => T); ``` -Defined in: [types.ts:64](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L64) +Defined in: [types.ts:96](https://github.com/TanStack/hotkeys/blob/main/packages/alpine-hotkeys/src/types.ts#L96) A value or a getter evaluated within an Alpine effect or reactive read. diff --git a/docs/framework/angular/guides/app-hotkeys.md b/docs/framework/angular/guides/app-hotkeys.md new file mode 100644 index 00000000..a2b5dd74 --- /dev/null +++ b/docs/framework/angular/guides/app-hotkeys.md @@ -0,0 +1,205 @@ +--- +title: App Hotkeys Guide +id: app-hotkeys +--- + +`injectHotkey` and `injectHotkeys` register a hotkey while its component is mounted. A cheat sheet built from live registrations therefore misses hotkeys on routes that have not rendered yet, and a settings screen has nowhere to change them. App hotkeys solve both: you declare every hotkey in one place, pass the declarations to `provideHotkeys`, and attach callbacks where the work happens. Users can then customize any of them, and your app decides how to save their choices. + +A declaration holds a hotkey's id, default hotkey, metadata, and options. It never holds a callback; `injectHotkey` and `injectHotkeys` still attach the callbacks. + +## Declare app hotkeys + +Use `defineAppHotkeys` in a module that contains only declarations. Keep it free of components and handlers, because it loads with your app shell: + +```ts +// app-hotkeys.ts +import { defineAppHotkeys } from '@tanstack/angular-hotkeys' + +export const appHotkeys = defineAppHotkeys({ + 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save', group: 'Document' } }, + 'doc.find': { + hotkey: 'Mod+F', + meta: { name: 'Find', group: 'Document' }, + context: 'editor', + }, + 'nav.home': { + hotkey: ['G', 'H'], + meta: { name: 'Go home', group: 'Navigation' }, + options: { timeout: 800 }, + }, + 'palette.open': { hotkey: null, meta: { name: 'Command palette' } }, +}) + +declare module '@tanstack/angular-hotkeys' { + interface Register { + appHotkeys: typeof appHotkeys + } +} +``` + +- The object key is the app hotkey's id. Treat ids as a stable contract, because saved custom hotkeys use them as keys. +- `hotkey` is the default: a chord, a sequence, or `null` for an action without a default. An unbound app hotkey registers nothing until the user assigns one. +- `options` describe the hotkey itself: `conflictBehavior`, `eventType`, `ignoreInputs`, `preventDefault`, `stopPropagation`, plus `requireReset` for a chord default or `timeout` for a sequence default. Options that don't match the default's kind are type errors. Pass `target` and `enabled` where you register the hotkey. +- `context` labels where a hotkey applies, such as `'editor'`. App hotkeys with different contexts may share a hotkey. It is used only for conflict checks and does not affect matching. + +The `Register` augmentation types the app hotkeys you read back, the ids accepted when changing custom hotkeys, and the `CustomHotkeys` type. + +## Provide app hotkeys + +```ts +import { provideHotkeys } from '@tanstack/angular-hotkeys' +import { appHotkeys } from './app-hotkeys' + +bootstrapApplication(AppComponent, { + providers: [provideHotkeys({ appHotkeys })], +}) +``` + +## Register app hotkeys + +Pass an app hotkey wherever you pass a hotkey: + +```ts +import { Component, signal } from '@angular/core' +import { injectAppHotkeys, injectHotkey, injectHotkeys } from '@tanstack/angular-hotkeys' + +@Component({ selector: 'app-editor', template: `...` }) +export class EditorComponent { + readonly hotkeys = injectAppHotkeys() + readonly isDirty = signal(false) + + constructor() { + injectHotkey(() => this.hotkeys()['doc.save'], () => this.save(), () => ({ + enabled: this.isDirty(), + })) + injectHotkeys(() => [ + { hotkey: this.hotkeys()['nav.home'], callback: () => this.goHome() }, + { hotkey: 'Escape', callback: () => this.close() }, + ]) + } +} +``` + +`injectAppHotkeys()` returns a signal. You can also import an app hotkey directly, such as `injectHotkey(appHotkeys['doc.save'], save)`. Without provided app hotkeys, the declared default is used. + +The registration takes `meta` and `options` from the declaration. Options merge in this order: default options, then declared options, then call-site options. `meta` comes only from the declaration, so the cheat sheet and the registration cannot disagree. Call-site options are typed by the declared default's kind, so `timeout` is accepted for `nav.home` but not for `doc.save`. + +When the user changes a hotkey, registered app hotkeys move to the new hotkey immediately, even if it changes from a chord to a sequence. + +## Build a cheat sheet + +`injectAppHotkeyList` returns every app hotkey in declaration order, with its live status: + +| Status | Meaning | +| --- | --- | +| `'active'` | At least one enabled registration | +| `'disabled'` | Registered, but every registration has `enabled: false` | +| `'inactive'` | Has a hotkey, but nothing has registered it | +| `'unbound'` | No hotkey assigned | + +```ts +import { Component } from '@angular/core' +import { formatForDisplay, injectAppHotkeyList } from '@tanstack/angular-hotkeys' + +@Component({ + selector: 'app-cheat-sheet', + template: ` +
    + @for (item of items(); track item.appHotkey.id) { +
  • + {{ item.appHotkey.meta?.name }}: + {{ item.appHotkey.hotkey ? format(item.appHotkey.hotkey) : 'Unassigned' }} + @if (item.isCustom) { (custom) } +
  • + } +
+ `, +}) +export class CheatSheetComponent { + readonly items = injectAppHotkeyList() + readonly format = formatForDisplay +} +``` + +An app hotkey registered by many components, such as one per list row, appears once. Its `registrations` array holds each live registration. Devtools also list declared app hotkeys that are not registered. + +## Customize and persist hotkeys + +TanStack Hotkeys does not store anything. Pass the user's saved custom hotkeys in and save them when they change: + +```ts +import { provideHotkeys } from '@tanstack/angular-hotkeys' + +bootstrapApplication(AppComponent, { + providers: [ + provideHotkeys({ + appHotkeys, + customHotkeys: JSON.parse(localStorage.getItem('hotkeys') ?? '{}'), + onCustomHotkeysChange: (custom) => + localStorage.setItem('hotkeys', JSON.stringify(custom)), + }), + ], +}) +``` + +`provideHotkeys` can also be added to a component's `providers` to scope app hotkeys and defaults to that subtree. + +- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them. +- `customHotkeys` is read once; afterwards the shared store owns the state. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning. +- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile. + +Recorded hotkeys use `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others. + +### Change hotkeys + +`injectCustomHotkeys` reads and changes the user's custom hotkeys: + +```ts +import { Component, computed, input } from '@angular/core' +import { + formatForDisplay, + injectAppHotkeys, + injectCustomHotkeys, + injectHotkeyRecorder, +} from '@tanstack/angular-hotkeys' +import type { AppHotkeyId } from '@tanstack/angular-hotkeys' + +@Component({ + selector: 'app-shortcut-row', + template: ` + {{ appHotkey().meta?.name }}: + {{ appHotkey().hotkey ? format(appHotkey().hotkey!) : 'Unassigned' }} + + + `, +}) +export class ShortcutRowComponent { + readonly id = input.required() + readonly hotkeys = injectAppHotkeys() + readonly appHotkey = computed(() => this.hotkeys()[this.id()]) + readonly custom = injectCustomHotkeys() + readonly format = formatForDisplay + + readonly recorder = injectHotkeyRecorder(() => ({ + maxSteps: 3, + validate: (hotkey) => { + const conflicts = this.custom.findConflicts(this.id(), hotkey) + return conflicts.length === 0 || `Used by ${conflicts[0]?.meta?.name}` + }, + onRecord: (hotkey) => this.custom.setCustomHotkey(this.id(), hotkey), + onClear: () => this.custom.setCustomHotkey(this.id(), null), + })) +} +``` + +| Member | Description | +| --- | --- | +| `customHotkeys()` | The hotkeys that differ from the defaults | +| `setCustomHotkey(id, hotkey)` | Sets a hotkey, or unbinds with `null`. Returns conflicting app hotkeys; when there are any, nothing changes. | +| `findConflicts(id, hotkey)` | App hotkeys that would collide if `id` used `hotkey` | +| `resetCustomHotkey(id)` | Restores one default | +| `resetCustomHotkeys()` | Restores every default | + +Two app hotkeys conflict when their contexts match or either has no `context`, and their hotkeys collide: equal chords, or one sequence starts with the other, including a sequence whose first step equals a chord. Conflicts are checked across every declaration, whether or not it is registered. + +Each app hotkey's `defaultHotkey` holds its declared default, and each cheat sheet item's `isCustom` reports whether the effective hotkey differs from it. diff --git a/docs/framework/angular/guides/formatting-display.md b/docs/framework/angular/guides/formatting-display.md index 7f4fb163..130cdeef 100644 --- a/docs/framework/angular/guides/formatting-display.md +++ b/docs/framework/angular/guides/formatting-display.md @@ -3,7 +3,7 @@ title: Formatting & Display Guide id: formatting-display --- -Use `formatForDisplay` whenever a binding appears in a menu, button, hint, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, and parsed bindings. Store the original binding and format it at render time: the display label is not a registration string. +Use `formatForDisplay` whenever a hotkey appears in a menu, button, hint, cheat sheet, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, parsed bindings, and sequences. Store the original binding and format it at render time: the display label is not a registration string. ## Format a binding @@ -32,16 +32,18 @@ const parts = formatForDisplay(binding, { platform: 'mac', parts: true }) With `parts` omitted or false, the result is a string. A runtime boolean returns `string | string[]`. Parts preserve literal plus keys and ignore `separatorToken`. -Sequences are arrays of bindings. Format their steps individually: +## Format sequences -```ts -import type { HotkeySequence } from '@tanstack/angular-hotkeys' +Pass a sequence to format each step and join the steps with `sequenceSeparator`, a space by default: -const sequence: HotkeySequence = ['Mod+[KeyK]', 'C'] -const label = sequence.map((step) => formatForDisplay(step)).join(' → ') +```ts +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac' }) // '⌘ K C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' → ' }) // 'G → G' +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac', parts: true }) +// [['⌘', 'K'], ['C']] — one array of keycaps per step ``` -`formatHotkeySequence` only joins stored strings with spaces. It intentionally retains brackets and code names, so use the code above for user-facing labels. +With `parts: true`, a sequence returns one array per step, so you can render each step as a group of keycaps. ## Choose symbols and separators @@ -63,7 +65,7 @@ formatForDisplay('Control++', { }) // 'Ctrl · +' ``` -An empty separator joins labels directly. `undefined` or `null` uses the platform default. `formatWithLabels(binding, options)` is the shorthand for `formatForDisplay` with `useSymbols: false`. +An empty separator joins labels directly. `undefined` or `null` uses the platform default. `separatorToken` applies within a chord; `sequenceSeparator` applies between steps. ## Supply layout labels @@ -88,6 +90,10 @@ formatForDisplay('Mod+[KeyQ]', { The precedence is `keyLabels`, then a layout entry, then the fallback label. Layout entries receive normal letter casing and key symbols; missing or empty entries fall back. Explicit labels are final. All of these options affect display only. +## Compare hotkeys + +`areHotkeysEqual(a, b)` compares chords or sequences after resolving modifiers, so `Mod+S` equals `Meta+S` on macOS. Sequences compare step by step. `isHotkeySequence` narrows a `Hotkey` to a sequence, and `simplifyHotkey` turns a one-step sequence into its chord. + ## Parse and store bindings `parseHotkey` returns either a logical `key` or a physical `code`, plus resolved modifier flags. Narrow the union before inspecting the identity: @@ -105,6 +111,6 @@ formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S' Parsed modifiers are already resolved. To display a portable `Mod` binding on another platform, serialize with the original platform first, as above. `normalizeRegisterableHotkey` accepts strings or raw objects and preserves the logical/physical distinction. Do not store display labels or put a bracketed code in a logical `key` field. -Use `validateHotkey` when accepting strings from an external source. It returns `valid`, `errors`, and `warnings`; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). +Use `validateHotkey` when accepting a chord or sequence from an external source. It returns `valid`, `errors`, and `warnings`, and names the failing step of a sequence; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). Try these options together in the [vanilla formatter playground](../../../framework/vanilla/examples/formatForDisplay). diff --git a/docs/framework/angular/guides/hotkey-recording.md b/docs/framework/angular/guides/hotkey-recording.md index 4f386b32..19d5236e 100644 --- a/docs/framework/angular/guides/hotkey-recording.md +++ b/docs/framework/angular/guides/hotkey-recording.md @@ -3,121 +3,139 @@ title: Hotkey Recording Guide id: hotkey-recording --- -TanStack Hotkeys provides the `injectHotkeyRecorder` API for building shortcut customization UIs in Angular. +`injectHotkeyRecorder` is for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the key combination they want, the same way system preferences or IDE shortcut editors work. By default it records one chord; set `maxSteps` to let users record a [sequence](./sequences) too. Recorders default to physical codes: recording a shortcut stores a string such as `Mod+[KeyS]`. Pass it directly to your hotkey registration and use `formatForDisplay` for the label. Set `recordBy: 'key'` when you intentionally want the produced character instead. -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Allow the framework to run its effect or watcher before relying on the update. +TanStack Hotkeys automatically suppresses registered hotkey callbacks while any recorder is active. You do not need to disable hotkeys while recording. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. ## Basic usage ```ts import { Component } from '@angular/core' -import { - formatForDisplay, - injectHotkeyRecorder, -} from '@tanstack/angular-hotkeys' +import { formatForDisplay, injectHotkeyRecorder } from '@tanstack/angular-hotkeys' @Component({ - standalone: true, + selector: 'app-shortcut-recorder', template: ` - - @if (recorder.isRecording()) { - - } `, }) export class ShortcutRecorderComponent { - readonly formatForDisplay = formatForDisplay + readonly format = formatForDisplay readonly recorder = injectHotkeyRecorder({ - onRecord: (hotkey) => { - console.log('Recorded:', hotkey) - }, + onRecord: (hotkey) => console.log('Recorded:', hotkey), // e.g. 'Mod+Shift+[KeyS]' }) } ``` -## Return value +## Recorder state and actions + +| Member | Description | +| --- | --- | +| `isRecording` | Whether the recorder is currently listening for key presses | +| `steps` | Chords captured so far in a multi-step recording | +| `recordedHotkey` | The last recorded hotkey, or `null` if nothing recorded | +| `startRecording()` | Start listening for key presses | +| `stopRecording()` | Stop listening without calling `onRecord` or `onCancel` | +| `cancelRecording()` | Stop listening and call `onCancel` | +| `commitRecording()` | Commit the current `steps` now (multi-step recordings) | -- `isRecording()`: Angular signal getter indicating whether recording is active -- `recordedHotkey()`: Angular signal getter with the most recently recorded hotkey -- `startRecording()`: start listening for key presses -- `stopRecording()`: stop listening and reset recorder state without calling `onRecord` -- `cancelRecording()`: stop listening and discard the in-progress recording +State members are signals: call `isRecording()`, `steps()`, and `recordedHotkey()`. ## Options ### `recordBy` -Recorders default to `recordBy: 'code'`. Option+S producing `ß` on macOS records `Alt+[KeyS]`; Option+2 producing `™` records `Alt+[Digit2]`. The stored brackets preserve physical identity through state, serialization, and registration; `formatForDisplay` produces a readable label. There is no code-to-letter conversion. Existing authored strings such as `Mod+S` remain logical bindings. +Recorders default to `recordBy: 'code'`. Option+S producing `ß` on macOS records `Alt+[KeyS]`; Option+2 producing `™` records `Alt+[Digit2]`. The stored brackets preserve physical identity through state, serialization, and registration; `formatForDisplay` produces a readable label. Existing authored strings such as `Mod+S` remain logical hotkeys. -Set `recordBy: 'key'` to intentionally record the produced logical character. A code-mode event with no usable code is rejected rather than silently switching modes. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the produced character without synthetic Control/Alt while retaining Shift. Recording events, repeats, and their releases do not trigger registered hotkeys or sequences. +Set `recordBy: 'key'` to intentionally record the produced logical character. A code-mode event with no usable code is rejected rather than silently switching modes. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the produced character without synthetic Control/Alt while retaining Shift. -```ts -injectHotkeyRecorder({ - recordBy: 'code', // default; choose 'key' for logical characters - onRecord: (hotkey) => {}, - onCancel: () => {}, - onClear: () => {}, -}) -``` +### `onRecord` + +Called when a recording commits. With the default `maxSteps: 1`, it receives a chord string such as `'Mod+[KeyS]'` as soon as the user presses a valid combination (a modifier + a non-modifier key, or a single non-modifier key). With a larger `maxSteps`, it receives a chord when one step was recorded, or a sequence otherwise. + +### `onCancel` + +Called when recording is cancelled, by pressing Escape or calling `cancelRecording()`. + +### `onClear` -## Global defaults via provider +Called when the recording is cleared: Backspace or Delete with no modifiers and no recorded steps. Clearing calls only `onClear`; your app decides whether that removes a hotkey or restores a default. + +### `ignoreInputs` + +Defaults to `true`. The recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Escape still cancels recording even while an input is focused. Set `ignoreInputs: false` to record from inside input elements. + +Options can also be a getter, such as `injectHotkeyRecorder(() => ({ maxSteps: 3, recordBy: this.mode(), onRecord }))`, so recording policy can follow signals. + +### Default options + +Set defaults for every recorder with `provideHotkeys`. Per-recorder options override them. ```ts -import { ApplicationConfig } from '@angular/core' -import { provideHotkeys } from '@tanstack/angular-hotkeys' - -export const appConfig: ApplicationConfig = { - providers: [ - provideHotkeys({ - hotkeyRecorder: { - onCancel: () => console.log('Recording cancelled'), - }, - }), - ], -} +provideHotkeys({ + defaultOptions: { hotkeyRecorder: { idleTimeoutMs: 2000 } }, +}) ``` -### `ignoreInputs` +## Recording sequences -`HotkeyRecorderOptions` includes an `ignoreInputs` option (defaults to `true`). When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements. Keystrokes pass through to the input as usual. Pressing Escape still cancels recording even when an input is focused. Set `ignoreInputs: false` if you want the recorder to capture keys from within input elements. +Set `maxSteps` above 1 to record up to that many chords. Each chord adds a step, and the recording commits when the user presses Enter, when it reaches `maxSteps`, or after `idleTimeoutMs` without a new step: ```ts -injectHotkeyRecorder({ - ignoreInputs: false, // record even from inside inputs - onRecord: (hotkey) => console.log(hotkey), +readonly recorder = injectHotkeyRecorder({ + maxSteps: 3, + onRecord: (hotkey) => console.log('Recorded:', hotkey), // 'Mod+[KeyS]' or ['[KeyG]', '[KeyG]'] }) + +// Live preview while recording +readonly label = computed(() => + this.recorder.isRecording() + ? formatForDisplay(this.recorder.steps()) || 'Press chords, then Enter…' + : formatForDisplay(this.recorder.recordedHotkey() ?? []), +) ``` +A one-step recording commits as a chord, so the result can always be passed straight to your hotkey registration. Use `maxSteps: Infinity` for no limit. + +| Option | Description | +| --- | --- | +| `maxSteps` | Maximum chords to record. Reaching it commits. Default `1`. | +| `commitKeys` | `'enter'` (default) commits on plain Enter. With `'none'`, plain Enter can be recorded as a step, and only `commitRecording()`, `idleTimeoutMs`, or `maxSteps` commit. | +| `idleTimeoutMs` | Commit automatically after this many milliseconds without a new step. The timer does not run before the first chord. | + +`commitKeys` and `idleTimeoutMs` are only accepted when `maxSteps` is set. With `maxSteps` above 1, `onRecord` and `validate` receive `Hotkey` (a chord or a sequence) instead of a chord. + ## 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. +| Key | Behavior | +| --- | --- | +| Modifier only (Shift, Ctrl, etc.) | Waits for a non-modifier key; modifier-only presses don't complete a step | +| Modifier + key (e.g., Ctrl+S) | Records the full combination | +| Single key (e.g., Escape, F1) | Records the single key | +| Enter (multi-step, `commitKeys: 'enter'`) | Commits the recorded steps | +| Escape | Cancels the recording | +| Backspace / Delete | Removes the last step; with no steps, clears and calls `onClear` | + +Repeats and IME composition never add a step. Recorded hotkeys use the portable `Mod` format: Command+S on macOS records `Mod+[KeyS]` rather than `Meta+[KeyS]`, so the primary modifier becomes Control if the hotkey is later used on Windows. ## Validation and conflicts -Both recorder APIs accept `detectConflicts`, `validate`, and `onReject`. For a single-hotkey recorder, these options can be supplied alongside `onRecord`: +The recorder accepts `detectConflicts`, `validate`, and `onReject` alongside `onRecord`: ```ts const options = { detectConflicts: { - // Use registration IDs to exclude the binding being edited: + // Use registration IDs to exclude the hotkey being edited: excludeIds: [registrationId], target: document, eventType: 'keydown', @@ -131,8 +149,76 @@ const options = { } ``` -`validate` returns true to accept, or false/a message to reject. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the candidate when available, and conflicting registration views for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. +`validate(hotkey, context)` returns true to accept, or false/a message to reject. The context holds the last chord's `event` and `parsedHotkey`, plus `events` and `parsedSequence` for every step. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the rejected `hotkey` when available, and conflicting registrations for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. A rejected multi-step recording keeps its steps so the user can edit them with Backspace. + +`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a hotkey. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Chords and sequence prefixes are checked: a recorded chord conflicts with a sequence that starts with it, and a recorded sequence conflicts with a registered chord equal to its first step. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute. + +For checks outside recording, use `findHotkeyConflicts(hotkey, options)`. Without source `events`, it compares hotkey identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. + +## Building a shortcut settings UI + +For an app-wide settings screen, declare the hotkeys with [app hotkeys](./app-hotkeys) and save the recorder's result with `setCustomHotkey`. That keeps cheat sheets, conflicts, and persistence in one place. + +For a single component, ordinary state is enough: + +```ts +import { Component, signal } from '@angular/core' +import { formatForDisplay, injectHotkeyRecorder, injectHotkeys } from '@tanstack/angular-hotkeys' +import type { Hotkey } from '@tanstack/angular-hotkeys' + +type Action = 'save' | 'undo' | 'search' + +@Component({ + selector: 'app-shortcut-settings', + template: ` + @for (entry of entries(); track entry[0]) { + + } + `, +}) +export class ShortcutSettingsComponent { + readonly format = formatForDisplay + readonly shortcuts = signal>({ + save: 'Mod+S', + undo: 'Mod+Z', + search: ['G', 'S'], + }) + readonly editing = signal(null) + readonly entries = () => Object.entries(this.shortcuts()) as Array<[Action, Hotkey]> + + readonly recorder = injectHotkeyRecorder({ + maxSteps: 2, + onRecord: (hotkey) => { + const action = this.editing() + if (action) this.shortcuts.update((prev) => ({ ...prev, [action]: hotkey })) + this.editing.set(null) + }, + onCancel: () => this.editing.set(null), + }) + + constructor() { + injectHotkeys(() => + this.entries().map(([action, hotkey]) => ({ + hotkey, + callback: () => this.run(action), + })), + ) + } + + edit(action: Action) { + this.editing.set(action) + this.recorder.startRecording() + } + + run(action: Action) { + /* ... */ + } +} +``` -`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a binding. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Both single bindings and sequence prefixes are checked. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can affect actual dispatch. +## Under the hood -For checks outside recording, use `findHotkeyConflicts(bindingOrSequence, options)`. Without source `events`, it compares binding identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. +`injectHotkeyRecorder` creates a `HotkeyRecorder` from `@tanstack/hotkeys`, exposes its state as signals, and destroys it with the injection context. diff --git a/docs/framework/angular/guides/hotkeys.md b/docs/framework/angular/guides/hotkeys.md index 07c0e44c..ddddd3c3 100644 --- a/docs/framework/angular/guides/hotkeys.md +++ b/docs/framework/angular/guides/hotkeys.md @@ -5,6 +5,18 @@ id: hotkeys The `injectHotkey` API is the primary way to register keyboard shortcuts in Angular applications. It wraps the singleton `HotkeyManager` with injection-context lifecycle management and Angular signal-friendly reactive options. +## Chords and sequences + +A hotkey is either a **chord**, keys pressed together such as `'Mod+S'`, or a **sequence**, chords pressed one after another such as `['G', 'G']`. Every API in this guide accepts both, and a one-step array such as `['Mod+S']` is the chord `'Mod+S'`. + +```ts +injectHotkey('Mod+S', () => this.save()) // chord +injectHotkey(['G', 'G'], () => this.scrollToTop(), { timeout: 500 }) // sequence +injectHotkey('Escape', () => this.close(), { requireReset: true }) // chord-only option +``` + +Most options apply to both kinds. `requireReset` applies only to chords and `timeout` only to sequences, and TypeScript rejects options that don't match the hotkey. When the kind isn't known statically, such as a `Hotkey` loaded from settings, only the shared options are accepted. Defaults apply only to the kinds that support them. See the [Sequences Guide](./sequences) for timing and matching, and the [App Hotkeys Guide](./app-hotkeys) to declare hotkeys up front and let users customize them. + ## Logical keys and physical positions Use a logical binding when the shortcut should follow the character on the active layout. Use a physical binding when it should follow a keyboard position: @@ -268,53 +280,39 @@ Group is descriptive metadata, not an execution scope. A shortcuts panel can gro ## Introspecting registrations -Use the `injectHotkeyRegistrations` API to get a live view of all hotkey and sequence registrations. Use it to build shortcut palettes, help dialogs, or devtools. +Use the `injectHotkeyRegistrations` function to get a live view of every registration, chords and sequences together. It's the building block for shortcut palettes and help dialogs. ```ts import { Component } from '@angular/core' import { injectHotkeyRegistrations, formatForDisplay } from '@tanstack/angular-hotkeys' @Component({ - standalone: true, + selector: 'app-shortcut-palette', template: ` -

Keyboard Shortcuts

    - @for (reg of registrations().hotkeys; track reg.id) { + @for (reg of registrations(); track reg.id) {
  • - {{ formatForDisplay(reg.hotkey) }} + {{ format(reg.hotkey) }} @if (reg.options.meta?.name) { - — {{ reg.options.meta.name }} + — {{ reg.options.meta?.name }} } - @if (reg.options.meta?.description) { -

    {{ reg.options.meta.description }}

    + @if (reg.kind === 'sequence') { + {{ reg.matchedStepCount }}/{{ reg.hotkey.length }} steps }
  • }
- @if (registrations().sequences.length > 0) { -

Sequences

-
    - @for (reg of registrations().sequences; track reg.id) { -
  • - {{ displaySequence(reg.sequence) }} - @if (reg.options.meta?.name) { - — {{ reg.options.meta.name }} - } -
  • - } -
- } `, }) export class ShortcutPaletteComponent { - readonly formatForDisplay = formatForDisplay - readonly displaySequence = (sequence: ReadonlyArray[0]>) => - sequence.map((step) => formatForDisplay(step)).join(' → ') + readonly format = formatForDisplay readonly registrations = injectHotkeyRegistrations() } ``` -The returned signal holds an object with two arrays. `hotkeys` contains registration objects with the hotkey string, options (including `meta`), and enabled state. `sequences` contains sequence registrations with the same structure. +The returned signal holds one array of registration views. Each view has an `id`, the `hotkey`, its `options` (including `meta`), its `target`, and a `triggerCount`. Narrow with `kind`: chord views add `parsedHotkey`; sequence views add `parsedSequence` and `matchedStepCount`, the steps matched so far in the current attempt. Disabled entries stay listed; unmounted registrations disappear. Format any hotkey with `formatForDisplay`, and group entries by `options.meta?.group` when building a help panel. + +Registrations only exist while their components are mounted. To list hotkeys from routes that have not rendered, declare them with [app hotkeys](./app-hotkeys). ## The hotkey manager diff --git a/docs/framework/angular/guides/key-state-tracking.md b/docs/framework/angular/guides/key-state-tracking.md index 17240675..bed782ab 100644 --- a/docs/framework/angular/guides/key-state-tracking.md +++ b/docs/framework/angular/guides/key-state-tracking.md @@ -103,6 +103,6 @@ tracker.isKeyHeld('Shift') readonly visible = injectHotkeyHint(() => this.binding()) // signal: visible() ``` -For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. +For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. A sequence reveals its hint from its first step, and an app hotkey uses its effective hotkey (no hint while unbound). Combine the boolean with the action's enabled state. The helper does not register a shortcut or determine whether its target is focused. The core equivalent is `matchesHeldModifiers(binding, heldKeys, options)`. diff --git a/docs/framework/angular/guides/sequence-recording.md b/docs/framework/angular/guides/sequence-recording.md deleted file mode 100644 index eb93680b..00000000 --- a/docs/framework/angular/guides/sequence-recording.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Sequence Recording Guide -id: sequence-recording ---- - -Use `injectHotkeySequenceRecorder` to capture a series of shortcut chords. By default, each step records its physical code: pressing G twice produces `['[KeyG]', '[KeyG]']`. Set `recordBy: 'key'` to follow logical characters instead. Pass the resulting array directly to sequence registration and format each step for display. - -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Allow the framework to run its effect or watcher before relying on the update. - -## Record and display a sequence - -This example uses a Save button so plain Enter can be recorded as a step: - -```ts -import { Component } from '@angular/core' -import { formatForDisplay, injectHotkeySequenceRecorder } from '@tanstack/angular-hotkeys' - -@Component({ - standalone: true, - template: ` - -

{{ label() }}

- @if (recorder.isRecording()) { - - - } - `, -}) -export class SequenceRecorderComponent { - readonly recorder = injectHotkeySequenceRecorder({ - commitKeys: 'none', - onRecord: (sequence) => console.log('Register or persist:', sequence), - onReject: ({ message }) => console.log(message), - }) - readonly label = () => - (this.recorder.isRecording() ? this.recorder.steps() : this.recorder.recordedSequence() ?? []) - .map((step) => formatForDisplay(step)).join(' → ') -} -``` - -## State and controls - -The recorder exposes `isRecording`, `steps`, and `recordedSequence` as signal getters. `steps` contains the current attempt; `recordedSequence` contains the last committed result. `startRecording()` begins a new session. `commitRecording()` saves a nonempty attempt, while `cancelRecording()` discards it and calls `onCancel`. `stopRecording()` resets recorder state without calling `onRecord` or `onCancel`. - -## Options and keyboard behavior - -- `recordBy`: `'code'` by default; `'key'` records produced logical characters. -- `commitKeys`: `'enter'` by default; plain Enter commits a nonempty sequence. `'none'` lets Enter become a step and requires manual or idle commit. -- `commitOnEnter: false`: also permits Enter as a step when `commitKeys` is `'enter'`. -- `idleTimeoutMs`: optionally commits after inactivity following a completed step. No timer runs before the first step. -- `ignoreInputs`: true by default, including shadow-root inputs. Set false to record from editable fields. Escape still cancels from an input. - -Escape cancels. Unmodified Backspace/Delete removes the last step; when already empty it stops and calls only `onClear`. Modifier-only presses, automatic repeats, and IME composition do not append steps. Recorded events and their releases do not trigger application shortcuts. - -Set provider defaults through `provideHotkeys({ hotkeySequenceRecorder: { ... } })`. - -## Validation and conflicts - -`validate(sequence, { events, parsedSequence })` runs at commit. Return true to accept, or false/a message to reject. `detectConflicts` checks live bindings and sequence prefixes, including physical/logical overlap established by the recorded events. `onReject` receives feedback; rejected steps remain editable with Backspace. - -The shared options and exclusions are described in the [hotkey recording guide](./hotkey-recording.md#validation-and-conflicts). The application owns reset, persistence, and any binding being edited; clearing never calls `onRecord([])`. diff --git a/docs/framework/angular/guides/sequences.md b/docs/framework/angular/guides/sequences.md index d8ec0b62..bb2ee963 100644 --- a/docs/framework/angular/guides/sequences.md +++ b/docs/framework/angular/guides/sequences.md @@ -3,167 +3,149 @@ title: Sequences Guide id: sequences --- -TanStack Hotkeys supports multi-key sequences in Angular, where keys are pressed one after another rather than simultaneously. +A sequence is a hotkey made of chords pressed one after another rather than together. Think Vim-style navigation, cheat codes, or multi-step commands. Sequences are ordinary hotkeys: register them with `injectHotkey` and `injectHotkeys`, list them with `injectHotkeyRegistrations`, record them with `injectHotkeyRecorder`, and format them with `formatForDisplay`. -Sequence steps use the same string syntax as single hotkeys. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. Display steps with `sequence.map((step) => formatForDisplay(step)).join(' → ')`. - -## Reactive options - -Sequence options support the same [property getter patterns](./hotkeys.md#property-getters) as hotkey options, including `enabled`, `timeout`, and `target`. Updates preserve the registration when its sequence, target, and platform stay the same. +Sequence steps use the same syntax as chords. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. ## Basic usage +Pass an array of chords wherever you pass a hotkey: + ```ts import { Component } from '@angular/core' -import { injectHotkeySequence } from '@tanstack/angular-hotkeys' +import { injectHotkey } from '@tanstack/angular-hotkeys' -@Component({ standalone: true, template: `` }) +@Component({ selector: 'app-root', template: `...` }) export class AppComponent { constructor() { - injectHotkeySequence(['G', 'G'], () => { + // Vim-style: press g then g to scroll to top + injectHotkey(['G', 'G'], () => { window.scrollTo({ top: 0, behavior: 'smooth' }) }) } } ``` +The user must press the steps in order, each within the timeout window. A one-step array such as `['Escape']` is the chord `'Escape'`. + ## Many sequences at once -Use `injectHotkeySequences` when you want several sequences (or a list built from data) in one injection context, instead of many `injectHotkeySequence` calls. +`injectHotkeys` registers chords and sequences together, including dynamic lists: ```ts -import { Component } from '@angular/core' -import { injectHotkeySequences } from '@tanstack/angular-hotkeys' +import { injectHotkeys } from '@tanstack/angular-hotkeys' -@Component({ standalone: true, template: `` }) -export class AppComponent { - constructor() { - injectHotkeySequences([ - { - sequence: ['G', 'G'], - callback: () => - window.scrollTo({ top: 0, behavior: 'smooth' }), - }, - { - sequence: ['D', 'D'], - callback: () => console.log('delete line'), - options: { timeout: 500 }, - }, - ]) - } -} +injectHotkeys([ + { hotkey: ['G', 'G'], callback: () => this.scrollToTop() }, + { hotkey: ['D', 'D'], callback: () => this.deleteLine(), options: { timeout: 500 } }, + { hotkey: 'Mod+S', callback: () => this.save() }, +]) ``` -Options merge like `injectHotkeys`: `provideHotkeys` defaults, then `commonOptions`, then each definition's `options`. - -## Matching steps - -Both `SequenceManager` and `createSequenceMatcher` ignore modifier-only events, IME composition, and automatic keydown repeats. These events neither advance the sequence nor refresh its timeout: holding G does not complete a two-press G sequence. The manager prefers exact matches over weaker logical-key fallbacks while preserving equally strong matches. - ## Sequence options -```ts -injectHotkeySequence(['G', 'G'], callback, { - timeout: 1000, - enabled: true, -}) -``` +Sequences accept the same options as chords, except `requireReset`, plus `timeout`. -### Reactive `enabled` +### `timeout` -When disabled, the sequence stays registered (visible in devtools); only execution is suppressed. +The maximum time (in milliseconds) allowed between consecutive steps. If the user takes longer than this between any two steps, the sequence resets. Defaults to `1000` (1 second). ```ts -import { Component, signal } from '@angular/core' -import { injectHotkeySequence } from '@tanstack/angular-hotkeys' - -@Component({ standalone: true, template: `` }) -export class VimModeComponent { - readonly isVimMode = signal(true) +// Fast sequence - user must type quickly +injectHotkey(['D', 'D'], () => this.deleteLine(), { timeout: 500 }) - constructor() { - injectHotkeySequence(['G', 'G'], () => scrollToTop(), () => ({ - enabled: this.isVimMode(), - })) - } -} +// Slow sequence - user has more time between keys +injectHotkey(['Shift+Z', 'Shift+Z'], () => this.forceQuit(), { timeout: 2000 }) ``` -## Global defaults via provider +`timeout` is only accepted when the hotkey is a sequence. Set a default for every sequence with `provideHotkeys`: ```ts -import { ApplicationConfig } from '@angular/core' import { provideHotkeys } from '@tanstack/angular-hotkeys' -export const appConfig: ApplicationConfig = { - providers: [ - provideHotkeys({ - hotkeySequence: { timeout: 1500 }, - }), - ], -} +bootstrapApplication(AppComponent, { + providers: [provideHotkeys({ defaultOptions: { hotkey: { timeout: 1500 } } })], +}) ``` -### `meta` +### `enabled` and `meta` -Sequences support the same `meta` option as hotkeys. Attach a `name` and `description` to surface in shortcut palettes and devtools. +These work exactly as they do for chords. Disabled sequences stay registered and visible in devtools, and `meta` flows to registrations, devtools, and cheat sheets. See the [Hotkeys Guide](./hotkeys). ```ts -injectHotkeySequence(['G', 'G'], () => scrollToTop(), { - meta: { name: 'Go to Top', description: 'Scroll to the top of the page' }, -}) +injectHotkey(['G', 'G'], () => this.scrollToTop(), () => ({ + enabled: this.isVimMode(), + meta: { name: 'Go to top', description: 'Scroll to the top of the page' }, +})) ``` -See the [Hotkeys Guide](./hotkeys.md#metadata-name--description) for details on declaration merging and introspecting registrations. - -## Chained modifier chords - -This example follows physical R and T positions. Brackets retain those positions even when the keys produce different letters. Other sequences can continue using logical characters. +## Sequences with modifiers -You can repeat the same modifier across consecutive steps: +Each step in a sequence can include modifiers, and consecutive steps can repeat the same modifier: ```ts -injectHotkeySequence(['Shift+[KeyR]', 'Shift+[KeyT]'], () => doNextAction()) -``` +// Ctrl+K followed by Ctrl+C (VS Code-style comment) +injectHotkey(['Mod+K', 'Mod+C'], () => this.commentSelection()) -While a sequence is in progress, the matcher ignores modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone): they do not advance the sequence and do not reset progress, so a user can press Shift alone between chords without breaking the sequence. +// Physical R then T positions, both with Shift +injectHotkey(['Shift+[KeyR]', 'Shift+[KeyT]'], () => this.doNextAction()) +``` -## Common patterns +Brackets retain physical positions even when the keys produce different letters. While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored. They neither advance the sequence nor reset progress, so a user can tap or hold Shift between `Shift+R` and `Shift+T`. -### Vim-style navigation +## Vim-style navigation ```ts -injectHotkeySequence(['G', 'G'], () => scrollToTop()) -injectHotkeySequence(['G', 'Shift+G'], () => scrollToBottom()) -injectHotkeySequence(['D', 'D'], () => deleteLine()) -injectHotkeySequence(['D', 'W'], () => deleteWord()) -injectHotkeySequence(['C', 'I', 'W'], () => changeInnerWord()) +injectHotkeys([ + { hotkey: ['G', 'G'], callback: () => this.scrollToTop() }, + { hotkey: ['G', 'Shift+G'], callback: () => this.scrollToBottom() }, + { hotkey: ['D', 'D'], callback: () => this.deleteLine() }, + { hotkey: ['D', 'W'], callback: () => this.deleteWord() }, + { hotkey: ['C', 'I', 'W'], callback: () => this.changeInnerWord() }, +]) ``` -### Konami Code +## Displaying sequences + +`formatForDisplay` formats each step and joins them with `sequenceSeparator`, a space by default: ```ts -injectHotkeySequence( - ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'B', 'A'], - () => enableEasterEgg(), - { timeout: 2000 }, -) +formatForDisplay(['Mod+K', 'Mod+C'], { platform: 'mac' }) // '⌘ K ⌘ C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' then ' }) // 'G then G' ``` -## Under the hood +Registration views report each sequence's progress in `matchedStepCount`, so a help panel can show how far the user has typed. See the [Hotkeys Guide](./hotkeys). -`injectHotkeySequence` uses the singleton `SequenceManager`. You can also access it directly: +## How sequences work -```ts -import { - createSequenceMatcher, - getSequenceManager, -} from '@tanstack/angular-hotkeys' +The `HotkeyManager` tracks progress for every sequence registration. When a key is pressed: -const manager = getSequenceManager() -const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 }) -``` +1. It checks whether the key matches the next expected step of each sequence +2. If it matches, that sequence advances +3. If the timeout expires between steps, the sequence resets +4. When all steps are completed, the callback fires + +Modifier-only events, IME composition, and automatic keydown repeats neither advance a sequence nor refresh its timeout: holding G does not complete a two-press G sequence. Exact matches take priority over weaker logical-key fallbacks. + +Multiple sequences can share a prefix, such as `['D', 'D']`, `['D', 'W']`, and `['D', 'I', 'W']`. Progress is tracked for each sequence independently, so after pressing `D` the manager waits for the next key to determine which sequence completes. ### Conflicting registrations -For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate bindings. `conflictBehavior` applies to equivalent sequences without rewriting their stored strings. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection also checks prefixes and observed physical/logical overlap. +For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate hotkeys. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection and app hotkey conflicts are stricter: there, a sequence also conflicts with a chord equal to its first step and with any sequence it starts. + +## Standalone matching + +For sequence matching without the singleton manager, use `createSequenceMatcher`: + +```ts +import { createSequenceMatcher } from '@tanstack/angular-hotkeys' + +const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 }) + +document.addEventListener('keydown', (e) => { + if (matcher.match(e)) { + console.log('Sequence completed!') + } + console.log('Progress:', matcher.getProgress()) // e.g., 1 +}) +``` diff --git a/docs/framework/angular/quick-start.md b/docs/framework/angular/quick-start.md index 52f720cd..4e3373b2 100644 --- a/docs/framework/angular/quick-start.md +++ b/docs/framework/angular/quick-start.md @@ -91,11 +91,12 @@ export class ModalComponent { ### Multi-key sequences ```ts -import { injectHotkeySequence } from '@tanstack/angular-hotkeys' +import { injectHotkey } from '@tanstack/angular-hotkeys' constructor() { - injectHotkeySequence(['G', 'G'], () => scrollToTop()) - injectHotkeySequence(['G', 'Shift+G'], () => scrollToBottom()) + // An array of chords registers a sequence + injectHotkey(['G', 'G'], () => scrollToTop()) + injectHotkey(['G', 'Shift+G'], () => scrollToBottom()) } ``` @@ -154,20 +155,25 @@ import { provideHotkeys } from '@tanstack/angular-hotkeys' export const appConfig: ApplicationConfig = { providers: [ provideHotkeys({ - hotkey: { preventDefault: true }, - hotkeySequence: { timeout: 1500 }, - hotkeyRecorder: { onCancel: () => console.log('Recording cancelled') }, + defaultOptions: { + hotkey: { preventDefault: true, timeout: 1500 }, + hotkeyRecorder: { onCancel: () => console.log('Recording cancelled') }, + }, }), ], } ``` +## Declaring app hotkeys + +To list every hotkey in a cheat sheet, including hotkeys whose components haven't mounted, and to let users customize them, declare them once with `defineAppHotkeys` and provide them with `provideHotkeys`. See the [App Hotkeys Guide](./guides/app-hotkeys). + ## Next steps - [Hotkeys Guide](./guides/hotkeys) - [Sequences Guide](./guides/sequences) - [Hotkey Recording Guide](./guides/hotkey-recording) -- [Sequence Recording Guide](./guides/sequence-recording) +- [App Hotkeys Guide](./guides/app-hotkeys) - [Key State Tracking Guide](./guides/key-state-tracking) - [Formatting & Display Guide](./guides/formatting-display) diff --git a/docs/framework/angular/reference/functions/injectAppHotkeyList.md b/docs/framework/angular/reference/functions/injectAppHotkeyList.md new file mode 100644 index 00000000..40a796e4 --- /dev/null +++ b/docs/framework/angular/reference/functions/injectAppHotkeyList.md @@ -0,0 +1,18 @@ +--- +id: injectAppHotkeyList +title: injectAppHotkeyList +--- + +```ts +function injectAppHotkeyList(): Signal>[]>; +``` + +Defined in: injectAppHotkeys.ts:51 + +Lists every app hotkey with its live status, in declaration order, for +cheat sheets and settings screens. App hotkeys whose components have not +mounted are `'inactive'`; those without a hotkey are `'unbound'`. + +## Returns + +`Signal`\<`AppHotkeyListItem`\<`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>[]\> diff --git a/docs/framework/angular/reference/functions/injectAppHotkeys.md b/docs/framework/angular/reference/functions/injectAppHotkeys.md new file mode 100644 index 00000000..a275c29d --- /dev/null +++ b/docs/framework/angular/reference/functions/injectAppHotkeys.md @@ -0,0 +1,26 @@ +--- +id: injectAppHotkeys +title: injectAppHotkeys +--- + +```ts +function injectAppHotkeys(): Signal>>>; +``` + +Defined in: injectAppHotkeys.ts:38 + +Returns a signal of the effective app hotkeys from `provideHotkeys`, typed +by `Register`: each declaration with the user's custom hotkey applied. + +## Returns + +`Signal`\<`Readonly`\<`Record`\<`string`, `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>\>\> + +## Example + +```ts +hotkeys = injectAppHotkeys() +constructor() { + injectHotkey(() => this.hotkeys()['doc.save'], () => this.save()) +} +``` diff --git a/docs/framework/angular/reference/functions/injectCustomHotkeys.md b/docs/framework/angular/reference/functions/injectCustomHotkeys.md new file mode 100644 index 00000000..bb481afe --- /dev/null +++ b/docs/framework/angular/reference/functions/injectCustomHotkeys.md @@ -0,0 +1,18 @@ +--- +id: injectCustomHotkeys +title: injectCustomHotkeys +--- + +```ts +function injectCustomHotkeys(): InjectCustomHotkeysResult; +``` + +Defined in: injectAppHotkeys.ts:90 + +Reads and changes the user's custom hotkeys. Changes apply to every +registered app hotkey immediately and are passed to `onCustomHotkeysChange` +from `provideHotkeys`, ready to save. + +## Returns + +[`InjectCustomHotkeysResult`](../interfaces/InjectCustomHotkeysResult.md) diff --git a/docs/framework/angular/reference/functions/injectDefaultHotkeysOptions.md b/docs/framework/angular/reference/functions/injectDefaultHotkeysOptions.md index 0ac393f9..456aa651 100644 --- a/docs/framework/angular/reference/functions/injectDefaultHotkeysOptions.md +++ b/docs/framework/angular/reference/functions/injectDefaultHotkeysOptions.md @@ -7,7 +7,7 @@ title: injectDefaultHotkeysOptions function injectDefaultHotkeysOptions(): HotkeysProviderOptions; ``` -Defined in: [hotkeys-provider.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L42) +Defined in: [hotkeys-provider.ts:123](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L123) ## Returns diff --git a/docs/framework/angular/reference/functions/injectHotkey.md b/docs/framework/angular/reference/functions/injectHotkey.md index 465b825b..b501db1c 100644 --- a/docs/framework/angular/reference/functions/injectHotkey.md +++ b/docs/framework/angular/reference/functions/injectHotkey.md @@ -3,88 +3,116 @@ id: injectHotkey title: injectHotkey --- +## Call Signature + ```ts -function injectHotkey( +function injectHotkey( hotkey, callback, options?): void; ``` -Defined in: [injectHotkey.ts:85](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkey.ts#L85) - -Angular inject-based API for registering a keyboard hotkey. +Defined in: [injectHotkey.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkey.ts#L69) -Uses the singleton HotkeyManager for efficient event handling. -The callback receives both the keyboard event and a context object -containing the hotkey string and parsed hotkey. +Angular inject-based API for registering a hotkey: a chord such as +`'Mod+S'`, a sequence such as `['G', 'G']`, or an app hotkey from +`defineAppHotkeys`. The hotkey and options may be getters that read signals. -Call in an injection context (e.g. constructor or field initializer). -Uses effect() to track reactive dependencies and update registration -when options or the callback change. -`enabled: false` keeps the registration (visible in devtools) and only suppresses firing; the same -handle is updated instead of unregistering and re-registering when identity is unchanged. +Chords accept `requireReset`; sequences accept `timeout`. A one-step +sequence registers as its chord. An app hotkey uses its effective hotkey +from `provideHotkeys` (including the user's custom hotkey) and its declared +meta and options, and registers nothing while unbound. -## Parameters +### Type Parameters -### hotkey +#### T -`RegisterableHotkey` \| (() => `RegisterableHotkey`) +`T` *extends* `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\> -The hotkey string (e.g. 'Mod+S', 'Escape') or getter function +### Parameters -### callback +#### hotkey -`HotkeyCallback` +`MaybeGetter`\<`T`\> -The function to call when the hotkey is pressed +#### callback -### options? +`HotkeyCallback` - \| [`InjectHotkeyOptions`](../interfaces/InjectHotkeyOptions.md) - \| (() => [`InjectHotkeyOptions`](../interfaces/InjectHotkeyOptions.md)) +#### options? -Options for the hotkey behavior, or getter for reactive options +`MaybeGetter`\<[`InjectAppHotkeyOptions`](../type-aliases/InjectAppHotkeyOptions.md)\<`T`\>\> -## Returns +### Returns `void` -## Examples +### Example ```ts @Component({ ... }) -export class SaveButtonComponent { - private readonly saveCount = signal(0) - +export class EditorComponent { constructor() { - injectHotkey('Mod+S', (event, { hotkey }) => { - event.preventDefault() - this.saveCount.update(c => c + 1) - }) + injectHotkey('Mod+S', () => this.save()) + injectHotkey(['G', 'G'], () => this.scrollToTop(), { timeout: 500 }) + injectHotkey('Escape', () => this.close(), () => ({ enabled: this.isOpen() })) } } ``` -```ts -@Component({ ... }) -export class ModalComponent { - isOpen = signal(true) +## Call Signature - constructor() { - injectHotkey('Escape', () => this.close(), () => ({ enabled: this.isOpen() })) - } -} +```ts +function injectHotkey( + hotkey, + callback, + options?): void; ``` +Defined in: [injectHotkey.ts:74](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkey.ts#L74) + +Angular inject-based API for registering a hotkey: a chord such as +`'Mod+S'`, a sequence such as `['G', 'G']`, or an app hotkey from +`defineAppHotkeys`. The hotkey and options may be getters that read signals. + +Chords accept `requireReset`; sequences accept `timeout`. A one-step +sequence registers as its chord. An app hotkey uses its effective hotkey +from `provideHotkeys` (including the user's custom hotkey) and its declared +meta and options, and registers nothing while unbound. + +### Type Parameters + +#### T + +`T` *extends* `RegisterableHotkey` + +### Parameters + +#### hotkey + +`MaybeGetter`\<`T`\> + +#### callback + +`HotkeyCallback` + +#### options? + +`MaybeGetter`\<`AdapterOptionsFor`\<`T`, [`InjectHotkeyTarget`](../type-aliases/InjectHotkeyTarget.md)\>\> + +### Returns + +`void` + +### Example + ```ts @Component({ ... }) export class EditorComponent { - private readonly editorRef = viewChild>('editorRef') - constructor() { - injectHotkey('Mod+B', () => this.toggleBold(), () => ({ - target: this.editorRef()?.nativeElement ?? null, - })) + injectHotkey('Mod+S', () => this.save()) + injectHotkey(['G', 'G'], () => this.scrollToTop(), { timeout: 500 }) + injectHotkey('Escape', () => this.close(), () => ({ enabled: this.isOpen() })) } } ``` diff --git a/docs/framework/angular/reference/functions/injectHotkeyHint.md b/docs/framework/angular/reference/functions/injectHotkeyHint.md index 0b5109a0..98a19905 100644 --- a/docs/framework/angular/reference/functions/injectHotkeyHint.md +++ b/docs/framework/angular/reference/functions/injectHotkeyHint.md @@ -7,19 +7,21 @@ title: injectHotkeyHint function injectHotkeyHint(hotkey, options?): Signal; ``` -Defined in: [injectHotkeyHint.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyHint.ts#L14) +Defined in: [injectHotkeyHint.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyHint.ts#L24) -Returns a computed signal indicating whether held modifiers reveal this shortcut. -Call in an injection context; pass signal getters for changing inputs. -Uses the nonempty-subset/AltGr rules of `matchesHeldModifiers`; `exact` requires -every modifier. Combine with the action's enabled state before showing a badge. -This helper does not register a shortcut or check whether its target is focused. +Returns a signal indicating whether held modifiers reveal this shortcut. +Pass getters to track changing hotkeys and options. A sequence reveals its +hint from its first step, and an app hotkey uses its effective hotkey +(false while unbound). This helper does not register a shortcut or check +whether its target is focused. ## Parameters ### hotkey -`RegisterableHotkey` \| (() => `RegisterableHotkey`) + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\> + \| (() => RegisterableHotkey \| AppHotkey\) ### options? diff --git a/docs/framework/angular/reference/functions/injectHotkeyRecorder.md b/docs/framework/angular/reference/functions/injectHotkeyRecorder.md index 7ce4b881..784593cc 100644 --- a/docs/framework/angular/reference/functions/injectHotkeyRecorder.md +++ b/docs/framework/angular/reference/functions/injectHotkeyRecorder.md @@ -3,33 +3,237 @@ id: injectHotkeyRecorder title: injectHotkeyRecorder --- +## Call Signature + +```ts +function injectHotkeyRecorder(options): AngularHotkeyRecorder; +``` + +Defined in: [injectHotkeyRecorder.ts:70](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L70) + +Angular inject-based API for recording keyboard shortcuts. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. Escape cancels; +Backspace/Delete removes the last step or clears. + +### Parameters + +#### options + +`HotkeyChordRecorderOptions` + +Configuration options for the recorder (or getter) + +### Returns + +[`AngularHotkeyRecorder`](../interfaces/AngularHotkeyRecorder.md)\<`HotkeyChord`\> + +Object with recording state signals and control functions + +### Example + +```ts +@Component({ ... }) +export class ShortcutSettingsComponent { + shortcut = signal('Mod+S') + recorder = injectHotkeyRecorder({ + onRecord: (hotkey) => this.shortcut.set(hotkey), + onCancel: () => console.log('Recording cancelled'), + }) + + constructor() { + injectHotkey( + () => this.shortcut(), + () => this.handleSave(), + ) + } +} +``` + +## Call Signature + +```ts +function injectHotkeyRecorder(options): AngularHotkeyRecorder; +``` + +Defined in: [injectHotkeyRecorder.ts:73](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L73) + +Angular inject-based API for recording keyboard shortcuts. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. Escape cancels; +Backspace/Delete removes the last step or clears. + +### Parameters + +#### options + +`HotkeySequenceRecorderOptions` + +Configuration options for the recorder (or getter) + +### Returns + +[`AngularHotkeyRecorder`](../interfaces/AngularHotkeyRecorder.md) + +Object with recording state signals and control functions + +### Example + +```ts +@Component({ ... }) +export class ShortcutSettingsComponent { + shortcut = signal('Mod+S') + recorder = injectHotkeyRecorder({ + onRecord: (hotkey) => this.shortcut.set(hotkey), + onCancel: () => console.log('Recording cancelled'), + }) + + constructor() { + injectHotkey( + () => this.shortcut(), + () => this.handleSave(), + ) + } +} +``` + +## Call Signature + +```ts +function injectHotkeyRecorder(options): AngularHotkeyRecorder; +``` + +Defined in: [injectHotkeyRecorder.ts:78](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L78) + +Angular inject-based API for recording keyboard shortcuts. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. Escape cancels; +Backspace/Delete removes the last step or clears. + +### Parameters + +#### options + +() => `HotkeySequenceRecorderOptions` + +Configuration options for the recorder (or getter) + +### Returns + +[`AngularHotkeyRecorder`](../interfaces/AngularHotkeyRecorder.md) + +Object with recording state signals and control functions + +### Example + +```ts +@Component({ ... }) +export class ShortcutSettingsComponent { + shortcut = signal('Mod+S') + recorder = injectHotkeyRecorder({ + onRecord: (hotkey) => this.shortcut.set(hotkey), + onCancel: () => console.log('Recording cancelled'), + }) + + constructor() { + injectHotkey( + () => this.shortcut(), + () => this.handleSave(), + ) + } +} +``` + +## Call Signature + +```ts +function injectHotkeyRecorder(options): AngularHotkeyRecorder; +``` + +Defined in: [injectHotkeyRecorder.ts:81](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L81) + +Angular inject-based API for recording keyboard shortcuts. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. Escape cancels; +Backspace/Delete removes the last step or clears. + +### Parameters + +#### options + +() => `HotkeyChordRecorderOptions` + +Configuration options for the recorder (or getter) + +### Returns + +[`AngularHotkeyRecorder`](../interfaces/AngularHotkeyRecorder.md)\<`HotkeyChord`\> + +Object with recording state signals and control functions + +### Example + +```ts +@Component({ ... }) +export class ShortcutSettingsComponent { + shortcut = signal('Mod+S') + recorder = injectHotkeyRecorder({ + onRecord: (hotkey) => this.shortcut.set(hotkey), + onCancel: () => console.log('Recording cancelled'), + }) + + constructor() { + injectHotkey( + () => this.shortcut(), + () => this.handleSave(), + ) + } +} +``` + +## Call Signature + ```ts function injectHotkeyRecorder(options): AngularHotkeyRecorder; ``` -Defined in: [injectHotkeyRecorder.ts:57](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L57) +Defined in: [injectHotkeyRecorder.ts:84](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L84) Angular inject-based API for recording keyboard shortcuts. -Thin wrapper around the framework-agnostic HotkeyRecorder class: captures -keyboard events, converts them to hotkey strings, and handles Escape to -cancel or Backspace/Delete to clear. +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. Escape cancels; +Backspace/Delete removes the last step or clears. -## Parameters +### Parameters -### options +#### options -`HotkeyRecorderOptions` \| (() => `HotkeyRecorderOptions`) +() => `HotkeyRecorderOptions` Configuration options for the recorder (or getter) -## Returns +### Returns [`AngularHotkeyRecorder`](../interfaces/AngularHotkeyRecorder.md) Object with recording state signals and control functions -## Example +### Example ```ts @Component({ ... }) diff --git a/docs/framework/angular/reference/functions/injectHotkeyRegistrations.md b/docs/framework/angular/reference/functions/injectHotkeyRegistrations.md index c34f975a..fbdb832f 100644 --- a/docs/framework/angular/reference/functions/injectHotkeyRegistrations.md +++ b/docs/framework/angular/reference/functions/injectHotkeyRegistrations.md @@ -4,22 +4,34 @@ title: injectHotkeyRegistrations --- ```ts -function injectHotkeyRegistrations(): HotkeyRegistrationsResult; +function injectHotkeyRegistrations(): Signal; ``` -Defined in: [injectHotkeyRegistrations.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRegistrations.ts#L35) +Defined in: [injectHotkeyRegistrations.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRegistrations.ts#L29) -Angular injectable that reactively reads all hotkey and sequence registrations -from the singleton managers. +Angular inject-based API that reactively reads every hotkey registration, +chords and sequences, from the singleton HotkeyManager. Narrow each view +with `kind`. -This is a standalone injectable that does NOT require the HotkeysProvider. -It subscribes to both HotkeyManager and SequenceManager stores and -triggers change detection when registrations change. - -Must be called in an injection context (constructor, factory, or `inject()`). +This does NOT require `provideHotkeys`. ## Returns -[`HotkeyRegistrationsResult`](../interfaces/HotkeyRegistrationsResult.md) +`Signal`\<`HotkeyRegistrationView`[]\> + +A signal of all registrations, without callbacks + +## Example -Object with `hotkeys` and `sequences` signals +```ts +@Component({ + template: ` + @for (reg of registrations(); track reg.id) { +
  • {{ reg.options.meta?.name }}
  • + } + `, +}) +export class ShortcutPalette { + registrations = injectHotkeyRegistrations() +} +``` diff --git a/docs/framework/angular/reference/functions/injectHotkeySequence.md b/docs/framework/angular/reference/functions/injectHotkeySequence.md deleted file mode 100644 index f1e88f7f..00000000 --- a/docs/framework/angular/reference/functions/injectHotkeySequence.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -id: injectHotkeySequence -title: injectHotkeySequence ---- - -```ts -function injectHotkeySequence( - sequence, - callback, - options?): void; -``` - -Defined in: [injectHotkeySequence.ts:58](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequence.ts#L58) - -Angular inject-based API for registering a keyboard shortcut sequence (Vim-style). - -Allows you to register multi-key sequences like 'g g' or 'd d' that trigger -when the full sequence is pressed within a timeout. - -Each step may include modifiers. You can chain the same modifier across -steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift, -Control, Alt, or Meta pressed alone) are ignored while matching—they do not -advance the sequence or reset progress. - -## Parameters - -### sequence - -`HotkeySequence` \| (() => `HotkeySequence`) - -Array of hotkey strings that form the sequence (or getter function) - -### callback - -`HotkeyCallback` - -Function to call when the sequence is completed - -### options? - - \| [`InjectHotkeySequenceOptions`](../interfaces/InjectHotkeySequenceOptions.md) - \| (() => [`InjectHotkeySequenceOptions`](../interfaces/InjectHotkeySequenceOptions.md)) - -Options for the sequence behavior (or getter function). `enabled: false` still registers - the sequence (visible in devtools); only execution is suppressed. - -## Returns - -`void` - -## Example - -```ts -@Component({ ... }) -export class VimEditorComponent { - lastSequence = signal(null) - - constructor() { - injectHotkeySequence(['G', 'G'], () => this.lastSequence.set('gg → Go to top')) - injectHotkeySequence(['D', 'D'], () => this.lastSequence.set('dd → Delete line')) - injectHotkeySequence(['C', 'I', 'W'], () => this.lastSequence.set('ciw'), { timeout: 500 }) - injectHotkeySequence(['Shift+R', 'Shift+T'], () => this.lastSequence.set('⇧R⇧T')) - } -} -``` diff --git a/docs/framework/angular/reference/functions/injectHotkeySequenceRecorder.md b/docs/framework/angular/reference/functions/injectHotkeySequenceRecorder.md deleted file mode 100644 index 3c0e8224..00000000 --- a/docs/framework/angular/reference/functions/injectHotkeySequenceRecorder.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: injectHotkeySequenceRecorder -title: injectHotkeySequenceRecorder ---- - -```ts -function injectHotkeySequenceRecorder(options): AngularHotkeySequenceRecorder; -``` - -Defined in: [injectHotkeySequenceRecorder.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequenceRecorder.ts#L31) - -Angular inject-based API for recording multi-chord sequences. - -## Parameters - -### options - -`HotkeySequenceRecorderOptions` \| (() => `HotkeySequenceRecorderOptions`) - -## Returns - -[`AngularHotkeySequenceRecorder`](../interfaces/AngularHotkeySequenceRecorder.md) diff --git a/docs/framework/angular/reference/functions/injectHotkeySequences.md b/docs/framework/angular/reference/functions/injectHotkeySequences.md deleted file mode 100644 index 3c45590c..00000000 --- a/docs/framework/angular/reference/functions/injectHotkeySequences.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -id: injectHotkeySequences -title: injectHotkeySequences ---- - -```ts -function injectHotkeySequences(sequences, commonOptions?): void; -``` - -Defined in: [injectHotkeySequences.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequences.ts#L51) - -Angular inject-based API for registering multiple keyboard shortcut sequences at once (Vim-style). - -Uses the singleton SequenceManager. Call in an injection context (e.g. constructor). -Uses `effect()` to track reactive dependencies when definitions or options are getters. - -Options are merged in this order: -provideHotkeys defaults < commonOptions < per-definition options - -Definitions with an empty `sequence` are skipped. Disabled sequences (`enabled: false`) -remain registered so they stay visible in devtools; the core manager suppresses execution. - -## Parameters - -### sequences - - \| [`InjectHotkeySequenceDefinition`](../interfaces/InjectHotkeySequenceDefinition.md)[] - \| (() => [`InjectHotkeySequenceDefinition`](../interfaces/InjectHotkeySequenceDefinition.md)[]) - -Array of sequence definitions, or getter returning them - -### commonOptions? - - \| [`InjectHotkeySequenceOptions`](../interfaces/InjectHotkeySequenceOptions.md) - \| (() => [`InjectHotkeySequenceOptions`](../interfaces/InjectHotkeySequenceOptions.md)) - -Shared options for all sequences, or getter - -## Returns - -`void` - -## Example - -```ts -@Component({ ... }) -export class VimComponent { - constructor() { - injectHotkeySequences([ - { sequence: ['G', 'G'], callback: () => this.goTop() }, - { sequence: ['D', 'D'], callback: () => this.deleteLine() }, - ]) - } -} -``` diff --git a/docs/framework/angular/reference/functions/injectHotkeys.md b/docs/framework/angular/reference/functions/injectHotkeys.md index b1595273..c3901418 100644 --- a/docs/framework/angular/reference/functions/injectHotkeys.md +++ b/docs/framework/angular/reference/functions/injectHotkeys.md @@ -7,67 +7,37 @@ title: injectHotkeys function injectHotkeys(hotkeys, commonOptions?): void; ``` -Defined in: [injectHotkeys.ts:75](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeys.ts#L75) +Defined in: [injectHotkeys.ts:36](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeys.ts#L36) -Angular inject-based API for registering multiple keyboard hotkeys at once. +Angular inject-based API for registering several hotkeys at once: chords, +sequences, and app hotkeys. Pass a getter for a reactive list. -Uses the singleton HotkeyManager for efficient event handling. -Accepts a dynamic array of hotkey definitions. - -Call in an injection context (e.g. constructor or field initializer). -Uses effect() to track reactive dependencies and update registrations -when options or the callback change. - -Options are merged in this order: -provideHotkeys defaults < commonOptions < per-definition options +Options merge in this order: provider defaults, app hotkey declaration, +commonOptions, per-hotkey options. Each option applies only to the kinds +that support it. Unbound app hotkeys and empty sequences register nothing. +A `null` target skips registration until it is set. ## Parameters ### hotkeys - \| [`InjectHotkeyDefinition`](../interfaces/InjectHotkeyDefinition.md)[] - \| (() => [`InjectHotkeyDefinition`](../interfaces/InjectHotkeyDefinition.md)[]) - -Array of hotkey definitions, or getter returning them + \| readonly [`InjectHotkeyDefinition`](../type-aliases/InjectHotkeyDefinition.md)[] + \| (() => readonly [`InjectHotkeyDefinition`](../type-aliases/InjectHotkeyDefinition.md)[]) ### commonOptions? - \| [`InjectHotkeyOptions`](../interfaces/InjectHotkeyOptions.md) - \| (() => [`InjectHotkeyOptions`](../interfaces/InjectHotkeyOptions.md)) - -Shared options for all hotkeys, or getter + \| [`InjectHotkeyCommonOptions`](../type-aliases/InjectHotkeyCommonOptions.md) + \| (() => [`InjectHotkeyCommonOptions`](../type-aliases/InjectHotkeyCommonOptions.md)) ## Returns `void` -## Examples +## Example ```ts -@Component({ ... }) -export class EditorComponent { - constructor() { - injectHotkeys([ - { hotkey: 'Mod+S', callback: () => this.save() }, - { hotkey: 'Mod+Z', callback: () => this.undo() }, - { hotkey: 'Escape', callback: () => this.close() }, - ]) - } -} -``` - -```ts -@Component({ ... }) -export class DynamicShortcuts { - shortcuts = signal([...]) - - constructor() { - injectHotkeys( - () => this.shortcuts().map((s) => ({ - hotkey: s.key, - callback: s.action, - })), - ) - } -} +injectHotkeys([ + { hotkey: 'Mod+S', callback: () => this.save() }, + { hotkey: ['G', 'G'], callback: () => this.scrollToTop(), options: { timeout: 500 } }, +]) ``` diff --git a/docs/framework/angular/reference/functions/injectHotkeysContext.md b/docs/framework/angular/reference/functions/injectHotkeysContext.md index a92e51c7..ecac4c25 100644 --- a/docs/framework/angular/reference/functions/injectHotkeysContext.md +++ b/docs/framework/angular/reference/functions/injectHotkeysContext.md @@ -7,7 +7,7 @@ title: injectHotkeysContext function injectHotkeysContext(): HotkeysContextValue; ``` -Defined in: [hotkeys-provider.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L38) +Defined in: [hotkeys-provider.ts:119](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L119) ## Returns diff --git a/docs/framework/angular/reference/functions/provideHotkeys.md b/docs/framework/angular/reference/functions/provideHotkeys.md index 37d6a97c..47a4ca17 100644 --- a/docs/framework/angular/reference/functions/provideHotkeys.md +++ b/docs/framework/angular/reference/functions/provideHotkeys.md @@ -4,17 +4,35 @@ title: provideHotkeys --- ```ts -function provideHotkeys(defaultOptions): StaticProvider; +function provideHotkeys(options?): Provider; ``` -Defined in: [hotkeys-provider.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L29) +Defined in: [hotkeys-provider.ts:84](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L84) + +Provides default options, app hotkeys, and the user's custom hotkeys to an +injector, such as the application or a component. ## Parameters -### defaultOptions +### options? -[`HotkeysProviderOptions`](../interfaces/HotkeysProviderOptions.md) +[`ProvideHotkeysOptions`](../interfaces/ProvideHotkeysOptions.md) = `{}` ## Returns -`StaticProvider` +`Provider` + +## Example + +```ts +bootstrapApplication(App, { + providers: [ + provideHotkeys({ + appHotkeys, + customHotkeys: JSON.parse(localStorage.getItem('hotkeys') ?? '{}'), + onCustomHotkeysChange: (custom) => + localStorage.setItem('hotkeys', JSON.stringify(custom)), + }), + ], +}) +``` diff --git a/docs/framework/angular/reference/index.md b/docs/framework/angular/reference/index.md index 1f6360d1..650d1410 100644 --- a/docs/framework/angular/reference/index.md +++ b/docs/framework/angular/reference/index.md @@ -6,14 +6,21 @@ title: "@tanstack/angular-hotkeys" ## Interfaces - [AngularHotkeyRecorder](interfaces/AngularHotkeyRecorder.md) -- [AngularHotkeySequenceRecorder](interfaces/AngularHotkeySequenceRecorder.md) -- [HotkeyRegistrationsResult](interfaces/HotkeyRegistrationsResult.md) - [HotkeysContextValue](interfaces/HotkeysContextValue.md) - [HotkeysProviderOptions](interfaces/HotkeysProviderOptions.md) -- [InjectHotkeyDefinition](interfaces/InjectHotkeyDefinition.md) -- [InjectHotkeyOptions](interfaces/InjectHotkeyOptions.md) -- [InjectHotkeySequenceDefinition](interfaces/InjectHotkeySequenceDefinition.md) -- [InjectHotkeySequenceOptions](interfaces/InjectHotkeySequenceOptions.md) +- [InjectCustomHotkeysResult](interfaces/InjectCustomHotkeysResult.md) +- [ProvideHotkeysOptions](interfaces/ProvideHotkeysOptions.md) + +## Type Aliases + +- [InjectAppHotkeyOptions](type-aliases/InjectAppHotkeyOptions.md) +- [InjectHotkeyChordOptions](type-aliases/InjectHotkeyChordOptions.md) +- [InjectHotkeyCommonOptions](type-aliases/InjectHotkeyCommonOptions.md) +- [InjectHotkeyDefinition](type-aliases/InjectHotkeyDefinition.md) +- [InjectHotkeyOptions](type-aliases/InjectHotkeyOptions.md) +- [InjectHotkeyOptionsFor](type-aliases/InjectHotkeyOptionsFor.md) +- [InjectHotkeySequenceOptions](type-aliases/InjectHotkeySequenceOptions.md) +- [InjectHotkeyTarget](type-aliases/InjectHotkeyTarget.md) ## Variables @@ -21,6 +28,9 @@ title: "@tanstack/angular-hotkeys" ## Functions +- [injectAppHotkeyList](functions/injectAppHotkeyList.md) +- [injectAppHotkeys](functions/injectAppHotkeys.md) +- [injectCustomHotkeys](functions/injectCustomHotkeys.md) - [injectDefaultHotkeysOptions](functions/injectDefaultHotkeysOptions.md) - [injectHeldKeyCodes](functions/injectHeldKeyCodes.md) - [injectHeldKeys](functions/injectHeldKeys.md) @@ -30,8 +40,5 @@ title: "@tanstack/angular-hotkeys" - [injectHotkeyRegistrations](functions/injectHotkeyRegistrations.md) - [injectHotkeys](functions/injectHotkeys.md) - [injectHotkeysContext](functions/injectHotkeysContext.md) -- [injectHotkeySequence](functions/injectHotkeySequence.md) -- [injectHotkeySequenceRecorder](functions/injectHotkeySequenceRecorder.md) -- [injectHotkeySequences](functions/injectHotkeySequences.md) - [injectKeyHold](functions/injectKeyHold.md) - [provideHotkeys](functions/provideHotkeys.md) diff --git a/docs/framework/angular/reference/interfaces/AngularHotkeyRecorder.md b/docs/framework/angular/reference/interfaces/AngularHotkeyRecorder.md index 3a630f97..216d53e2 100644 --- a/docs/framework/angular/reference/interfaces/AngularHotkeyRecorder.md +++ b/docs/framework/angular/reference/interfaces/AngularHotkeyRecorder.md @@ -3,7 +3,13 @@ id: AngularHotkeyRecorder title: AngularHotkeyRecorder --- -Defined in: [injectHotkeyRecorder.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L15) +Defined in: [injectHotkeyRecorder.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L22) + +## Type Parameters + +### THotkey + +`THotkey` *extends* `Hotkey` = `Hotkey` ## Properties @@ -13,9 +19,25 @@ Defined in: [injectHotkeyRecorder.ts:15](https://github.com/TanStack/hotkeys/blo readonly cancelRecording: () => void; ``` -Defined in: [injectHotkeyRecorder.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L25) +Defined in: [injectHotkeyRecorder.ts:34](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L34) + +Cancel recording and call onCancel + +#### Returns + +`void` + +*** + +### commitRecording + +```ts +readonly commitRecording: () => void; +``` + +Defined in: [injectHotkeyRecorder.ts:36](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L36) -Cancel recording without saving +Commit the recorded steps now (multi-step recordings) #### Returns @@ -29,7 +51,7 @@ Cancel recording without saving readonly isRecording: () => boolean; ``` -Defined in: [injectHotkeyRecorder.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L17) +Defined in: [injectHotkeyRecorder.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L24) Whether recording is currently active @@ -42,16 +64,16 @@ Whether recording is currently active ### recordedHotkey ```ts -readonly recordedHotkey: () => Hotkey | null; +readonly recordedHotkey: () => THotkey | null; ``` -Defined in: [injectHotkeyRecorder.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L19) +Defined in: [injectHotkeyRecorder.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L28) -The currently recorded hotkey (for live preview) +The last committed hotkey #### Returns -`Hotkey` \| `null` +`THotkey` \| `null` *** @@ -61,7 +83,7 @@ The currently recorded hotkey (for live preview) readonly startRecording: () => void; ``` -Defined in: [injectHotkeyRecorder.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L21) +Defined in: [injectHotkeyRecorder.ts:30](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L30) Start recording a new hotkey @@ -71,15 +93,31 @@ Start recording a new hotkey *** +### steps + +```ts +readonly steps: () => HotkeySequence; +``` + +Defined in: [injectHotkeyRecorder.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L26) + +Chords recorded so far in a multi-step recording (for live preview) + +#### Returns + +`HotkeySequence` + +*** + ### stopRecording ```ts readonly stopRecording: () => void; ``` -Defined in: [injectHotkeyRecorder.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L23) +Defined in: [injectHotkeyRecorder.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRecorder.ts#L32) -Stop recording (same as cancel) +Stop recording without calling onCancel #### Returns diff --git a/docs/framework/angular/reference/interfaces/AngularHotkeySequenceRecorder.md b/docs/framework/angular/reference/interfaces/AngularHotkeySequenceRecorder.md deleted file mode 100644 index 705df05d..00000000 --- a/docs/framework/angular/reference/interfaces/AngularHotkeySequenceRecorder.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -id: AngularHotkeySequenceRecorder -title: AngularHotkeySequenceRecorder ---- - -Defined in: [injectHotkeySequenceRecorder.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequenceRecorder.ts#L18) - -## Properties - -### cancelRecording - -```ts -readonly cancelRecording: () => void; -``` - -Defined in: [injectHotkeySequenceRecorder.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequenceRecorder.ts#L24) - -#### Returns - -`void` - -*** - -### commitRecording - -```ts -readonly commitRecording: () => void; -``` - -Defined in: [injectHotkeySequenceRecorder.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequenceRecorder.ts#L25) - -#### Returns - -`void` - -*** - -### isRecording - -```ts -readonly isRecording: () => boolean; -``` - -Defined in: [injectHotkeySequenceRecorder.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequenceRecorder.ts#L19) - -#### Returns - -`boolean` - -*** - -### recordedSequence - -```ts -readonly recordedSequence: () => HotkeySequence | null; -``` - -Defined in: [injectHotkeySequenceRecorder.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequenceRecorder.ts#L21) - -#### Returns - -`HotkeySequence` \| `null` - -*** - -### startRecording - -```ts -readonly startRecording: () => void; -``` - -Defined in: [injectHotkeySequenceRecorder.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequenceRecorder.ts#L22) - -#### Returns - -`void` - -*** - -### steps - -```ts -readonly steps: () => HotkeySequence; -``` - -Defined in: [injectHotkeySequenceRecorder.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequenceRecorder.ts#L20) - -#### Returns - -`HotkeySequence` - -*** - -### stopRecording - -```ts -readonly stopRecording: () => void; -``` - -Defined in: [injectHotkeySequenceRecorder.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequenceRecorder.ts#L23) - -#### Returns - -`void` diff --git a/docs/framework/angular/reference/interfaces/HotkeyRegistrationsResult.md b/docs/framework/angular/reference/interfaces/HotkeyRegistrationsResult.md deleted file mode 100644 index 28ef11f4..00000000 --- a/docs/framework/angular/reference/interfaces/HotkeyRegistrationsResult.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -id: HotkeyRegistrationsResult -title: HotkeyRegistrationsResult ---- - -Defined in: [injectHotkeyRegistrations.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRegistrations.ts#L16) - -Return type for injectHotkeyRegistrations. - -## Properties - -### hotkeys - -```ts -hotkeys: Signal; -``` - -Defined in: [injectHotkeyRegistrations.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRegistrations.ts#L18) - -Signal for all registered hotkeys (public view, no callbacks) - -*** - -### sequences - -```ts -sequences: Signal; -``` - -Defined in: [injectHotkeyRegistrations.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeyRegistrations.ts#L20) - -Signal for all registered sequences diff --git a/docs/framework/angular/reference/interfaces/HotkeysContextValue.md b/docs/framework/angular/reference/interfaces/HotkeysContextValue.md index 28d84013..bb64c3af 100644 --- a/docs/framework/angular/reference/interfaces/HotkeysContextValue.md +++ b/docs/framework/angular/reference/interfaces/HotkeysContextValue.md @@ -3,14 +3,40 @@ id: HotkeysContextValue title: HotkeysContextValue --- -Defined in: [hotkeys-provider.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L17) +Defined in: [hotkeys-provider.ts:45](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L45) ## Properties +### appHotkeysState + +```ts +readonly appHotkeysState: Signal< + | AppHotkeysState>>> +| null>; +``` + +Defined in: [hotkeys-provider.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L49) + +Tracks app hotkeys and custom hotkeys so signals and effects re-run. + +*** + +### appHotkeysStore + +```ts +readonly appHotkeysStore: + | AppHotkeysStore>>> + | null; +``` + +Defined in: [hotkeys-provider.ts:47](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L47) + +*** + ### defaultOptions ```ts readonly defaultOptions: HotkeysProviderOptions; ``` -Defined in: [hotkeys-provider.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L18) +Defined in: [hotkeys-provider.ts:46](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L46) diff --git a/docs/framework/angular/reference/interfaces/HotkeysProviderOptions.md b/docs/framework/angular/reference/interfaces/HotkeysProviderOptions.md index e7b370d1..577ba6ad 100644 --- a/docs/framework/angular/reference/interfaces/HotkeysProviderOptions.md +++ b/docs/framework/angular/reference/interfaces/HotkeysProviderOptions.md @@ -3,44 +3,29 @@ id: HotkeysProviderOptions title: HotkeysProviderOptions --- -Defined in: [hotkeys-provider.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L10) +Defined in: [hotkeys-provider.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L13) ## Properties ### hotkey? ```ts -optional hotkey?: Partial; +optional hotkey?: InjectHotkeyCommonOptions; ``` -Defined in: [hotkeys-provider.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L11) - -*** - -### hotkeyRecorder? - -```ts -optional hotkeyRecorder?: Partial; -``` +Defined in: [hotkeys-provider.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L18) -Defined in: [hotkeys-provider.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L12) +Defaults for every hotkey registered by `injectHotkey` and `injectHotkeys`. +`requireReset` applies to chords and `timeout` applies to sequences. *** -### hotkeySequence? +### hotkeyRecorder? ```ts -optional hotkeySequence?: Partial; +optional hotkeyRecorder?: Partial>; ``` -Defined in: [hotkeys-provider.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L14) - -*** - -### hotkeySequenceRecorder? +Defined in: [hotkeys-provider.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L20) -```ts -optional hotkeySequenceRecorder?: Partial; -``` - -Defined in: [hotkeys-provider.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L13) +Defaults for every `injectHotkeyRecorder`. diff --git a/docs/framework/angular/reference/interfaces/InjectCustomHotkeysResult.md b/docs/framework/angular/reference/interfaces/InjectCustomHotkeysResult.md new file mode 100644 index 00000000..58ba1c2d --- /dev/null +++ b/docs/framework/angular/reference/interfaces/InjectCustomHotkeysResult.md @@ -0,0 +1,111 @@ +--- +id: InjectCustomHotkeysResult +title: InjectCustomHotkeysResult +--- + +Defined in: injectAppHotkeys.ts:63 + +Custom hotkey state and actions returned by `injectCustomHotkeys`. + +## Properties + +### customHotkeys + +```ts +readonly customHotkeys: Signal>>>>; +``` + +Defined in: injectAppHotkeys.ts:65 + +The user's hotkeys that differ from the defaults. + +*** + +### findConflicts + +```ts +readonly findConflicts: (id, hotkey) => AppHotkey[]; +``` + +Defined in: injectAppHotkeys.ts:79 + +Other app hotkeys that would collide if `id` used `hotkey`. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] + +*** + +### resetCustomHotkey + +```ts +readonly resetCustomHotkey: (id) => void; +``` + +Defined in: injectAppHotkeys.ts:75 + +Restores the default hotkey for an app hotkey. + +#### Parameters + +##### id + +`string` + +#### Returns + +`void` + +*** + +### resetCustomHotkeys + +```ts +readonly resetCustomHotkeys: () => void; +``` + +Defined in: injectAppHotkeys.ts:77 + +Restores every default hotkey. + +#### Returns + +`void` + +*** + +### setCustomHotkey + +```ts +readonly setCustomHotkey: (id, hotkey) => AppHotkey[]; +``` + +Defined in: injectAppHotkeys.ts:70 + +Sets the user's hotkey for an app hotkey. `null` unbinds it. +Returns conflicting app hotkeys; when any exist, nothing changes. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` \| `null` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] diff --git a/docs/framework/angular/reference/interfaces/InjectHotkeyDefinition.md b/docs/framework/angular/reference/interfaces/InjectHotkeyDefinition.md deleted file mode 100644 index fc9d4ba5..00000000 --- a/docs/framework/angular/reference/interfaces/InjectHotkeyDefinition.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -id: InjectHotkeyDefinition -title: InjectHotkeyDefinition ---- - -Defined in: [injectHotkeys.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeys.ts#L19) - -A single hotkey definition for use with `injectHotkeys`. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [injectHotkeys.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeys.ts#L23) - -The function to call when the hotkey is pressed - -*** - -### hotkey - -```ts -hotkey: RegisterableHotkey | (() => RegisterableHotkey); -``` - -Defined in: [injectHotkeys.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeys.ts#L21) - -The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object - -*** - -### options? - -```ts -optional options?: - | InjectHotkeyOptions - | (() => InjectHotkeyOptions); -``` - -Defined in: [injectHotkeys.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeys.ts#L25) - -Per-hotkey options (merged on top of commonOptions) diff --git a/docs/framework/angular/reference/interfaces/InjectHotkeyOptions.md b/docs/framework/angular/reference/interfaces/InjectHotkeyOptions.md deleted file mode 100644 index 57e5f7f5..00000000 --- a/docs/framework/angular/reference/interfaces/InjectHotkeyOptions.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -id: InjectHotkeyOptions -title: InjectHotkeyOptions ---- - -Defined in: [injectHotkey.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkey.ts#L16) - -## Extends - -- `Omit`\<`HotkeyOptions`, `"target"`\> - -## Properties - -### target? - -```ts -optional target?: Document | Window | HTMLElement | null; -``` - -Defined in: [injectHotkey.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkey.ts#L24) - -The DOM element to attach the event listener to. -Can be a direct DOM element, an accessor (for reactive targets that become -available after mount), or null. Defaults to document. -When using scoped targets, pass an accessor: () => ({ target: elementSignal() }) -so the hotkey waits for the element to be attached before registering. diff --git a/docs/framework/angular/reference/interfaces/InjectHotkeySequenceDefinition.md b/docs/framework/angular/reference/interfaces/InjectHotkeySequenceDefinition.md deleted file mode 100644 index 262535c2..00000000 --- a/docs/framework/angular/reference/interfaces/InjectHotkeySequenceDefinition.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -id: InjectHotkeySequenceDefinition -title: InjectHotkeySequenceDefinition ---- - -Defined in: [injectHotkeySequences.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequences.ts#L14) - -A single sequence definition for use with `injectHotkeySequences`. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [injectHotkeySequences.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequences.ts#L18) - -The function to call when the sequence is completed - -*** - -### options? - -```ts -optional options?: - | InjectHotkeySequenceOptions - | (() => InjectHotkeySequenceOptions); -``` - -Defined in: [injectHotkeySequences.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequences.ts#L20) - -Per-sequence options (merged on top of commonOptions) - -*** - -### sequence - -```ts -sequence: HotkeySequence | (() => HotkeySequence); -``` - -Defined in: [injectHotkeySequences.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequences.ts#L16) - -Array of hotkey strings that form the sequence diff --git a/docs/framework/angular/reference/interfaces/InjectHotkeySequenceOptions.md b/docs/framework/angular/reference/interfaces/InjectHotkeySequenceOptions.md deleted file mode 100644 index 2bc10639..00000000 --- a/docs/framework/angular/reference/interfaces/InjectHotkeySequenceOptions.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -id: InjectHotkeySequenceOptions -title: InjectHotkeySequenceOptions ---- - -Defined in: [injectHotkeySequence.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequence.ts#L13) - -## Extends - -- `Omit`\<`SequenceOptions`, `"enabled"` \| `"target"`\> - -## Properties - -### enabled? - -```ts -optional enabled?: boolean; -``` - -Defined in: [injectHotkeySequence.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequence.ts#L18) - -Whether the sequence is enabled. Defaults to true. - -*** - -### target? - -```ts -optional target?: Document | Window | HTMLElement | null; -``` - -Defined in: [injectHotkeySequence.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeySequence.ts#L24) - -The DOM element to attach the event listener to. -Can be a direct DOM element, an accessor target, or null. -Defaults to document when omitted. diff --git a/docs/framework/angular/reference/interfaces/ProvideHotkeysOptions.md b/docs/framework/angular/reference/interfaces/ProvideHotkeysOptions.md new file mode 100644 index 00000000..89d099e3 --- /dev/null +++ b/docs/framework/angular/reference/interfaces/ProvideHotkeysOptions.md @@ -0,0 +1,68 @@ +--- +id: ProvideHotkeysOptions +title: ProvideHotkeysOptions +--- + +Defined in: [hotkeys-provider.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L29) + +Options for `provideHotkeys`. + +## Properties + +### appHotkeys? + +```ts +optional appHotkeys?: Readonly>>; +``` + +Defined in: [hotkeys-provider.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L35) + +Every app hotkey, from `defineAppHotkeys`. Listed by +`injectAppHotkeyList` whether or not they are registered. + +*** + +### customHotkeys? + +```ts +optional customHotkeys?: + | CustomHotkeys>>> + | null; +``` + +Defined in: [hotkeys-provider.ts:40](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L40) + +Starting custom hotkeys, such as saved data. Read once; the provider owns +the state afterwards. Invalid entries are ignored with a warning. + +*** + +### defaultOptions? + +```ts +optional defaultOptions?: HotkeysProviderOptions; +``` + +Defined in: [hotkeys-provider.ts:30](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L30) + +*** + +### onCustomHotkeysChange? + +```ts +optional onCustomHotkeysChange?: (customHotkeys) => void; +``` + +Defined in: [hotkeys-provider.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L42) + +Called with the custom hotkeys after every change, ready to save. + +#### Parameters + +##### customHotkeys + +`CustomHotkeys` + +#### Returns + +`void` diff --git a/docs/framework/angular/reference/type-aliases/InjectAppHotkeyOptions.md b/docs/framework/angular/reference/type-aliases/InjectAppHotkeyOptions.md new file mode 100644 index 00000000..917627f7 --- /dev/null +++ b/docs/framework/angular/reference/type-aliases/InjectAppHotkeyOptions.md @@ -0,0 +1,18 @@ +--- +id: InjectAppHotkeyOptions +title: InjectAppHotkeyOptions +--- + +```ts +type InjectAppHotkeyOptions = AdapterAppHotkeyOptions; +``` + +Defined in: [injectHotkey.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkey.ts#L42) + +Call-site options for an app hotkey, typed by its declared default kind. + +## Type Parameters + +### T + +`T` *extends* `AppHotkey` = `AppHotkey` diff --git a/docs/framework/angular/reference/type-aliases/InjectHotkeyChordOptions.md b/docs/framework/angular/reference/type-aliases/InjectHotkeyChordOptions.md new file mode 100644 index 00000000..9b8d3c69 --- /dev/null +++ b/docs/framework/angular/reference/type-aliases/InjectHotkeyChordOptions.md @@ -0,0 +1,12 @@ +--- +id: InjectHotkeyChordOptions +title: InjectHotkeyChordOptions +--- + +```ts +type InjectHotkeyChordOptions = AdapterChordOptions; +``` + +Defined in: [injectHotkey.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkey.ts#L28) + +Options for a chord such as `'Mod+S'`. diff --git a/docs/framework/angular/reference/type-aliases/InjectHotkeyCommonOptions.md b/docs/framework/angular/reference/type-aliases/InjectHotkeyCommonOptions.md new file mode 100644 index 00000000..cfa6d75d --- /dev/null +++ b/docs/framework/angular/reference/type-aliases/InjectHotkeyCommonOptions.md @@ -0,0 +1,12 @@ +--- +id: InjectHotkeyCommonOptions +title: InjectHotkeyCommonOptions +--- + +```ts +type InjectHotkeyCommonOptions = AdapterCommonOptions; +``` + +Defined in: [injectHotkey.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkey.ts#L35) + +Options applied to every hotkey, such as provider defaults. Each applies to the kinds that support it. diff --git a/docs/framework/angular/reference/type-aliases/InjectHotkeyDefinition.md b/docs/framework/angular/reference/type-aliases/InjectHotkeyDefinition.md new file mode 100644 index 00000000..45d6451f --- /dev/null +++ b/docs/framework/angular/reference/type-aliases/InjectHotkeyDefinition.md @@ -0,0 +1,13 @@ +--- +id: InjectHotkeyDefinition +title: InjectHotkeyDefinition +--- + +```ts +type InjectHotkeyDefinition = AdapterHotkeyDefinition; +``` + +Defined in: [injectHotkeys.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkeys.ts#L17) + +One hotkey for `injectHotkeys`. Chords accept `requireReset`; sequences +accept `timeout`; hotkeys of unknown kind accept only shared options. diff --git a/docs/framework/angular/reference/type-aliases/InjectHotkeyOptions.md b/docs/framework/angular/reference/type-aliases/InjectHotkeyOptions.md new file mode 100644 index 00000000..ebea284c --- /dev/null +++ b/docs/framework/angular/reference/type-aliases/InjectHotkeyOptions.md @@ -0,0 +1,12 @@ +--- +id: InjectHotkeyOptions +title: InjectHotkeyOptions +--- + +```ts +type InjectHotkeyOptions = AdapterHotkeyOptions; +``` + +Defined in: [injectHotkey.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkey.ts#L25) + +Options shared by chords and sequences. diff --git a/docs/framework/angular/reference/type-aliases/InjectHotkeyOptionsFor.md b/docs/framework/angular/reference/type-aliases/InjectHotkeyOptionsFor.md new file mode 100644 index 00000000..6420d459 --- /dev/null +++ b/docs/framework/angular/reference/type-aliases/InjectHotkeyOptionsFor.md @@ -0,0 +1,18 @@ +--- +id: InjectHotkeyOptionsFor +title: InjectHotkeyOptionsFor +--- + +```ts +type InjectHotkeyOptionsFor = AdapterOptionsFor; +``` + +Defined in: [injectHotkey.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkey.ts#L38) + +The options type for a hotkey: chord options for chords, sequence options for sequences. + +## Type Parameters + +### T + +`T` *extends* `RegisterableHotkey` diff --git a/docs/framework/angular/reference/type-aliases/InjectHotkeySequenceOptions.md b/docs/framework/angular/reference/type-aliases/InjectHotkeySequenceOptions.md new file mode 100644 index 00000000..40e067d1 --- /dev/null +++ b/docs/framework/angular/reference/type-aliases/InjectHotkeySequenceOptions.md @@ -0,0 +1,12 @@ +--- +id: InjectHotkeySequenceOptions +title: InjectHotkeySequenceOptions +--- + +```ts +type InjectHotkeySequenceOptions = AdapterSequenceOptions; +``` + +Defined in: [injectHotkey.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkey.ts#L31) + +Options for a sequence such as `['G', 'G']`. diff --git a/docs/framework/angular/reference/type-aliases/InjectHotkeyTarget.md b/docs/framework/angular/reference/type-aliases/InjectHotkeyTarget.md new file mode 100644 index 00000000..8b03d005 --- /dev/null +++ b/docs/framework/angular/reference/type-aliases/InjectHotkeyTarget.md @@ -0,0 +1,14 @@ +--- +id: InjectHotkeyTarget +title: InjectHotkeyTarget +--- + +```ts +type InjectHotkeyTarget = Target | null; +``` + +Defined in: [injectHotkey.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/injectHotkey.ts#L22) + +The element to listen on. A `null` target skips registration, so pass a +getter such as `() => ({ target: this.element() })` for elements that +attach after the view renders. diff --git a/docs/framework/angular/reference/variables/HOTKEYS_INJECTION_TOKEN.md b/docs/framework/angular/reference/variables/HOTKEYS_INJECTION_TOKEN.md index e7c6d03c..2352ffb8 100644 --- a/docs/framework/angular/reference/variables/HOTKEYS_INJECTION_TOKEN.md +++ b/docs/framework/angular/reference/variables/HOTKEYS_INJECTION_TOKEN.md @@ -7,4 +7,4 @@ title: HOTKEYS_INJECTION_TOKEN const HOTKEYS_INJECTION_TOKEN: InjectionToken; ``` -Defined in: [hotkeys-provider.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L21) +Defined in: [hotkeys-provider.ts:54](https://github.com/TanStack/hotkeys/blob/main/packages/angular-hotkeys/src/hotkeys-provider.ts#L54) diff --git a/docs/framework/ember/guides/app-hotkeys.md b/docs/framework/ember/guides/app-hotkeys.md new file mode 100644 index 00000000..219d704c --- /dev/null +++ b/docs/framework/ember/guides/app-hotkeys.md @@ -0,0 +1,201 @@ +--- +title: App Hotkeys Guide +id: app-hotkeys +--- + +`useHotkey` and `useHotkeys` register a hotkey while its component is mounted. A cheat sheet built from live registrations therefore misses hotkeys on routes that have not rendered yet, and a settings screen has nowhere to change them. App hotkeys solve both: you declare every hotkey in one place, publish them with `provideAppHotkeys`, and attach callbacks where the work happens. Users can then customize any of them, and your app decides how to save their choices. + +A declaration holds a hotkey's id, default hotkey, metadata, and options. It never holds a callback; `useHotkey` and `useHotkeys` still attach the callbacks. + +## Declare app hotkeys + +Use `defineAppHotkeys` in a module that contains only declarations. Keep it free of components and handlers, because it loads with your app shell: + +```ts +// app-hotkeys.ts +import { defineAppHotkeys } from '@tanstack/ember-hotkeys' + +export const appHotkeys = defineAppHotkeys({ + 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save', group: 'Document' } }, + 'doc.find': { + hotkey: 'Mod+F', + meta: { name: 'Find', group: 'Document' }, + context: 'editor', + }, + 'nav.home': { + hotkey: ['G', 'H'], + meta: { name: 'Go home', group: 'Navigation' }, + options: { timeout: 800 }, + }, + 'palette.open': { hotkey: null, meta: { name: 'Command palette' } }, +}) + +declare module '@tanstack/ember-hotkeys' { + interface Register { + appHotkeys: typeof appHotkeys + } +} +``` + +- The object key is the app hotkey's id. Treat ids as a stable contract, because saved custom hotkeys use them as keys. +- `hotkey` is the default: a chord, a sequence, or `null` for an action without a default. An unbound app hotkey registers nothing until the user assigns one. +- `options` describe the hotkey itself: `conflictBehavior`, `eventType`, `ignoreInputs`, `preventDefault`, `stopPropagation`, plus `requireReset` for a chord default or `timeout` for a sequence default. Options that don't match the default's kind are type errors. Pass `target` and `enabled` where you register the hotkey. +- `context` labels where a hotkey applies, such as `'editor'`. App hotkeys with different contexts may share a hotkey. It is used only for conflict checks and does not affect matching. + +The `Register` augmentation types the app hotkeys you read back, the ids accepted when changing custom hotkeys, and the `CustomHotkeys` type. + +## Provide app hotkeys + +```ts +// app/app.ts, before the application boots +import { provideAppHotkeys } from '@tanstack/ember-hotkeys' +import { appHotkeys } from './app-hotkeys' + +provideAppHotkeys(appHotkeys) +``` + +Ember has no provider component. `provideAppHotkeys` creates the shared `AppHotkeysStore` and publishes it for helpers, modifiers, utilities, and devtools. Call it once at startup; it returns `{ store, dispose }`. + +## Register app hotkeys + +Pass an app hotkey wherever you pass a hotkey: + +```gts +import Component from '@glimmer/component' +import { tracked } from '@glimmer/tracking' +import { useAppHotkeys, useHotkey } from '@tanstack/ember-hotkeys' + +export default class Editor extends Component { + @tracked dirty = false + hotkeys = useAppHotkeys(this) + save = () => {} + + get saveHotkey() { + return this.hotkeys.value['doc.save'] + } + + +} +``` + +Template paths can't index ids that contain dots, so read app hotkeys through getters. You can also import an app hotkey directly; it still uses the user's custom hotkey. Without provided app hotkeys, the declared default is used. + +The registration takes `meta` and `options` from the declaration. Options merge in this order: default options, then declared options, then call-site options. `meta` comes only from the declaration, so the cheat sheet and the registration cannot disagree. Call-site options are typed by the declared default's kind, so `timeout` is accepted for `nav.home` but not for `doc.save`. + +When the user changes a hotkey, registered app hotkeys move to the new hotkey immediately, even if it changes from a chord to a sequence. + +## Build a cheat sheet + +`useAppHotkeyList` returns every app hotkey in declaration order, with its live status: + +| Status | Meaning | +| --- | --- | +| `'active'` | At least one enabled registration | +| `'disabled'` | Registered, but every registration has `enabled: false` | +| `'inactive'` | Has a hotkey, but nothing has registered it | +| `'unbound'` | No hotkey assigned | + +```gts +import Component from '@glimmer/component' +import { formatForDisplay, useAppHotkeyList } from '@tanstack/ember-hotkeys' +import type { AppHotkey } from '@tanstack/ember-hotkeys' + +const label = (appHotkey: AppHotkey) => + appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned' + +export default class CheatSheet extends Component { + items = useAppHotkeyList(this) + + +} +``` + +An app hotkey registered by many components, such as one per list row, appears once. Its `registrations` array holds each live registration. Devtools also list declared app hotkeys that are not registered. + +## Customize and persist hotkeys + +TanStack Hotkeys does not store anything. Pass the user's saved custom hotkeys in and save them when they change: + +```ts +import { provideAppHotkeys } from '@tanstack/ember-hotkeys' + +provideAppHotkeys(appHotkeys, { + customHotkeys: JSON.parse(localStorage.getItem('hotkeys') ?? '{}'), + onCustomHotkeysChange: (custom) => + localStorage.setItem('hotkeys', JSON.stringify(custom)), +}) +``` + +- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them. +- `customHotkeys` is read once; afterwards the shared store owns the state. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning. +- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile. + +Recorded hotkeys use `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others. + +### Change hotkeys + +`useCustomHotkeys` reads and changes the user's custom hotkeys: + +```gts +import Component from '@glimmer/component' +import { on } from '@ember/modifier' +import { + formatForDisplay, + useAppHotkeys, + useCustomHotkeys, + useHotkeyRecorder, +} from '@tanstack/ember-hotkeys' +import type { AppHotkeyId } from '@tanstack/ember-hotkeys' + +export default class ShortcutRow extends Component<{ Args: { id: AppHotkeyId } }> { + hotkeys = useAppHotkeys(this) + custom = useCustomHotkeys(this) + recorder = useHotkeyRecorder(this, () => ({ + maxSteps: 3, + validate: (hotkey) => { + const conflicts = this.custom.findConflicts(this.args.id, hotkey) + return conflicts.length === 0 || `Used by ${conflicts[0]?.meta?.name}` + }, + onRecord: (hotkey) => this.custom.setCustomHotkey(this.args.id, hotkey), + onClear: () => this.custom.setCustomHotkey(this.args.id, null), + })) + + get appHotkey() { + return this.hotkeys.value[this.args.id] + } + get label() { + const { hotkey } = this.appHotkey + return hotkey ? formatForDisplay(hotkey) : 'Unassigned' + } + reset = () => this.custom.resetCustomHotkey(this.args.id) + + +} +``` + +| Member | Description | +| --- | --- | +| `value` | The hotkeys that differ from the defaults | +| `setCustomHotkey(id, hotkey)` | Sets a hotkey, or unbinds with `null`. Returns conflicting app hotkeys; when there are any, nothing changes. | +| `findConflicts(id, hotkey)` | App hotkeys that would collide if `id` used `hotkey` | +| `resetCustomHotkey(id)` | Restores one default | +| `resetCustomHotkeys()` | Restores every default | + +Two app hotkeys conflict when their contexts match or either has no `context`, and their hotkeys collide: equal chords, or one sequence starts with the other, including a sequence whose first step equals a chord. Conflicts are checked across every declaration, whether or not it is registered. + +Each app hotkey's `defaultHotkey` holds its declared default, and each cheat sheet item's `isCustom` reports whether the effective hotkey differs from it. diff --git a/docs/framework/ember/guides/formatting-display.md b/docs/framework/ember/guides/formatting-display.md index 4232d8a5..c941b8e3 100644 --- a/docs/framework/ember/guides/formatting-display.md +++ b/docs/framework/ember/guides/formatting-display.md @@ -3,7 +3,7 @@ title: Formatting & Display Guide id: formatting-display --- -Use `formatForDisplay` whenever a binding appears in a menu, button, hint, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, and parsed bindings. Store the original binding and format it at render time: the display label is not a registration string. +Use `formatForDisplay` whenever a hotkey appears in a menu, button, hint, cheat sheet, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, parsed bindings, and sequences. Store the original binding and format it at render time: the display label is not a registration string. ## Format a binding @@ -27,21 +27,23 @@ Set `parts: true` to get one label per key instead of a joined string: ```ts const binding = 'Mod+[KeyS]' const parts = formatForDisplay(binding, { platform: 'mac', parts: true }) -// ['⌘', 'S']; render each part in its own +// ['⌘', 'S'] — render each part in its own ``` With `parts` omitted or false, the result is a string. A runtime boolean returns `string | string[]`. Parts preserve literal plus keys and ignore `separatorToken`. -Sequences are arrays of bindings. Format their steps individually: +## Format sequences -```ts -import type { HotkeySequence } from '@tanstack/ember-hotkeys' +Pass a sequence to format each step and join the steps with `sequenceSeparator`, a space by default: -const sequence: HotkeySequence = ['Mod+[KeyK]', 'C'] -const label = sequence.map((step) => formatForDisplay(step)).join(' → ') +```ts +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac' }) // '⌘ K C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' → ' }) // 'G → G' +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac', parts: true }) +// [['⌘', 'K'], ['C']] — one array of keycaps per step ``` -`formatHotkeySequence` only joins stored strings with spaces. It intentionally retains brackets and code names, so use the code above for user-facing labels. +With `parts: true`, a sequence returns one array per step, so you can render each step as a group of keycaps. ## Choose symbols and separators @@ -49,21 +51,21 @@ const label = sequence.map((step) => formatForDisplay(step)).join(' → ') ```ts formatForDisplay('Shift+[ArrowUp]', { - platform: 'mac', - useSymbols: { modifiers: false, keys: true }, - parts: true, + platform: 'mac', + useSymbols: { modifiers: false, keys: true }, + parts: true, }) // ['Shift', '↑'] formatForDisplay('Mod+[KeyS]', { - platform: 'mac', useSymbols: false, + platform: 'mac', useSymbols: false, }) // 'Cmd+S' formatForDisplay('Control++', { - platform: 'windows', separatorToken: ' · ', + platform: 'windows', separatorToken: ' · ', }) // 'Ctrl · +' ``` -An empty separator joins labels directly. `undefined` or `null` uses the platform default. `formatWithLabels(binding, options)` is the shorthand for `formatForDisplay` with `useSymbols: false`. +An empty separator joins labels directly. `undefined` or `null` uses the platform default. `separatorToken` applies within a chord; `sequenceSeparator` applies between steps. ## Supply layout labels @@ -82,12 +84,16 @@ Use `keyLabels` for explicit labels keyed by physical code or normalized logical ```ts formatForDisplay('Mod+[KeyQ]', { - platform: 'mac', layoutMap, keyLabels: { KeyQ: 'Action' }, + platform: 'mac', layoutMap, keyLabels: { KeyQ: 'Action' }, }) // '⌘ Action' ``` The precedence is `keyLabels`, then a layout entry, then the fallback label. Layout entries receive normal letter casing and key symbols; missing or empty entries fall back. Explicit labels are final. All of these options affect display only. +## Compare hotkeys + +`areHotkeysEqual(a, b)` compares chords or sequences after resolving modifiers, so `Mod+S` equals `Meta+S` on macOS. Sequences compare step by step. `isHotkeySequence` narrows a `Hotkey` to a sequence, and `simplifyHotkey` turns a one-step sequence into its chord. + ## Parse and store bindings `parseHotkey` returns either a logical `key` or a physical `code`, plus resolved modifier flags. Narrow the union before inspecting the identity: @@ -97,7 +103,7 @@ import { parseHotkey, normalizeHotkeyFromParsed } from '@tanstack/ember-hotkeys' const parsed = parseHotkey('Mod+[KeyS]', 'mac') if (parsed.code !== undefined) { - console.log(parsed.code) // 'KeyS'; parsed.key is undefined + console.log(parsed.code) // 'KeyS'; parsed.key is undefined } const stored = normalizeHotkeyFromParsed(parsed, 'mac') // 'Mod+[KeyS]' formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S' @@ -105,6 +111,6 @@ formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S' Parsed modifiers are already resolved. To display a portable `Mod` binding on another platform, serialize with the original platform first, as above. `normalizeRegisterableHotkey` accepts strings or raw objects and preserves the logical/physical distinction. Do not store display labels or put a bracketed code in a logical `key` field. -Use `validateHotkey` when accepting strings from an external source. It returns `valid`, `errors`, and `warnings`; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). +Use `validateHotkey` when accepting a chord or sequence from an external source. It returns `valid`, `errors`, and `warnings`, and names the failing step of a sequence; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). Try these options together in the [vanilla formatter playground](../../../framework/vanilla/examples/formatForDisplay). diff --git a/docs/framework/ember/guides/hotkey-recording.md b/docs/framework/ember/guides/hotkey-recording.md index 570bd243..a7349381 100644 --- a/docs/framework/ember/guides/hotkey-recording.md +++ b/docs/framework/ember/guides/hotkey-recording.md @@ -3,158 +3,223 @@ title: Hotkey Recording Guide id: hotkey-recording --- -Use `useHotkeyRecorder` to build a shortcut customization UI. Recording defaults to physical codes, producing values such as `Mod+[KeyS]`. Store that value directly and pass it to `useHotkey`. Use `formatForDisplay` for the label. +`useHotkeyRecorder` is for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the key combination they want, the same way system preferences or IDE shortcut editors work. By default it records one chord; set `maxSteps` to let users record a [sequence](./sequences) too. -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. +Recorders default to physical codes: recording a shortcut stores a string such as `Mod+[KeyS]`. Pass it directly to your hotkey registration and use `formatForDisplay` for the label. Set `recordBy: 'key'` when you intentionally want the produced character instead. -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. +TanStack Hotkeys automatically suppresses registered hotkey callbacks while any recorder is active. You do not need to disable hotkeys while recording. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. ## Basic usage - ```gts import Component from '@glimmer/component' -import { tracked } from '@glimmer/tracking' import { on } from '@ember/modifier' -import { useHotkey, useHotkeyRecorder, formatForDisplay } from '@tanstack/ember-hotkeys' -import type { Hotkey } from '@tanstack/ember-hotkeys' - -export default class ShortcutSettings extends Component { - @tracked binding: Hotkey = 'Mod+S' - recorder = useHotkeyRecorder(this, { - onRecord: (hotkey) => { this.binding = hotkey }, - onClear: () => { this.binding = 'Mod+S' }, - }) - save = () => console.log('Saved') - get label() { return formatForDisplay(this.binding) } - - +import { formatForDisplay, useHotkeyRecorder } from '@tanstack/ember-hotkeys' + +export default class ShortcutRecorder extends Component { + recorder = useHotkeyRecorder(this, { + onRecord: (hotkey) => console.log('Recorded:', hotkey), // e.g. 'Mod+Shift+[KeyS]' + }) + + get label() { + const { isRecording, recordedHotkey } = this.recorder + if (isRecording) return 'Press a key combination...' + return recordedHotkey ? formatForDisplay(recordedHotkey) : 'Click to record' + } + + } ``` -This example stores the replacement binding in application state and restores `Mod+S` when the user clears it. Cancellation leaves the saved binding unchanged. +## Recorder state and actions -## Return value - -| Property | Type | Meaning | -| --- | --- | --- | -| `isRecording` | `boolean` | Whether a session is active. | -| `recordedHotkey` | `Hotkey \| null` | The recorded binding, or `null` after starting, stopping, or cancelling. | -| `startRecording` | `() => void` | Start a new session. | -| `stopRecording` | `() => void` | Stop and clear recorder state without calling `onRecord` or `onCancel`. | -| `cancelRecording` | `() => void` | Stop, clear recorder state, and call `onCancel`. | +| Member | Description | +| --- | --- | +| `isRecording` | Whether the recorder is currently listening for key presses | +| `steps` | Chords captured so far in a multi-step recording | +| `recordedHotkey` | The last recorded hotkey, or `null` if nothing recorded | +| `startRecording()` | Start listening for key presses | +| `stopRecording()` | Stop listening without calling `onRecord` or `onCancel` | +| `cancelRecording()` | Stop listening and call `onCancel` | +| `commitRecording()` | Commit the current `steps` now (multi-step recordings) | -The state fields are reactive getters. Read `recorder.isRecording` and `recorder.recordedHotkey` where the framework tracks dependencies; destructuring them once captures a snapshot. +State members are tracked getters, and the recorder is destroyed with its owner. ## Options ### `recordBy` -The default is `'code'`. On macOS, Option+S producing `ß` records `Alt+[KeyS]`, and Option+2 producing `™` records `Alt+[Digit2]`. Stored brackets preserve physical identity through serialization and registration. Existing authored strings such as `Mod+S` remain logical. +Recorders default to `recordBy: 'code'`. Option+S producing `ß` on macOS records `Alt+[KeyS]`; Option+2 producing `™` records `Alt+[Digit2]`. The stored brackets preserve physical identity through state, serialization, and registration; `formatForDisplay` produces a readable label. Existing authored strings such as `Mod+S` remain logical hotkeys. -Set `recordBy: 'key'` to record the produced character. Code mode rejects an event without a usable code and never falls back to key mode. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the character without synthetic Control/Alt while retaining Shift. +Set `recordBy: 'key'` to intentionally record the produced logical character. A code-mode event with no usable code is rejected rather than silently switching modes. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the produced character without synthetic Control/Alt while retaining Shift. ### `onRecord` -Receives the recorded `Hotkey` when the user enters a valid chord. A chord can be a single non-modifier key or a key with modifiers. Update your saved binding here. +Called when a recording commits. With the default `maxSteps: 1`, it receives a chord string such as `'Mod+[KeyS]'` as soon as the user presses a valid combination (a modifier + a non-modifier key, or a single non-modifier key). With a larger `maxSteps`, it receives a chord when one step was recorded, or a sequence otherwise. ### `onCancel` -Runs when Escape cancels a session or when you call `cancelRecording()`. Use it to exit an editing state without changing the saved binding. +Called when recording is cancelled, by pressing Escape or calling `cancelRecording()`. ### `onClear` -Runs when the user presses unmodified Backspace or Delete during recording. Clearing calls only `onClear`; it does not call `onRecord`. Your application decides whether to remove the binding or restore an initial value. +Called when the recording is cleared: Backspace or Delete with no modifiers and no recorded steps. Clearing calls only `onClear`; your app decides whether that removes a hotkey or restores a default. -### Shared defaults and changing options +### `ignoreInputs` -Pass defaults to `createHotkeysScope`. The scope accepts `hotkey`, `hotkeySequence`, `hotkeyRecorder`, and `hotkeySequenceRecorder` options. Use the returned contextual helpers and recorder factories. Pass the scope through component arguments to share it with descendants; helpers and recorders still clean up with their own owners. Pass a getter for tracked defaults. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. See [shared defaults](../quick-start.md#shared-defaults) for a complete example. +Defaults to `true`. The recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Escape still cancels recording even while an input is focused. Set `ignoreInputs: false` to record from inside input elements. -Pass an options getter when configuration changes: `useHotkeyRecorder(this, () => ({ recordBy: this.recordBy, onRecord: this.saveBinding }))`. The recorder reads current options when handling input, committing, or cancelling. Changes to recording mode, validation, conflict checks, and callbacks apply to the active session. Existing sequence steps remain unchanged when options change. +Options can also be a getter, such as `useHotkeyRecorder(this, () => ({ maxSteps: 3, recordBy: this.mode, onRecord }))`, read during recording and commit. -## Recording behavior +### Default options -| Input | Behavior | -| --- | --- | -| Modifier alone | Wait for a non-modifier key. | -| Modifier plus a non-modifier key | Record the chord and finish. | -| Single non-modifier key, such as F1 | Record the key and finish. | -| Escape | Cancel. | -| Unmodified Backspace or Delete | Clear and call `onClear`. | -| Automatic key repeat or IME composition | Do not record a new chord. | +Set defaults for every recorder with `createHotkeysScope`. Per-recorder options override them. -Recording events, repeats, and their key releases do not trigger registered hotkeys or sequences. +```ts +hotkeys = createHotkeysScope({ hotkeyRecorder: { idleTimeoutMs: 2000 } }) +recorder = this.hotkeys.useHotkeyRecorder(this, { maxSteps: 3, onRecord: this.save }) +``` -### `ignoreInputs` +## Recording sequences + +Set `maxSteps` above 1 to record up to that many chords. Each chord adds a step, and the recording commits when the user presses Enter, when it reaches `maxSteps`, or after `idleTimeoutMs` without a new step: + +```ts +recorder = useHotkeyRecorder(this, { + maxSteps: 3, + onRecord: (hotkey) => console.log('Recorded:', hotkey), // 'Mod+[KeyS]' or ['[KeyG]', '[KeyG]'] +}) + +// Live preview while recording +get label() { + return this.recorder.isRecording + ? formatForDisplay(this.recorder.steps) || 'Press chords, then Enter…' + : formatForDisplay(this.recorder.recordedHotkey ?? []) +} +``` -This defaults to `true`. Normal typing in inputs, textareas, selects, and contentEditable elements passes through. Escape still cancels while an input is focused. Set `ignoreInputs: false` to capture shortcuts from a focused input. +A one-step recording commits as a chord, so the result can always be passed straight to your hotkey registration. Use `maxSteps: Infinity` for no limit. -### Mod auto-conversion +| Option | Description | +| --- | --- | +| `maxSteps` | Maximum chords to record. Reaching it commits. Default `1`. | +| `commitKeys` | `'enter'` (default) commits on plain Enter. With `'none'`, plain Enter can be recorded as a step, and only `commitRecording()`, `idleTimeoutMs`, or `maxSteps` commit. | +| `idleTimeoutMs` | Commit automatically after this many milliseconds without a new step. The timer does not run before the first chord. | -On macOS, Command+S becomes `Mod+[KeyS]`. Reusing that binding on Windows resolves `Mod` to Control while preserving the physical key position. Pass a `platform` option when detection must be overridden. +`commitKeys` and `idleTimeoutMs` are only accepted when `maxSteps` is set. With `maxSteps` above 1, `onRecord` and `validate` receive `Hotkey` (a chord or a sequence) instead of a chord. + +## Recording behavior + +| Key | Behavior | +| --- | --- | +| Modifier only (Shift, Ctrl, etc.) | Waits for a non-modifier key; modifier-only presses don't complete a step | +| Modifier + key (e.g., Ctrl+S) | Records the full combination | +| Single key (e.g., Escape, F1) | Records the single key | +| Enter (multi-step, `commitKeys: 'enter'`) | Commits the recorded steps | +| Escape | Cancels the recording | +| Backspace / Delete | Removes the last step; with no steps, clears and calls `onClear` | + +Repeats and IME composition never add a step. Recorded hotkeys use the portable `Mod` format: Command+S on macOS records `Mod+[KeyS]` rather than `Meta+[KeyS]`, so the primary modifier becomes Control if the hotkey is later used on Windows. ## Validation and conflicts -Supply these options alongside `onRecord`: +The recorder accepts `detectConflicts`, `validate`, and `onReject` alongside `onRecord`: ```ts -import type { HotkeyRecorderOptions } from '@tanstack/ember-hotkeys' - -const options: HotkeyRecorderOptions = { - onRecord: (hotkey) => console.log('Accepted', hotkey), - detectConflicts: { - // Replace this with the ID from the live registration being edited. - excludeIds: ['registration-being-edited'], - target: document, - eventType: 'keydown', - }, - validate: (_hotkey, { parsedHotkey }) => - parsedHotkey.modifiers.length > 0 || 'Include a modifier.', - onReject: ({ reason, message, conflicts }) => { - console.log(reason, message, conflicts) - }, +const options = { + detectConflicts: { + // Use registration IDs to exclude the hotkey being edited: + excludeIds: [registrationId], + target: document, + eventType: 'keydown', + }, + validate: (_hotkey, { parsedHotkey }) => + parsedHotkey.modifiers.length > 0 || 'Include a modifier.', + onReject: ({ reason, message, conflicts }) => { + // Show feedback; recording stays active. + console.log(reason, message, conflicts) + }, } ``` -`validate` returns `true` to accept, or `false` or a message to reject. Validation runs before commit. A rejected candidate leaves recording active. `onReject` reports `missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`, plus the candidate when available and conflicting views for a conflict. +`validate(hotkey, context)` returns true to accept, or false/a message to reject. The context holds the last chord's `event` and `parsedHotkey`, plus `events` and `parsedSequence` for every step. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the rejected `hotkey` when available, and conflicting registrations for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. A rejected multi-step recording keeps its steps so the user can edit them with Backspace. -`detectConflicts: true` checks enabled live registrations with the intended event type, defaulting to keydown, and overlapping targets, defaulting to document. Document and nested element scopes can conflict; disjoint widgets can reuse a binding. The options object supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. +`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a hotkey. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Chords and sequence prefixes are checked: a recorded chord conflicts with a sequence that starts with it, and a recorded sequence conflicts with a registered chord equal to its first step. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute. -Checks include single bindings and sequence prefixes. Source events detect physical/logical overlap on the recorded layout. This is conservative collision detection: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can change dispatch. - -For checks outside recording, call `findHotkeyConflicts(bindingOrSequence, options)`. Without source `events`, it compares identities and prefixes rather than guessing which logical character a physical position produces. +For checks outside recording, use `findHotkeyConflicts(hotkey, options)`. Without source `events`, it compares hotkey identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. ## Building a shortcut settings UI -For several actions, keep the bindings in an array or object and record the ID of the action being edited. On `onRecord`, replace that action's binding and clear the editing ID. On `onCancel`, clear only the editing ID. Use the plural registration API for the current list. +For an app-wide settings screen, declare the hotkeys with [app hotkeys](./app-hotkeys) and save the recorder's result with `setCustomHotkey`. That keeps cheat sheets, conflicts, and persistence in one place. + +For a single component, ordinary state is enough: ```gts -// In the component class: -get definitions() { - return this.shortcuts.map((shortcut) => ({ - hotkey: shortcut.hotkey, - callback: () => this.runAction(shortcut.id), - options: { meta: { name: shortcut.name } }, - })) -} +import Component from '@glimmer/component' +import { tracked } from '@glimmer/tracking' +import { fn } from '@ember/helper' +import { on } from '@ember/modifier' +import { formatForDisplay, useHotkeyRecorder, useHotkeys } from '@tanstack/ember-hotkeys' +import type { EmberHotkeyDefinition, Hotkey } from '@tanstack/ember-hotkeys' - -``` +type Action = 'save' | 'undo' | 'search' -The [useHotkeyRecorder example](../examples/useHotkeyRecorder) includes multiple actions, editable names and descriptions, create/delete controls, reset and clear behavior, cancellation, and a live registry. The [kitchen sink](../examples/kitchen-sink) also demonstrates conflict feedback and physical versus logical recording. +export default class ShortcutSettings extends Component { + @tracked shortcuts: Record = { + save: 'Mod+S', + undo: 'Mod+Z', + search: ['G', 'S'], + } + @tracked editing: Action | null = null + + recorder = useHotkeyRecorder(this, { + maxSteps: 2, + onRecord: (hotkey) => { + if (this.editing) this.shortcuts = { ...this.shortcuts, [this.editing]: hotkey } + this.editing = null + }, + onCancel: () => (this.editing = null), + }) + + get entries() { + return Object.entries(this.shortcuts).map(([action, hotkey]) => ({ + action: action as Action, + label: + this.editing === action && this.recorder.isRecording + ? 'Press keys…' + : formatForDisplay(hotkey), + })) + } + + get definitions(): Array { + return Object.entries(this.shortcuts).map(([action, hotkey]) => ({ + hotkey, + callback: () => this.run(action as Action), + })) + } + + edit = (action: Action) => { + this.editing = action + this.recorder.startRecording() + } + + run(action: Action) { + /* ... */ + } + + +} +``` ## Under the hood -Ember subscribes to the core TanStack Store through tracked state and destroys the recorder with its owner. The core `HotkeyRecorder` owns the recording listeners. Store the accepted binding in application state rather than relying on recorder session state as your preferences store. +`useHotkeyRecorder` creates a `HotkeyRecorder` from `@tanstack/hotkeys`, exposes its state through tracked getters, and destroys it with its owner. diff --git a/docs/framework/ember/guides/hotkeys.md b/docs/framework/ember/guides/hotkeys.md index 9431615d..cd0976fd 100644 --- a/docs/framework/ember/guides/hotkeys.md +++ b/docs/framework/ember/guides/hotkeys.md @@ -3,10 +3,22 @@ title: Hotkeys Guide id: hotkeys --- -`useHotkey` registers a shortcut with the shared `HotkeyManager`. `useHotkey`, `useHotkeys`, `useHotkeySequence`, and `useHotkeySequences` are template helpers. Invoke them with `{{...}}`, not as JavaScript hooks. Tracked arguments update registrations after rendering. Removing a helper from the template unregisters its bindings. +`useHotkey` registers a shortcut with the shared `HotkeyManager`. `useHotkey` and `useHotkeys` are template helpers. Invoke them with `{{...}}`, not as JavaScript hooks. Tracked arguments update registrations after rendering. Removing a helper from the template unregisters its bindings. State readers and recorders are JavaScript functions. Pass the containing component as their first argument, usually `this`. The adapter releases subscriptions and recorders when that owner is destroyed. State readers expose `.value`; recorder fields and registration arrays are reactive getters. Read those properties in templates or getters instead of destructuring an initial snapshot. +## Chords and sequences + +A hotkey is either a **chord**, keys pressed together such as `'Mod+S'`, or a **sequence**, chords pressed one after another such as `['G', 'G']`. Every API in this guide accepts both, and a one-step array such as `['Mod+S']` is the chord `'Mod+S'`. + +```gts +{{useHotkey 'Mod+S' this.save}} +{{useHotkey (array 'G' 'G') this.scrollToTop timeout=500}} +{{useHotkey 'Escape' this.close requireReset=true}} +``` + +Most options apply to both kinds. `requireReset` applies only to chords and `timeout` only to sequences; an option that doesn't match the hotkey's kind is ignored. In JavaScript, `useHotkeys` definitions are typed by their hotkey, so TypeScript rejects mismatched options there. Defaults apply only to the kinds that support them. See the [Sequences Guide](./sequences) for timing and matching, and the [App Hotkeys Guide](./app-hotkeys) to declare hotkeys up front and let users customize them. + ## Logical keys and physical positions Use a logical binding when the shortcut should follow the character on the active layout. Use a physical binding when it should follow a keyboard position: @@ -86,12 +98,12 @@ Registration helpers track named arguments and property getters inside definitio import Component from '@glimmer/component' import { tracked } from '@glimmer/tracking' import { useHotkeys } from '@tanstack/ember-hotkeys' -import type { HotkeyDefinition } from '@tanstack/ember-hotkeys' +import type { EmberHotkeyDefinition } from '@tanstack/ember-hotkeys' export default class Editor extends Component { @tracked enabled = true - definitions: Array = [{ + definitions: Array = [{ hotkey: 'Mod+S', callback: () => console.log('Save'), options: this.createOptions(), @@ -110,7 +122,7 @@ export default class Editor extends Component { } ``` -The helper reads getters during rendering and applies registration changes after rendering. This also works with `useHotkeySequences`. For a single registration, pass tracked named arguments such as `enabled=this.enabled`. Scope defaults and recorder options accept property getters or functions returning options. Recorders read current options during the active session without requiring a render. +The helper reads getters during rendering and applies registration changes after rendering. This also works with sequences. For a single registration, pass tracked named arguments such as `enabled=this.enabled`. Scope defaults and recorder options accept property getters or functions returning options. Recorders read current options during the active session without requiring a render. Getters must read tracked state. A plain `{ enabled: this.enabled }` stored once does not track later changes. Callbacks remain functions and are not invoked to resolve options. @@ -136,7 +148,7 @@ By default, Control/Meta shortcuts and Escape work in text inputs, textareas, se ### Shared default options -Pass defaults to `createHotkeysScope`. The scope accepts `hotkey`, `hotkeySequence`, `hotkeyRecorder`, and `hotkeySequenceRecorder` options. Use the returned contextual helpers and recorder factories. Pass the scope through component arguments to share it with descendants; helpers and recorders still clean up with their own owners. Pass a getter for tracked defaults. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. See [shared defaults](../quick-start.md#shared-defaults) for a complete example. +Pass defaults to `createHotkeysScope`. The scope accepts `hotkey` and `hotkeyRecorder` options. Use the returned contextual helpers and recorder factories. Pass the scope through component arguments to share it with descendants; helpers and recorders still clean up with their own owners. Pass a getter for tracked defaults. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. See [shared defaults](../quick-start.md#shared-defaults) for a complete example. ## Hotkey options @@ -211,23 +223,23 @@ Pass `'mac'`, `'windows'`, or `'linux'` to override detection. Use the same plat ## Current callbacks and automatic cleanup -`useHotkey`, `useHotkeys`, `useHotkeySequence`, and `useHotkeySequences` are template helpers. Invoke them with `{{...}}`, not as JavaScript hooks. Tracked arguments update registrations after rendering. Removing a helper from the template unregisters its bindings. +`useHotkey` and `useHotkeys` are template helpers. Invoke them with `{{...}}`, not as JavaScript hooks. Tracked arguments update registrations after rendering. Removing a helper from the template unregisters its bindings. State readers and recorders are JavaScript functions. Pass the containing component as their first argument, usually `this`. The adapter releases subscriptions and recorders when that owner is destroyed. State readers expose `.value`; recorder fields and registration arrays are reactive getters. Read those properties in templates or getters instead of destructuring an initial snapshot. ## Registering multiple hotkeys -Use `useHotkeys` to register a dynamic list. Per-definition options override common options. Removing an entry unregisters it. Each entry has the shared [HotkeyDefinition](../../../reference/adapter/interfaces/HotkeyDefinition) shape. +Use `useHotkeys` to register a dynamic list. Per-definition options override common options. Removing an entry unregisters it. Each entry has the shared [EmberHotkeyDefinition](../reference/type-aliases/EmberHotkeyDefinition.md) shape. ```gts import Component from '@glimmer/component' import { tracked } from '@glimmer/tracking' import { useHotkeys } from '@tanstack/ember-hotkeys' -import type { HotkeyDefinition } from '@tanstack/ember-hotkeys' +import type { EmberHotkeyDefinition } from '@tanstack/ember-hotkeys' export default class Menu extends Component { @tracked enabled = true - @tracked shortcuts: Array = [ + @tracked shortcuts: Array = [ { hotkey: 'Mod+S', callback: () => console.log('Save') }, { hotkey: 'Mod+Z', callback: () => console.log('Undo'), options: { enabled: false } }, ] @@ -255,7 +267,7 @@ declare module '@tanstack/hotkeys' { ## Introspecting registrations -Read `hotkeys` and `sequences` from `useHotkeyRegistrations(this)`. Disabled entries stay listed; destroyed registrations disappear. Both arrays include options and metadata. Hotkey views include enabled state and trigger counts; sequence views include sequence steps and progress. +Use the `useHotkeyRegistrations` utility to get a live view of every registration, chords and sequences together. It's the building block for shortcut palettes and help dialogs. ```gts import Component from '@glimmer/component' @@ -276,7 +288,9 @@ export default class Shortcuts extends Component { } ``` -Read `registrations.sequences` in the same way. Format each sequence step with `formatForDisplay` and join the labels with an arrow. Group entries by `registration.options.meta?.group` when building a help panel. +The tracked `hotkeys` array holds one list of registration views. Each view has an `id`, the `hotkey`, its `options` (including `meta`), its `target`, and a `triggerCount`. Narrow with `kind`: chord views add `parsedHotkey`; sequence views add `parsedSequence` and `matchedStepCount`, the steps matched so far in the current attempt. Disabled entries stay listed; unmounted registrations disappear. Format any hotkey with `formatForDisplay`, and group entries by `options.meta?.group` when building a help panel. + +Registrations only exist while their components are mounted. To list hotkeys from routes that have not rendered, declare them with [app hotkeys](./app-hotkeys). ## The hotkey manager diff --git a/docs/framework/ember/guides/key-state-tracking.md b/docs/framework/ember/guides/key-state-tracking.md index 09080923..64199688 100644 --- a/docs/framework/ember/guides/key-state-tracking.md +++ b/docs/framework/ember/guides/key-state-tracking.md @@ -92,7 +92,7 @@ hint = useHotkeyHint(this, 'Alt+Shift+[KeyK]') // Read this.hint.value in a template or getter. ``` -Holding Alt, Shift, or both reveals this hint. Extra Control hides it, as does releasing all modifiers or blurring the window. Nonmodifier keys are ignored. AltGraph never reveals hints. +Holding Alt, Shift, or both reveals this hint. Extra Control hides it, as does releasing all modifiers or blurring the window. Nonmodifier keys are ignored. AltGraph never reveals hints. A sequence reveals its hint from its first step, and an app hotkey uses its effective hotkey (no hint while unbound). Pass `{ exact: true }` to require every binding modifier, or `{ platform: 'mac' }` to resolve `Mod` explicitly. Supply the same platform used for registration. Combine the result with the action's enabled state. The core equivalent is `matchesHeldModifiers(binding, heldKeys, options)`. diff --git a/docs/framework/ember/guides/sequence-recording.md b/docs/framework/ember/guides/sequence-recording.md deleted file mode 100644 index 6836bbfc..00000000 --- a/docs/framework/ember/guides/sequence-recording.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: Sequence Recording Guide -id: sequence-recording ---- - -Use `useHotkeySequenceRecorder` to record an ordered sequence of chords. Recording defaults to physical codes, such as `['[KeyG]', 'Alt+[KeyS]']`. Use `recordBy: 'key'` for logical characters. Pass the saved array directly to `useHotkeySequence`. - -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. - -## Basic usage - - -```gts -import Component from '@glimmer/component' -import { tracked } from '@glimmer/tracking' -import { on } from '@ember/modifier' -import { useHotkeySequence, useHotkeySequenceRecorder, formatForDisplay } from '@tanstack/ember-hotkeys' -import type { HotkeySequence } from '@tanstack/ember-hotkeys' - -export default class SequenceSettings extends Component { - @tracked binding: HotkeySequence = ['G', 'G'] - recorder = useHotkeySequenceRecorder(this, { - onRecord: (sequence) => { this.binding = sequence }, - }) - goToTop = () => console.log('Go to top') - get preview() { return this.recorder.steps.map((step) => formatForDisplay(step)).join(' → ') } - - -} -``` - -Press and release each chord, then press Enter or click Save. Cancellation leaves the saved binding unchanged. - -## Return value - -| Property | Type | Meaning | -| --- | --- | --- | -| `isRecording` | `boolean` | Whether a session is active. | -| `steps` | `HotkeySequence` | Chords captured in the current session. | -| `recordedSequence` | `HotkeySequence \| null` | The last committed sequence. | -| `startRecording` | `() => void` | Start a new session. | -| `stopRecording` | `() => void` | Stop without calling `onRecord` or `onCancel`. | -| `cancelRecording` | `() => void` | Discard the session and call `onCancel`. | -| `commitRecording` | `() => void` | Commit current steps; do nothing if empty. | - -Read state through the recorder object. Destructuring its reactive getters once captures a snapshot. - -## Options - -- `recordBy`: `'code'` by default, or `'key'` for logical characters. Missing physical codes never fall back to logical recording. -- `onRecord(sequence)`: called when a nonempty sequence is committed. Clearing calls only `onClear`. -- `onCancel` and `onClear`: handle cancellation and removal of a saved binding. -- `commitKeys`: `'enter'` by default. Use `'none'` for manual or idle-timeout commit; plain Enter can then be recorded as a chord. -- `commitOnEnter`: with `commitKeys: 'enter'`, set this to `false` to record Enter as a chord and finish another way. -- `idleTimeoutMs`: commit after this many milliseconds of inactivity following a completed chord. No timer runs while waiting for the first chord. -- `platform`: override platform detection for portable `Mod` conversion. - -### Shared defaults - -Pass defaults to `createHotkeysScope`. The scope accepts `hotkey`, `hotkeySequence`, `hotkeyRecorder`, and `hotkeySequenceRecorder` options. Use the returned contextual helpers and recorder factories. Pass the scope through component arguments to share it with descendants; helpers and recorders still clean up with their own owners. Pass a getter for tracked defaults. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. See [shared defaults](../quick-start.md#shared-defaults) for a complete example. Options getters are read during recording and commit, so policy and callback changes also apply to active sessions. - -### `ignoreInputs` - -The default is `true`, so normal typing in inputs, textareas, selects, and contentEditable elements passes through. Escape still cancels. Set `ignoreInputs: false` to record in a focused input. - -## Validation and conflicts - -`validate(sequence, { events, parsedSequence })` runs when committing and returns `true`, `false`, or a rejection message. `detectConflicts` checks single bindings and sequence prefixes. A rejected commit keeps the session active and the steps intact, so the user can edit them with Backspace. - -The recorder supports the same `detectConflicts`, `validate`, and `onReject` options as the [single-hotkey recorder](./hotkey-recording.md#validation-and-conflicts). Use live registration IDs to exclude the binding being edited. Physical/logical overlap is based on the captured events, not a guessed keyboard layout. - -## Behavior - -| Input | Behavior | -| --- | --- | -| Valid chord | Append to `steps` and continue listening. | -| Unmodified Enter with default commit settings and nonempty steps | Commit and call `onRecord`. | -| Escape | Cancel and call `onCancel`. | -| Unmodified Backspace or Delete with nonempty steps | Remove the last step without committing. | -| Unmodified Backspace or Delete with empty steps | Stop and call only `onClear`. | -| Modifier-only press, repeat, or IME composition | Do not append a step. | - -Recorded chords use portable `Mod`. Recording events, commit keys, and their releases are isolated from application hotkeys and sequences. Code mode rejects AltGraph character entry; key mode preserves the produced character without synthetic Control/Alt. - -## Under the hood - -Ember owns the core Store subscription and recorder through the supplied owner. Destruction cancels active recording and releases listeners. - -See the [useHotkeySequenceRecorder example](../examples/useHotkeySequenceRecorder) for editable sequence settings and the [kitchen sink](../examples/kitchen-sink) for manual commit, idle timeout, validation, and conflict feedback. diff --git a/docs/framework/ember/guides/sequences.md b/docs/framework/ember/guides/sequences.md index 47acb7c8..006a9407 100644 --- a/docs/framework/ember/guides/sequences.md +++ b/docs/framework/ember/guides/sequences.md @@ -3,142 +3,161 @@ title: Sequences Guide id: sequences --- -A sequence is an ordered list of chords. Use `useHotkeySequence` for one sequence and `useHotkeySequences` for a changing list. A chord can be a logical binding such as `G`, a physical position such as `[KeyG]`, or a modifier combination such as `Mod+[KeyK]`. +A sequence is a hotkey made of chords pressed one after another rather than together. Think Vim-style navigation, cheat codes, or multi-step commands. Sequences are ordinary hotkeys: register them with `useHotkey` and `useHotkeys`, list them with `useHotkeyRegistrations`, record them with `useHotkeyRecorder`, and format them with `formatForDisplay`. -## Reactive options - -Sequence options support the same [property getter patterns](./hotkeys.md#property-getters) as hotkey options, including `enabled`, `timeout`, and `target`. Updates preserve the registration when its sequence, target, and platform stay the same. +Sequence steps use the same syntax as chords. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. ## Basic usage +Pass an array of chords wherever you pass a hotkey: ```gts -import Component from '@glimmer/component' -import { tracked } from '@glimmer/tracking' import { array } from '@ember/helper' -import { useHotkeySequence, useHotkeySequences } from '@tanstack/ember-hotkeys' -import type { HotkeySequenceDefinition } from '@tanstack/ember-hotkeys' - -export default class Navigation extends Component { - @tracked enabled = true - @tracked timeout = 1000 - goToTop = () => window.scrollTo({ top: 0 }) - definitions: Array = [ - { sequence: ['D', 'D'], callback: () => console.log('Delete line') }, - { sequence: ['D', 'W'], callback: () => console.log('Delete word'), options: { timeout: 500 } }, - ] - +import Component from '@glimmer/component' +import { useHotkey } from '@tanstack/ember-hotkeys' + +export default class Editor extends Component { + // Vim-style: press g then g to scroll to top + scrollToTop = () => window.scrollTo({ top: 0, behavior: 'smooth' }) + + } ``` -The user must press and release each chord in order. The timeout applies between consecutive steps, rather than to the total duration of the sequence. +The user must press the steps in order, each within the timeout window. A one-step array such as `['Escape']` is the chord `'Escape'`. ## Many sequences at once -`useHotkeySequences` accepts an array of [HotkeySequenceDefinition](../../../reference/adapter/interfaces/HotkeySequenceDefinition) entries. Each contains `sequence`, `callback`, and optional `options`. An empty array removes all registrations owned by that call. Per-definition options override common options. Pass a tracked array or a getter derived from tracked state to the helper. Replace the array when changing its entries. +`useHotkeys` registers chords and sequences together, including dynamic lists: + +```gts +import { useHotkeys } from '@tanstack/ember-hotkeys' +import type { EmberHotkeyDefinition } from '@tanstack/ember-hotkeys' + +export default class Editor extends Component { + get definitions(): Array { + return [ + { hotkey: ['G', 'G'], callback: this.scrollToTop }, + { hotkey: ['D', 'D'], callback: this.deleteLine, options: { timeout: 500 } }, + { hotkey: 'Mod+S', callback: this.save }, + ] + } + + +} +``` ## Sequence options -### `timeout` +Sequences accept the same options as chords, except `requireReset`, plus `timeout`. -The default is `1000` milliseconds between steps. A shorter timeout, such as `500`, requires faster entry; `2000` gives users more time. Expired sequences reset their progress. +### `timeout` -### `enabled` +The maximum time (in milliseconds) allowed between consecutive steps. If the user takes longer than this between any two steps, the sequence resets. Defaults to `1000` (1 second). -The default is `true`. Disabled sequences remain in the registry but do not execute. Pass a reactive flag to change this without discarding the registration. +```gts +{{! Fast sequence - user must type quickly }} +{{useHotkey (array 'D' 'D') this.deleteLine timeout=500}} -### Shared defaults +{{! Slow sequence - user has more time between keys }} +{{useHotkey (array 'Shift+Z' 'Shift+Z') this.forceQuit timeout=2000}} +``` -Pass defaults to `createHotkeysScope`. The scope accepts `hotkey`, `hotkeySequence`, `hotkeyRecorder`, and `hotkeySequenceRecorder` options. Use the returned contextual helpers and recorder factories. Pass the scope through component arguments to share it with descendants; helpers and recorders still clean up with their own owners. Pass a getter for tracked defaults. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. See [shared defaults](../quick-start.md#shared-defaults) for a complete example. +`timeout` is only accepted when the hotkey is a sequence. Set a default for every sequence with `createHotkeysScope`: -### Targets, input handling, and propagation +```ts +import { createHotkeysScope } from '@tanstack/ember-hotkeys' -Sequences support `target`, `ignoreInputs`, `preventDefault`, `stopPropagation`, `platform`, and `conflictBehavior`. Use a focusable element to scope a sequence to a widget, and `null` while that element is unavailable. When omitted, `ignoreInputs` follows the first step: Control/Meta combinations and Escape work in inputs, while other first steps are ignored there. Set it explicitly to override that behavior. See the [target examples](./hotkeys.md#target). +hotkeys = createHotkeysScope({ hotkey: { timeout: 1500 } }) +// Use the scope's contextual helpers: {{this.hotkeys.useHotkey ...}} +``` -### Metadata +### `enabled` and `meta` -Attach a `meta` object with a name, description, and group. Read it through live registration views to build help panels. Metadata does not change execution scope. +These work exactly as they do for chords. Disabled sequences stay registered and visible in devtools, and `meta` flows to registrations, devtools, and cheat sheets. See the [Hotkeys Guide](./hotkeys). -```hbs -{{useHotkeySequence (array 'G' 'G') this.goToTop meta=(hash name='Go to top' group='Navigation')}} +```gts +{{useHotkey + (array 'G' 'G') + this.scrollToTop + enabled=this.vimMode + meta=(hash name='Go to top' description='Scroll to the top of the page') +}} ``` -Import `array` and `hash` from `@ember/helper` for this template expression. - ## Sequences with modifiers -Use `Mod+K`, then `Mod+C` for an editor-style comment shortcut. Logical and physical steps can be mixed. +Each step in a sequence can include modifiers, and consecutive steps can repeat the same modifier: -```hbs -{{useHotkeySequence (array 'Mod+K' 'Mod+C') this.comment}} -``` - -## Chained modifier chords - -Physical codes retain the same positions even when the keyboard layout changes. +```gts +{{! Ctrl+K followed by Ctrl+C (VS Code-style comment) }} +{{useHotkey (array 'Mod+K' 'Mod+C') this.commentSelection}} -```hbs -{{useHotkeySequence (array 'Shift+[KeyR]' 'Shift+[KeyT]') this.nextAction}} +{{! Physical R then T positions, both with Shift }} +{{useHotkey (array 'Shift+[KeyR]' 'Shift+[KeyT]') this.doNextAction}} ``` -### Modifier-only keys between steps +Brackets retain physical positions even when the keys produce different letters. While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored. They neither advance the sequence nor reset progress, so a user can tap or hold Shift between `Shift+R` and `Shift+T`. -Pressing Shift, Control, Alt, or Meta alone neither advances nor resets an in-progress sequence. Users can release and press Shift between the R and T chords. Modifier-only events, automatic repeats, and IME composition also do not refresh the timeout. +## Vim-style navigation -## Common sequence patterns +```gts +get vimHotkeys(): Array { + return [ + { hotkey: ['G', 'G'], callback: this.scrollToTop }, + { hotkey: ['G', 'Shift+G'], callback: this.scrollToBottom }, + { hotkey: ['D', 'D'], callback: this.deleteLine }, + { hotkey: ['D', 'W'], callback: this.deleteWord }, + { hotkey: ['C', 'I', 'W'], callback: this.changeInnerWord }, + ] +} -| Action | Steps | -| --- | --- | -| Go to top | `['G', 'G']` | -| Go to bottom | `['G', 'Shift+G']` | -| Delete line | `['D', 'D']` | -| Delete word | `['D', 'W']` | -| Change inner word | `['C', 'I', 'W']` | -| Open help | `['H', 'E', 'L', 'P']` | + +``` -For a Konami-style sequence, use the full ordered list and a longer timeout: +## Displaying sequences -```hbs -{{useHotkeySequence (array 'ArrowUp' 'ArrowUp' 'ArrowDown' 'ArrowDown' 'ArrowLeft' 'ArrowRight' 'ArrowLeft' 'ArrowRight' 'B' 'A') this.unlock timeout=2000}} -``` +`formatForDisplay` formats each step and joins them with `sequenceSeparator`, a space by default: -## How sequences work +```ts +formatForDisplay(['Mod+K', 'Mod+C'], { platform: 'mac' }) // '⌘ K ⌘ C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' then ' }) // 'G then G' +``` -The shared `SequenceManager` tracks every registration independently. Matching the next expected step advances progress. Completing all steps invokes the callback. The manager resets progress when the timeout expires and prefers exact key matches over weaker logical-key fallbacks. +Registration views report each sequence's progress in `matchedStepCount`, so a help panel can show how far the user has typed. See the [Hotkeys Guide](./hotkeys). -### Overlapping sequences +## How sequences work -`['D', 'D']`, `['D', 'W']`, and `['D', 'I', 'W']` can share a prefix. Pressing D advances each matching sequence; later steps determine which one completes. +The `HotkeyManager` tracks progress for every sequence registration. When a key is pressed: -### Conflicting registrations +1. It checks whether the key matches the next expected step of each sequence +2. If it matches, that sequence advances +3. If the timeout expires between steps, the sequence resets +4. When all steps are completed, the callback fires -Duplicate detection compares resolved steps on the same target. Modifier aliases, modifier order, and logical letter casing do not create distinct bindings. Physical and logical identities remain distinct. Sharing a prefix alone is not a duplicate registration, but recorder conflict checks also inspect prefixes and physical/logical overlap. +Modifier-only events, IME composition, and automatic keydown repeats neither advance a sequence nor refresh its timeout: holding G does not complete a two-press G sequence. Exact matches take priority over weaker logical-key fallbacks. -### Updates and cleanup +Multiple sequences can share a prefix, such as `['D', 'D']`, `['D', 'W']`, and `['D', 'I', 'W']`. Progress is tracked for each sequence independently, so after pressing `D` the manager waits for the next key to determine which sequence completes. -`useHotkey`, `useHotkeys`, `useHotkeySequence`, and `useHotkeySequences` are template helpers. Invoke them with `{{...}}`, not as JavaScript hooks. Tracked arguments update registrations after rendering. Removing a helper from the template unregisters its bindings. +### Conflicting registrations -State readers and recorders are JavaScript functions. Pass the containing component as their first argument, usually `this`. The adapter releases subscriptions and recorders when that owner is destroyed. State readers expose `.value`; recorder fields and registration arrays are reactive getters. Read those properties in templates or getters instead of destructuring an initial snapshot. +For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate hotkeys. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection and app hotkey conflicts are stricter: there, a sequence also conflicts with a chord equal to its first step and with any sequence it starts. -## The sequence manager +## Standalone matching -Use `getSequenceManager()` for the shared registry. For standalone matching without registration, create your own matcher and remove your event listener when its owner is destroyed. +For sequence matching without the singleton manager, use `createSequenceMatcher`: ```ts import { createSequenceMatcher } from '@tanstack/ember-hotkeys' const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 }) -const onKeyDown = (event: KeyboardEvent) => { - if (matcher.match(event)) console.log('Sequence completed') - console.log(matcher.getProgress()) -} -document.addEventListener('keydown', onKeyDown) -// During teardown: -document.removeEventListener('keydown', onKeyDown) -``` -Try [useHotkeySequence](../examples/useHotkeySequence) and [useHotkeySequences](../examples/useHotkeySequences). +document.addEventListener('keydown', (e) => { + if (matcher.match(e)) { + console.log('Sequence completed!') + } + console.log('Progress:', matcher.getProgress()) // e.g., 1 +}) +``` diff --git a/docs/framework/ember/quick-start.md b/docs/framework/ember/quick-start.md index 42c56560..f2badacf 100644 --- a/docs/framework/ember/quick-start.md +++ b/docs/framework/ember/quick-start.md @@ -36,7 +36,7 @@ Press Command+S on macOS or Control+S on Windows and Linux. The count increases ## Registration and cleanup -`useHotkey`, `useHotkeys`, `useHotkeySequence`, and `useHotkeySequences` are template helpers. Invoke them with `{{...}}`, not as JavaScript hooks. Tracked arguments update registrations after rendering. Removing a helper from the template unregisters its bindings. +`useHotkey` and `useHotkeys` are template helpers. Invoke them with `{{...}}`, not as JavaScript hooks. Tracked arguments update registrations after rendering. Removing a helper from the template unregisters its bindings. State readers and recorders are JavaScript functions. Pass the containing component as their first argument, usually `this`. The adapter releases subscriptions and recorders when that owner is destroyed. State readers expose `.value`; recorder fields and registration arrays are reactive getters. Read those properties in templates or getters instead of destructuring an initial snapshot. @@ -78,10 +78,10 @@ Use Ember tracked state for the enabled flag. ```hbs -{{useHotkeySequence (array 'G' 'G') this.goToTop}} +{{useHotkey (array 'G' 'G') this.goToTop}} ``` -Import `array` from `@ember/helper` and `useHotkeySequence` from `@tanstack/ember-hotkeys`. +An array of chords registers a sequence. Import `array` from `@ember/helper` and `useHotkey` from `@tanstack/ember-hotkeys`. Release G between presses. Automatic key repeats do not advance sequences. @@ -114,7 +114,7 @@ Use `formatForDisplay(binding)` for platform-specific labels. Keep the original ## Shared defaults -Pass defaults to `createHotkeysScope`. The scope accepts `hotkey`, `hotkeySequence`, `hotkeyRecorder`, and `hotkeySequenceRecorder` options. Use the returned contextual helpers, modifiers, and recorder factories. Pass the scope through component arguments to share it with descendants; helpers and recorders still clean up with their own owners. Pass a getter for tracked defaults. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. +Pass defaults to `createHotkeysScope`. The scope accepts `hotkey` and `hotkeyRecorder` options. `requireReset` applies to chords and `timeout` to sequences. Use the returned contextual helpers, modifiers, and recorder factories. Pass the scope through component arguments to share it with descendants; helpers and recorders still clean up with their own owners. Pass a getter for tracked defaults. Call-specific options override scope defaults, and per-definition options override common options. Omitted options use the core defaults. ```gts import Component from '@glimmer/component' @@ -125,9 +125,7 @@ export default class Editor extends Component { @tracked enabled = true hotkeys = createHotkeysScope(() => ({ hotkey: { enabled: this.enabled, requireReset: true }, - hotkeySequence: { timeout: 1500 }, - hotkeyRecorder: { ignoreInputs: false }, - hotkeySequenceRecorder: { idleTimeoutMs: 2000 }, + hotkeyRecorder: { ignoreInputs: false, idleTimeoutMs: 2000 }, })) save = () => console.log('Saved') next = () => console.log('Next') @@ -139,14 +137,18 @@ export default class Editor extends Component { } ``` -A child can receive this scope as `@hotkeys={{this.hotkeys}}` and call `{{@hotkeys.useHotkey ...}}`. To use recorder defaults, call `this.hotkeys.useHotkeyRecorder(this, options)` or `this.hotkeys.useHotkeySequenceRecorder(this, options)`. The standalone imports use core defaults. The scope itself owns no registrations and needs no `destroy()` call. +A child can receive this scope as `@hotkeys={{this.hotkeys}}` and call `{{@hotkeys.useHotkey ...}}`. To use recorder defaults, call `this.hotkeys.useHotkeyRecorder(this, options)`. The standalone imports use core defaults. The scope itself owns no registrations and needs no `destroy()` call. + +## Declaring app hotkeys + +To list every hotkey in a cheat sheet, including hotkeys whose components haven't mounted, and to let users customize them, declare them once with `defineAppHotkeys` and provide them with `provideAppHotkeys`. See the [App Hotkeys Guide](./guides/app-hotkeys). ## Examples and next steps - [Hotkeys](./guides/hotkeys) - [Sequences](./guides/sequences) +- [App hotkeys](./guides/app-hotkeys) - [Hotkey recording](./guides/hotkey-recording) -- [Sequence recording](./guides/sequence-recording) - [Key state tracking](./guides/key-state-tracking) - [Formatting and display](./guides/formatting-display) - [Kitchen sink](./examples/kitchen-sink) diff --git a/docs/framework/ember/reference/classes/onHotkey.md b/docs/framework/ember/reference/classes/onHotkey.md index e54b6e7d..edb1f5c1 100644 --- a/docs/framework/ember/reference/classes/onHotkey.md +++ b/docs/framework/ember/reference/classes/onHotkey.md @@ -3,16 +3,16 @@ id: onHotkey title: onHotkey --- -Defined in: [packages/ember-hotkeys/src/onHotkey.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/onHotkey.ts#L14) +Defined in: [packages/ember-hotkeys/src/onHotkey.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/onHotkey.ts#L11) -Attaches a shortcut to the element that owns the modifier. +Attaches a hotkey (chord, sequence, or app hotkey) to the element that owns the modifier. ## Extends - `default`\<\{ `Args`: \{ `Named`: [`ElementHotkeyOptions`](../type-aliases/ElementHotkeyOptions.md); - `Positional`: \[`RegisterableHotkey`, `HotkeyCallback`\]; + `Positional`: \[`RegisterableHotkey` \| `AppHotkey`, `HotkeyCallback`\]; \}; `Element`: `HTMLElement`; \}\> @@ -40,7 +40,9 @@ An instance of an Owner (for service injection etc.). `ArgsFor`\<\{ `Args`: \{ `Named`: [`ElementHotkeyOptions`](../type-aliases/ElementHotkeyOptions.md); - `Positional`: \[`RegisterableHotkey`, `HotkeyCallback`\]; + `Positional`: \[ + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\>, `HotkeyCallback`\]; \}; `Element`: `HTMLElement`; \}\> @@ -57,7 +59,7 @@ The positional and named arguments passed to the modifier. Modifier<{ Element: HTMLElement Args: { - Positional: [RegisterableHotkey, HotkeyCallback] + Positional: [RegisterableHotkey | AppHotkey, HotkeyCallback] Named: ElementHotkeyOptions } }>.constructor @@ -81,7 +83,9 @@ Defined in: node\_modules/.pnpm/@glint+template@1.9.0/node\_modules/@glint/templ ##### args -...\[`RegisterableHotkey`, `HotkeyCallback`, `NamedArgs`\<[`ElementHotkeyOptions`](../type-aliases/ElementHotkeyOptions.md)\>\] +...\[ + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\>, `HotkeyCallback`, `NamedArgs`\<[`ElementHotkeyOptions`](../type-aliases/ElementHotkeyOptions.md)\>\] #### Returns @@ -104,7 +108,7 @@ modify( options): void; ``` -Defined in: [packages/ember-hotkeys/src/onHotkey.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/onHotkey.ts#L25) +Defined in: [packages/ember-hotkeys/src/onHotkey.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/onHotkey.ts#L20) Called when the modifier is installed and any time any tracked state used in the modifier changes. @@ -146,7 +150,9 @@ The element to which the modifier is applied. ##### positional -\[`RegisterableHotkey`, `HotkeyCallback`\] +\[ + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\>, `HotkeyCallback`\] The positional arguments to the modifier. diff --git a/docs/framework/ember/reference/classes/onHotkeys.md b/docs/framework/ember/reference/classes/onHotkeys.md index fcac0cbf..1eee0a83 100644 --- a/docs/framework/ember/reference/classes/onHotkeys.md +++ b/docs/framework/ember/reference/classes/onHotkeys.md @@ -3,16 +3,16 @@ id: onHotkeys title: onHotkeys --- -Defined in: [packages/ember-hotkeys/src/onHotkeys.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/onHotkeys.ts#L16) +Defined in: [packages/ember-hotkeys/src/onHotkeys.ts:9](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/onHotkeys.ts#L9) -Attach a reactive list of shortcuts directly to an element. +Attach a reactive list of hotkeys directly to an element. ## Extends - `default`\<\{ `Args`: \{ `Named`: [`ElementHotkeyOptions`](../type-aliases/ElementHotkeyOptions.md); - `Positional`: \[`HotkeyDefinition`[]\]; + `Positional`: \[`ReadonlyArray`\<[`EmberHotkeyDefinition`](../type-aliases/EmberHotkeyDefinition.md)\>\]; \}; `Element`: `HTMLElement`; \}\> @@ -40,7 +40,7 @@ An instance of an Owner (for service injection etc.). `ArgsFor`\<\{ `Args`: \{ `Named`: [`ElementHotkeyOptions`](../type-aliases/ElementHotkeyOptions.md); - `Positional`: \[`HotkeyDefinition`[]\]; + `Positional`: \[readonly [`EmberHotkeyDefinition`](../type-aliases/EmberHotkeyDefinition.md)[]\]; \}; `Element`: `HTMLElement`; \}\> @@ -57,7 +57,7 @@ The positional and named arguments passed to the modifier. Modifier<{ Element: HTMLElement Args: { - Positional: [Array] + Positional: [ReadonlyArray] Named: ElementHotkeyOptions } }>.constructor @@ -81,7 +81,7 @@ Defined in: node\_modules/.pnpm/@glint+template@1.9.0/node\_modules/@glint/templ ##### args -...\[`HotkeyDefinition`[], `NamedArgs`\<[`ElementHotkeyOptions`](../type-aliases/ElementHotkeyOptions.md)\>\] +...\[readonly [`EmberHotkeyDefinition`](../type-aliases/EmberHotkeyDefinition.md)[], `NamedArgs`\<[`ElementHotkeyOptions`](../type-aliases/ElementHotkeyOptions.md)\>\] #### Returns @@ -104,7 +104,7 @@ modify( options): void; ``` -Defined in: [packages/ember-hotkeys/src/onHotkeys.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/onHotkeys.ts#L28) +Defined in: [packages/ember-hotkeys/src/onHotkeys.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/onHotkeys.ts#L18) Called when the modifier is installed and any time any tracked state used in the modifier changes. @@ -146,7 +146,7 @@ The element to which the modifier is applied. ##### positional -\[`HotkeyDefinition`[]\] +\[readonly [`EmberHotkeyDefinition`](../type-aliases/EmberHotkeyDefinition.md)[]\] The positional arguments to the modifier. diff --git a/docs/framework/ember/reference/classes/useHotkey.md b/docs/framework/ember/reference/classes/useHotkey.md index bf8c48bf..72aea9dc 100644 --- a/docs/framework/ember/reference/classes/useHotkey.md +++ b/docs/framework/ember/reference/classes/useHotkey.md @@ -3,16 +3,19 @@ id: useHotkey title: useHotkey --- -Defined in: [packages/ember-hotkeys/src/useHotkey.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkey.ts#L17) +Defined in: [packages/ember-hotkeys/src/useHotkey.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkey.ts#L16) -Use {{useHotkey "Mod+S" this.save enabled=this.enabled}} in a template. +Registers a hotkey for the helper's template lifecycle: a chord such as +`"Mod+S"`, a sequence such as `(array "G" "G")`, or an app hotkey. + +Use `{{useHotkey "Mod+S" this.save enabled=this.enabled}}` in a template. ## Extends - `default`\<\{ `Args`: \{ - `Named`: `HotkeyOptions`; - `Positional`: \[`RegisterableHotkey`, `HotkeyCallback`\]; + `Named`: [`EmberHotkeyCommonOptions`](../type-aliases/EmberHotkeyCommonOptions.md); + `Positional`: \[`RegisterableHotkey` \| `AppHotkey`, `HotkeyCallback`\]; \}; `Return`: `void`; \}\> @@ -42,8 +45,8 @@ Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_su ```ts Helper<{ Args: { - Positional: [RegisterableHotkey, HotkeyCallback] - Named: HotkeyOptions + Positional: [RegisterableHotkey | AppHotkey, HotkeyCallback] + Named: EmberHotkeyCommonOptions } Return: void }>.constructor @@ -63,7 +66,9 @@ Defined in: node\_modules/.pnpm/@glint+template@1.9.0/node\_modules/@glint/templ ##### args -...\[`RegisterableHotkey`, `HotkeyCallback`, `NamedArgs`\<`HotkeyOptions`\>\] +...\[ + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\>, `HotkeyCallback`, `NamedArgs`\<[`EmberHotkeyCommonOptions`](../type-aliases/EmberHotkeyCommonOptions.md)\>\] #### Returns @@ -884,7 +889,7 @@ Helper.cacheFor compute(positional, options): void; ``` -Defined in: [packages/ember-hotkeys/src/useHotkey.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkey.ts#L28) +Defined in: [packages/ember-hotkeys/src/useHotkey.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkey.ts#L25) Override this function when writing a class-based helper. @@ -892,13 +897,15 @@ Override this function when writing a class-based helper. ##### positional -\[`RegisterableHotkey`, `HotkeyCallback`\] +\[ + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\>, `HotkeyCallback`\] The positional arguments to the helper ##### options -`HotkeyOptions` +[`EmberHotkeyCommonOptions`](../type-aliases/EmberHotkeyCommonOptions.md) #### Returns diff --git a/docs/framework/ember/reference/classes/useHotkeySequence.md b/docs/framework/ember/reference/classes/useHotkeySequence.md deleted file mode 100644 index 5ab0df46..00000000 --- a/docs/framework/ember/reference/classes/useHotkeySequence.md +++ /dev/null @@ -1,2515 +0,0 @@ ---- -id: useHotkeySequence -title: useHotkeySequence ---- - -Defined in: [packages/ember-hotkeys/src/useHotkeySequence.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeySequence.ts#L17) - -Use {{useHotkeySequence this.sequence this.run}} in a template. - -## Extends - -- `default`\<\{ - `Args`: \{ - `Named`: `SequenceOptions`; - `Positional`: \[`HotkeySequence`, `HotkeyCallback`\]; - \}; - `Return`: `void`; -\}\> - -## Constructors - -### Constructor - -```ts -new useHotkeySequence(owner?): UseHotkeySequence; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/index.d.ts:28 - -#### Parameters - -##### owner? - -`Owner` - -#### Returns - -`UseHotkeySequence` - -#### Inherited from - -```ts -Helper<{ - Args: { Positional: [HotkeySequence, HotkeyCallback]; Named: SequenceOptions } - Return: void -}>.constructor -``` - -## Properties - -### \[Invoke\] - -```ts -[Invoke]: (...args) => void; -``` - -Defined in: node\_modules/.pnpm/@glint+template@1.9.0/node\_modules/@glint/template/-private/integration.d.ts:22 - -#### Parameters - -##### args - -...\[`HotkeySequence`, `HotkeyCallback`, `NamedArgs`\<`SequenceOptions`\>\] - -#### Returns - -`void` - -#### Inherited from - -```ts -Helper.[Invoke] -``` - -*** - -### \[OWNER\]? - -```ts -optional [OWNER]?: Owner; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:77 - -**`Internal`** - -#### Inherited from - -```ts -Helper.[OWNER] -``` - -*** - -### \[RECOMPUTE\_TAG\] - -```ts -[RECOMPUTE_TAG]: DirtyableTag; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/-internals/glimmer/lib/helper.d.ts:103 - -#### Inherited from - -```ts -Helper.[RECOMPUTE_TAG] -``` - -*** - -### concatenatedProperties? - -```ts -optional concatenatedProperties?: string | string[]; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:637 - -#### Inherited from - -```ts -Helper.concatenatedProperties -``` - -*** - -### mergedProperties? - -```ts -optional mergedProperties?: unknown[]; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:638 - -#### Inherited from - -```ts -Helper.mergedProperties -``` - -*** - -### \_lazyInjections? - -```ts -readonly static optional _lazyInjections?: () => void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:636 - -#### Returns - -`void` - -#### Inherited from - -```ts -Helper._lazyInjections -``` - -*** - -### \_onLookup? - -```ts -readonly static optional _onLookup?: (debugContainerKey) => void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:635 - -#### Parameters - -##### debugContainerKey - -`string` - -#### Returns - -`void` - -#### Inherited from - -```ts -Helper._onLookup -``` - -*** - -### \_without - -```ts -static _without: any[] | undefined; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:113 - -**`Internal`** - -#### Inherited from - -```ts -Helper._without -``` - -*** - -### \[INIT\_FACTORY\]? - -```ts -static optional [INIT_FACTORY]?: null; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:114 - -#### Inherited from - -```ts -Helper.[INIT_FACTORY] -``` - -*** - -### \[IS\_CLASSIC\_HELPER\] - -```ts -static [IS_CLASSIC_HELPER]: boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/-internals/glimmer/lib/helper.d.ts:100 - -#### Inherited from - -```ts -Helper.[IS_CLASSIC_HELPER] -``` - -*** - -### ~~helper~~ - -```ts -static helper: { - (helperFn): FunctionBasedHelper<{ - Args: { - Named: N; - Positional: P; - }; - Return: R; -}>; - (helperFn): FunctionBasedHelper; -}; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/-internals/glimmer/lib/helper.d.ts:102 - -#### Call Signature - -```ts -(helperFn): FunctionBasedHelper<{ - Args: { - Named: N; - Positional: P; - }; - Return: R; -}>; -``` - -In many cases it is not necessary to use the full `Helper` class. -The `helper` method create pure-function helpers without instances. -For example: - -```app/helpers/format-currency.js -import { helper } from '@ember/component/helper'; - -export default helper(function([cents], {currency}) { - return `${currency}${cents * 0.01}`; -}); -``` - -##### Type Parameters - -###### P - -`P` *extends* `DefaultPositional` - -###### N - -`N` *extends* `object` - -###### R - -`R` = `unknown` - -##### Parameters - -###### helperFn - -(`positional`, `named`) => `R` - -##### Returns - -`FunctionBasedHelper`\<\{ - `Args`: \{ - `Named`: `N`; - `Positional`: `P`; - \}; - `Return`: `R`; -\}\> - -##### Static - -##### Method - -helper - -##### For - -@ember/component/helper - -##### Since - -1.13.0 - -#### Call Signature - -```ts -(helperFn): FunctionBasedHelper; -``` - -In many cases it is not necessary to use the full `Helper` class. -The `helper` method create pure-function helpers without instances. -For example: - -```app/helpers/format-currency.js -import { helper } from '@ember/component/helper'; - -export default helper(function([cents], {currency}) { - return `${currency}${cents * 0.01}`; -}); -``` - -##### Type Parameters - -###### S - -`S` - -##### Parameters - -###### helperFn - -(`positional`, `named`) => `GetOr`\<`S`\> - -##### Returns - -`FunctionBasedHelper`\<`S`\> - -##### Static - -##### Method - -helper - -##### For - -@ember/component/helper - -##### Since - -1.13.0 - -#### Deprecated - -#### Inherited from - -```ts -Helper.helper -``` - -*** - -### isClass - -```ts -readonly static isClass: boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:633 - -#### Inherited from - -```ts -Helper.isClass -``` - -*** - -### isHelperFactory - -```ts -static isHelperFactory: boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/-internals/glimmer/lib/helper.d.ts:99 - -#### Inherited from - -```ts -Helper.isHelperFactory -``` - -*** - -### isMethod - -```ts -readonly static isMethod: boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:634 - -#### Inherited from - -```ts -Helper.isMethod -``` - -*** - -### mixins - -```ts -static mixins: Mixin[] | undefined; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:105 - -**`Internal`** - -#### Inherited from - -```ts -Helper.mixins -``` - -*** - -### ownerConstructor - -```ts -static ownerConstructor: any; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:111 - -**`Internal`** - -#### Inherited from - -```ts -Helper.ownerConstructor -``` - -*** - -### properties - -```ts -static properties: - | { -[key: string]: any; -} - | undefined; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:107 - -**`Internal`** - -#### Inherited from - -```ts -Helper.properties -``` - -*** - -### PrototypeMixin - -```ts -static PrototypeMixin: any; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:629 - -#### Inherited from - -```ts -Helper.PrototypeMixin -``` - -*** - -### superclass - -```ts -static superclass: any; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:630 - -#### Inherited from - -```ts -Helper.superclass -``` - -## Accessors - -### \_debugContainerKey - -#### Get Signature - -```ts -get _debugContainerKey(): false | `${string}:${string}`; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/index.d.ts:30 - -##### Returns - -`false` \| `` `${string}:${string}` `` - -#### Inherited from - -```ts -Helper._debugContainerKey -``` - -*** - -### isDestroyed - -#### Get Signature - -```ts -get isDestroyed(): boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:270 - -Destroyed object property flag. - -if this property is `true` the observers and bindings were already -removed by the effect of calling the `destroy()` method. - -##### Default - -```ts -false -@public -``` - -##### Returns - -`boolean` - -#### Set Signature - -```ts -set isDestroyed(_value): void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:271 - -##### Parameters - -###### \_value - -`boolean` - -##### Returns - -`void` - -#### Inherited from - -```ts -Helper.isDestroyed -``` - -*** - -### isDestroying - -#### Get Signature - -```ts -get isDestroying(): boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:282 - -Destruction scheduled flag. The `destroy()` method has been called. - -The object stays intact until the end of the run loop at which point -the `isDestroyed` flag is set. - -##### Default - -```ts -false -@public -``` - -##### Returns - -`boolean` - -#### Set Signature - -```ts -set isDestroying(_value): void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:283 - -##### Parameters - -###### \_value - -`boolean` - -##### Returns - -`void` - -#### Inherited from - -```ts -Helper.isDestroying -``` - -## Methods - -### \_super() - -```ts -_super(...args): any; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:73 - -**`Internal`** - -#### Parameters - -##### args - -...`any`[] - -#### Returns - -`any` - -#### Inherited from - -```ts -Helper._super -``` - -*** - -### addObserver() - -#### Call Signature - -```ts -addObserver( - key, - target, - method): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:322 - -Adds an observer on a property. - -This is the core method used to register an observer for a property. - -Once you call this method, any time the key's value is set, your observer -will be notified. Note that the observers are triggered any time the -value is set, regardless of whether it has actually changed. Your -observer should be prepared to handle that. - -There are two common invocation patterns for `.addObserver()`: - -- Passing two arguments: - - the name of the property to observe (as a string) - - the function to invoke (an actual function) -- Passing three arguments: - - the name of the property to observe (as a string) - - the target object (will be used to look up and invoke a - function on) - - the name of the function to invoke on the target object - (as a string). - -```app/components/my-component.js -import Component from '@ember/component'; - -export default Component.extend({ - init() { - this._super(...arguments); - - // the following are equivalent: - - // using three arguments - this.addObserver('foo', this, 'fooDidChange'); - - // using two arguments - this.addObserver('foo', (...args) => { - this.fooDidChange(...args); - }); - }, - - fooDidChange() { - // your custom logic code - } -}); -``` - -### Observer Methods - -Observer methods have the following signature: - -```app/components/my-component.js -import Component from '@ember/component'; - -export default Component.extend({ - init() { - this._super(...arguments); - this.addObserver('foo', this, 'fooDidChange'); - }, - - fooDidChange(sender, key, value, rev) { - // your code - } -}); -``` - -The `sender` is the object that changed. The `key` is the property that -changes. The `value` property is currently reserved and unused. The `rev` -is the last property revision of the object when it changed, which you can -use to detect if the key value has really changed or not. - -Usually you will not need the value or revision parameters at -the end. In this case, it is common to write observer methods that take -only a sender and key value as parameters or, if you aren't interested in -any of these values, to write an observer that has no parameters at all. - -While observers are still supported, there are [plans to deprecate them](https://github.com/emberjs/rfcs/pull/1115) -See the [in-progress deprecation guide](https://github.com/ember-learn/deprecation-app/pull/1407) -for guidance on how to avoid using observers. - -##### Type Parameters - -###### Target - -`Target` - -##### Parameters - -###### key - -keyof `UseHotkeySequence` - -The key to observe - -###### target - -`Target` - -The target object to invoke - -###### method - -`ObserverMethod`\<`Target`, `UseHotkeySequence`\> - -The method to invoke - -##### Returns - -`this` - -##### Method - -addObserver - -##### Inherited from - -```ts -Helper.addObserver -``` - -#### Call Signature - -```ts -addObserver(key, method): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:323 - -##### Parameters - -###### key - -keyof `UseHotkeySequence` - -###### method - -`ObserverMethod`\<`UseHotkeySequence`, `UseHotkeySequence`\> - -##### Returns - -`this` - -##### Inherited from - -```ts -Helper.addObserver -``` - -*** - -### cacheFor() - -```ts -cacheFor(key): unknown; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:394 - -Returns the cached value of a computed property, if it exists. -This allows you to inspect the value of a computed property -without accidentally invoking it if it is intended to be -generated lazily. - -#### Type Parameters - -##### K - -`K` *extends* keyof `UseHotkeySequence` - -#### Parameters - -##### key - -`K` - -#### Returns - -`unknown` - -The cached value of the computed property, if any - -#### Method - -cacheFor - -#### Inherited from - -```ts -Helper.cacheFor -``` - -*** - -### compute() - -```ts -compute(positional, options): void; -``` - -Defined in: [packages/ember-hotkeys/src/useHotkeySequence.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeySequence.ts#L25) - -Override this function when writing a class-based helper. - -#### Parameters - -##### positional - -\[`HotkeySequence`, `HotkeyCallback`\] - -The positional arguments to the helper - -##### options - -`SequenceOptions` - -#### Returns - -`void` - -#### Method - -compute - -#### Since - -1.13.0 - -#### Overrides - -```ts -Helper.compute -``` - -*** - -### decrementProperty() - -```ts -decrementProperty(keyName, decrement?): number; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:368 - -Set the value of a property to the current value minus some amount. - -```javascript -player.decrementProperty('lives'); -orc.decrementProperty('health', 5); -``` - -#### Parameters - -##### keyName - -keyof `UseHotkeySequence` - -The name of the property to decrement - -##### decrement? - -`number` - -The amount to decrement by. Defaults to 1 - -#### Returns - -`number` - -The new property value - -#### Method - -decrementProperty - -#### Inherited from - -```ts -Helper.decrementProperty -``` - -*** - -### destroy() - -```ts -destroy(): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:298 - -Destroys an object by setting the `isDestroyed` flag and removing its -metadata, which effectively destroys observers and bindings. - -If you try to set a property on a destroyed object, an exception will be -raised. - -Note that destruction is scheduled for the end of the run loop and does not -happen immediately. It will set an isDestroying flag immediately. - -#### Returns - -`this` - -receiver - -#### Method - -destroy - -#### Inherited from - -```ts -Helper.destroy -``` - -*** - -### get() - -#### Call Signature - -```ts -get(key): UseHotkeySequence[K]; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:119 - -Retrieves the value of a property from the object. - -This method is usually similar to using `object[keyName]` or `object.keyName`, -however it supports both computed properties and the unknownProperty -handler. - -Because `get` unifies the syntax for accessing all these kinds -of properties, it can make many refactorings easier, such as replacing a -simple property with a computed property, or vice versa. - -### Computed Properties - -Computed properties are methods defined with the `property` modifier -declared at the end, such as: - -```javascript -import { computed } from '@ember/object'; - -fullName: computed('firstName', 'lastName', function() { - return this.get('firstName') + ' ' + this.get('lastName'); -}) -``` - -When you call `get` on a computed property, the function will be -called and the return value will be returned instead of the function -itself. - -### Unknown Properties - -Likewise, if you try to call `get` on a property whose value is -`undefined`, the `unknownProperty()` method will be called on the object. -If this method returns any value other than `undefined`, it will be returned -instead. This allows you to implement "virtual" properties that are -not defined upfront. - -##### Type Parameters - -###### K - -`K` *extends* keyof `UseHotkeySequence` - -##### Parameters - -###### key - -`K` - -##### Returns - -`UseHotkeySequence`\[`K`\] - -The property value or undefined. - -##### Method - -get - -##### Inherited from - -```ts -Helper.get -``` - -#### Call Signature - -```ts -get(key): unknown; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:120 - -##### Parameters - -###### key - -`string` - -##### Returns - -`unknown` - -##### Inherited from - -```ts -Helper.get -``` - -*** - -### getProperties() - -#### Call Signature - -```ts -getProperties(list): { [Key in keyof useHotkeySequence]: useHotkeySequence[Key] }; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:142 - -To get the values of multiple properties at once, call `getProperties` -with a list of strings or an array: - -```javascript -record.getProperties('firstName', 'lastName', 'zipCode'); -// { firstName: 'John', lastName: 'Doe', zipCode: '10011' } -``` - -is equivalent to: - -```javascript -record.getProperties(['firstName', 'lastName', 'zipCode']); -// { firstName: 'John', lastName: 'Doe', zipCode: '10011' } -``` - -##### Type Parameters - -###### L - -`L` *extends* keyof `UseHotkeySequence`[] - -##### Parameters - -###### list - -`L` - -of keys to get - -##### Returns - -`{ [Key in keyof useHotkeySequence]: useHotkeySequence[Key] }` - -##### Method - -getProperties - -##### Inherited from - -```ts -Helper.getProperties -``` - -#### Call Signature - -```ts -getProperties(...list): { [Key in keyof useHotkeySequence]: useHotkeySequence[Key] }; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:145 - -##### Type Parameters - -###### L - -`L` *extends* keyof `UseHotkeySequence`[] - -##### Parameters - -###### list - -...`L` - -##### Returns - -`{ [Key in keyof useHotkeySequence]: useHotkeySequence[Key] }` - -##### Inherited from - -```ts -Helper.getProperties -``` - -#### Call Signature - -```ts -getProperties(list): { [Key in string]: unknown }; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:148 - -##### Type Parameters - -###### L - -`L` *extends* `string`[] - -##### Parameters - -###### list - -`L` - -##### Returns - -`{ [Key in string]: unknown }` - -##### Inherited from - -```ts -Helper.getProperties -``` - -#### Call Signature - -```ts -getProperties(...list): { [Key in string]: unknown }; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:151 - -##### Type Parameters - -###### L - -`L` *extends* `string`[] - -##### Parameters - -###### list - -...`L` - -##### Returns - -`{ [Key in string]: unknown }` - -##### Inherited from - -```ts -Helper.getProperties -``` - -*** - -### incrementProperty() - -```ts -incrementProperty(keyName, increment?): number; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:353 - -Set the value of a property to the current value plus some amount. - -```javascript -person.incrementProperty('age'); -team.incrementProperty('score', 2); -``` - -#### Parameters - -##### keyName - -keyof `UseHotkeySequence` - -The name of the property to increment - -##### increment? - -`number` - -The amount to increment by. Defaults to 1 - -#### Returns - -`number` - -The new property value - -#### Method - -incrementProperty - -#### Inherited from - -```ts -Helper.incrementProperty -``` - -*** - -### init() - -```ts -init(properties): void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/-internals/glimmer/lib/helper.d.ts:105 - -An overridable method called when objects are instantiated. By default, -does nothing unless it is overridden during class definition. - -Example: - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend({ - init() { - alert(`Name is ${this.get('name')}`); - } -}); - -let steve = Person.create({ - name: 'Steve' -}); - -// alerts 'Name is Steve'. -``` - -NOTE: If you do override `init` for a framework class like `Component` -from `@ember/component`, be sure to call `this._super(...arguments)` -in your `init` declaration! -If you don't, Ember may not have an opportunity to -do important setup work, and you'll see strange behavior in your -application. - -#### Parameters - -##### properties - -`object` \| `undefined` - -#### Returns - -`void` - -#### Method - -init - -#### Inherited from - -```ts -Helper.init -``` - -*** - -### notifyPropertyChange() - -```ts -notifyPropertyChange(keyName): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:234 - -Convenience method to call `propertyWillChange` and `propertyDidChange` in -succession. - -Notify the observer system that a property has just changed. - -Sometimes you need to change a value directly or indirectly without -actually calling `get()` or `set()` on it. In this case, you can use this -method instead. Calling this method will notify all observers that the -property has potentially changed value. - -#### Parameters - -##### keyName - -`string` - -The property key to be notified about. - -#### Returns - -`this` - -#### Method - -notifyPropertyChange - -#### Inherited from - -```ts -Helper.notifyPropertyChange -``` - -*** - -### recompute() - -```ts -recompute(): void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/-internals/glimmer/lib/helper.d.ts:142 - -On a class-based helper, it may be useful to force a recomputation of that -helpers value. This is akin to `rerender` on a component. - -In most cases, `recompute` is not needed because accessing tracked -properties in `compute` will automatically re-run the helper when -those properties change. Use `recompute` only when you need to -trigger a recomputation imperatively, for example in response to an -external event: - -```app/helpers/current-time.js -import Helper from '@ember/component/helper'; - -export default class CurrentTimeHelper extends Helper { - interval = null; - - compute() { - return new Date().toLocaleTimeString(); - } - - constructor() { - super(...arguments); - this.interval = setInterval(() => this.recompute(), 1000); - } - - willDestroy() { - super.willDestroy(); - clearInterval(this.interval); - } -} -``` - -#### Returns - -`void` - -#### Method - -recompute - -#### Since - -1.13.0 - -#### Inherited from - -```ts -Helper.recompute -``` - -*** - -### removeObserver() - -#### Call Signature - -```ts -removeObserver( - key, - target, - method): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:337 - -Remove an observer you have previously registered on this object. Pass -the same key, target, and method you passed to `addObserver()` and your -target will no longer receive notifications. - -##### Type Parameters - -###### Target - -`Target` - -##### Parameters - -###### key - -keyof `UseHotkeySequence` - -The key to observe - -###### target - -`Target` - -The target object to invoke - -###### method - -`ObserverMethod`\<`Target`, `UseHotkeySequence`\> - -The method to invoke - -##### Returns - -`this` - -##### Method - -removeObserver - -##### Inherited from - -```ts -Helper.removeObserver -``` - -#### Call Signature - -```ts -removeObserver(key, method): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:338 - -##### Parameters - -###### key - -keyof `UseHotkeySequence` - -###### method - -`ObserverMethod`\<`UseHotkeySequence`, `UseHotkeySequence`\> - -##### Returns - -`this` - -##### Inherited from - -```ts -Helper.removeObserver -``` - -*** - -### reopen() - -```ts -reopen(...args): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:79 - -#### Parameters - -##### args - -...(`Record`\<`string`, `unknown`\> \| `Mixin`)[] - -#### Returns - -`this` - -#### Inherited from - -```ts -Helper.reopen -``` - -*** - -### set() - -#### Call Signature - -```ts -set(key, value): T; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:198 - -Sets the provided key or path to the value. - -```javascript -record.set("key", value); -``` - -This method is generally very similar to calling `object["key"] = value` or -`object.key = value`, except that it provides support for computed -properties, the `setUnknownProperty()` method and property observers. - -### Computed Properties - -If you try to set a value on a key that has a computed property handler -defined (see the `get()` method for an example), then `set()` will call -that method, passing both the value and key instead of simply changing -the value itself. This is useful for those times when you need to -implement a property that is composed of one or more member -properties. - -### Unknown Properties - -If you try to set a value on a key that is undefined in the target -object, then the `setUnknownProperty()` handler will be called instead. This -gives you an opportunity to implement complex "virtual" properties that -are not predefined on the object. If `setUnknownProperty()` returns -undefined, then `set()` will simply set the value on the object. - -### Property Observers - -In addition to changing the property, `set()` will also register a property -change with the object. Unless you have placed this call inside of a -`beginPropertyChanges()` and `endPropertyChanges(),` any "local" observers -(i.e. observer methods declared on the same object), will be called -immediately. Any "remote" observers (i.e. observer methods declared on -another object) will be placed in a queue and called at a later time in a -coalesced manner. - -##### Type Parameters - -###### K - -`K` *extends* keyof `UseHotkeySequence` - -###### T - -`T` *extends* - \| `string` - \| `boolean` - \| `unknown`[] - \| `string`[] - \| ((...`args`) => `void`) - \| ((`positional`, `options`) => `void`) - \| `Owner` - \| `DirtyableTag` - \| ((`properties`) => `void`) - \| (() => `void`) - \| ((...`args`) => `this`) - \| (() => `this`) - \| (\{ -\<`K`\> (`key`): `UseHotkeySequence`\[`K`\]; - (`key`): `unknown`; -\}) - \| (\{ -\<`L`\> (`list`): `{ [Key in keyof useHotkeySequence]: useHotkeySequence[Key] }`; -\<`L`\> (...`list`): `{ [Key in keyof useHotkeySequence]: useHotkeySequence[Key] }`; -\<`L`\> (`list`): `{ [Key in string]: unknown }`; -\<`L`\> (...`list`): `{ [Key in string]: unknown }`; -\}) - \| (\{ -\<`K`, `T`\> (`key`, `value`): `T`; -\<`T`\> (`key`, `value`): `T`; -\}) - \| (\{ -\<`K`, `P`\> (`hash`): `P`; -\<`T`\> (`hash`): `T`; -\}) - \| ((`keyName`) => `this`) - \| (\{ -\<`Target`\> (`key`, `target`, `method`): `this`; - (`key`, `method`): `this`; -\}) - \| (\{ -\<`Target`\> (`key`, `target`, `method`): `this`; - (`key`, `method`): `this`; -\}) - \| ((`keyName`, `increment?`) => `number`) - \| ((`keyName`, `decrement?`) => `number`) - \| ((`keyName`) => `boolean`) - \| (\<`K`\>(`key`) => `unknown`) - \| ((...`args`) => `any`) - \| (() => `void`) - \| (() => `string`) - \| `undefined` - -##### Parameters - -###### key - -`K` - -###### value - -`T` - -The value to set or `null`. - -##### Returns - -`T` - -The passed value - -##### Method - -set - -##### Inherited from - -```ts -Helper.set -``` - -#### Call Signature - -```ts -set(key, value): T; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:199 - -##### Type Parameters - -###### T - -`T` - -##### Parameters - -###### key - -`string` - -###### value - -`T` - -##### Returns - -`T` - -##### Inherited from - -```ts -Helper.set -``` - -*** - -### setProperties() - -#### Call Signature - -```ts -setProperties(hash): P; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:214 - -Sets a list of properties at once. These properties are set inside -a single `beginPropertyChanges` and `endPropertyChanges` batch, so -observers will be buffered. - -```javascript -record.setProperties({ firstName: 'Charles', lastName: 'Jolley' }); -``` - -##### Type Parameters - -###### K - -`K` *extends* keyof `UseHotkeySequence` - -###### P - -`P` *extends* `{ [Key in keyof useHotkeySequence]: useHotkeySequence[Key] }` - -##### Parameters - -###### hash - -`P` - -the hash of keys and values to set - -##### Returns - -`P` - -The passed in hash - -##### Method - -setProperties - -##### Inherited from - -```ts -Helper.setProperties -``` - -#### Call Signature - -```ts -setProperties(hash): T; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:217 - -##### Type Parameters - -###### T - -`T` *extends* `Record`\<`string`, `unknown`\> - -##### Parameters - -###### hash - -`T` - -##### Returns - -`T` - -##### Inherited from - -```ts -Helper.setProperties -``` - -*** - -### toggleProperty() - -```ts -toggleProperty(keyName): boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:382 - -Set the value of a boolean property to the opposite of its -current value. - -```javascript -starship.toggleProperty('warpDriveEngaged'); -``` - -#### Parameters - -##### keyName - -keyof `UseHotkeySequence` - -The name of the property to toggle - -#### Returns - -`boolean` - -The new property value - -#### Method - -toggleProperty - -#### Inherited from - -```ts -Helper.toggleProperty -``` - -*** - -### toString() - -```ts -toString(): string; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:345 - -Returns a string representation which attempts to provide more information -than Javascript's `toString` typically does, in a generic way for all Ember -objects. - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend(); -person = Person.create(); -person.toString(); //=> "" -``` - -If the object's class is not defined on an Ember namespace, it will -indicate it is a subclass of the registered superclass: - -```javascript -const Student = Person.extend(); -let student = Student.create(); -student.toString(); //=> "<(subclass of Person):ember1025>" -``` - -If the method `toStringExtension` is defined, its return value will be -included in the output. - -```javascript -const Teacher = Person.extend({ - toStringExtension() { - return this.get('fullName'); - } -}); -teacher = Teacher.create(); -teacher.toString(); //=> "" -``` - -#### Returns - -`string` - -string representation - -#### Method - -toString - -#### Inherited from - -```ts -Helper.toString -``` - -*** - -### willDestroy() - -```ts -willDestroy(): void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:305 - -Override to implement teardown. - -#### Returns - -`void` - -#### Method - -willDestroy - -#### Inherited from - -```ts -Helper.willDestroy -``` - -*** - -### applyPartial() - -```ts -static applyPartial(obj): Record; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:145 - -**`Internal`** - -#### Parameters - -##### obj - -`object` - -#### Returns - -`Record`\<`string`, `any`\> - -#### Inherited from - -```ts -Helper.applyPartial -``` - -*** - -### create() - -#### Call Signature - -```ts -readonly static create(this): InstanceType; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:482 - -Creates an instance of a class. Accepts either no arguments, or an object -containing values to initialize the newly instantiated object with. - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend({ - helloWorld() { - alert(`Hi, my name is ${this.get('name')}`); - } -}); - -let tom = Person.create({ - name: 'Tom Dale' -}); - -tom.helloWorld(); // alerts "Hi, my name is Tom Dale". -``` - -`create` will call the `init` function if defined during -`AnyObject.extend` - -If no arguments are passed to `create`, it will not set values to the new -instance during initialization: - -```javascript -let noName = Person.create(); -noName.helloWorld(); // alerts undefined -``` - -NOTE: For performance reasons, you cannot declare methods or computed -properties during `create`. You should instead declare methods and computed -properties when using `extend`. - -##### Type Parameters - -###### C - -`C` *extends* *typeof* `CoreObject` - -##### Parameters - -###### this - -`C` - -##### Returns - -`InstanceType`\<`C`\> - -##### Method - -create - -##### For - -@ember/object - -##### Static - -##### Inherited from - -```ts -Helper.create -``` - -#### Call Signature - -```ts -readonly static create(this, ...args): InstanceType & MergeArray; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:483 - -Creates an instance of a class. Accepts either no arguments, or an object -containing values to initialize the newly instantiated object with. - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend({ - helloWorld() { - alert(`Hi, my name is ${this.get('name')}`); - } -}); - -let tom = Person.create({ - name: 'Tom Dale' -}); - -tom.helloWorld(); // alerts "Hi, my name is Tom Dale". -``` - -`create` will call the `init` function if defined during -`AnyObject.extend` - -If no arguments are passed to `create`, it will not set values to the new -instance during initialization: - -```javascript -let noName = Person.create(); -noName.helloWorld(); // alerts undefined -``` - -NOTE: For performance reasons, you cannot declare methods or computed -properties during `create`. You should instead declare methods and computed -properties when using `extend`. - -##### Type Parameters - -###### C - -`C` *extends* *typeof* `CoreObject` - -###### I - -`I` *extends* `CoreObject` - -###### K - -`K` *extends* `string` \| `number` \| `symbol` - -###### Args - -`Args` *extends* `Partial`\<\{ \[Key in string \| number \| symbol\]: I\[Key\] \}\>[] - -##### Parameters - -###### this - -`C` - -###### args - -...`Args` - -##### Returns - -`InstanceType`\<`C`\> & `MergeArray`\<`Args`\> - -##### Method - -create - -##### For - -@ember/object - -##### Static - -##### Inherited from - -```ts -Helper.create -``` - -*** - -### detectInstance() - -```ts -readonly static detectInstance(obj): obj is CoreObject; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:585 - -#### Parameters - -##### obj - -`unknown` - -#### Returns - -`obj is CoreObject` - -#### Inherited from - -```ts -Helper.detectInstance -``` - -*** - -### extend() - -```ts -readonly static extend(this, ...mixins?): Readonly & EmberClassConstructor & MergeArray; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:440 - -Creates a new subclass. - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend({ - say(thing) { - alert(thing); - } -}); -``` - -This defines a new subclass of EmberObject: `Person`. It contains one method: `say()`. - -You can also create a subclass from any existing class by calling its `extend()` method. -For example, you might want to create a subclass of Ember's built-in `Component` class: - -```javascript -import Component from '@ember/component'; - -const PersonComponent = Component.extend({ - tagName: 'li', - classNameBindings: ['isAdministrator'] -}); -``` - -When defining a subclass, you can override methods but still access the -implementation of your parent class by calling the special `_super()` method: - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend({ - say(thing) { - let name = this.get('name'); - alert(`${name} says: ${thing}`); - } -}); - -const Soldier = Person.extend({ - say(thing) { - this._super(`${thing}, sir!`); - }, - march(numberOfHours) { - alert(`${this.get('name')} marches for ${numberOfHours} hours.`); - } -}); - -let yehuda = Soldier.create({ - name: 'Yehuda Katz' -}); - -yehuda.say('Yes'); // alerts "Yehuda Katz says: Yes, sir!" -``` - -The `create()` on line #17 creates an *instance* of the `Soldier` class. -The `extend()` on line #8 creates a *subclass* of `Person`. Any instance -of the `Person` class will *not* have the `march()` method. - -You can also pass `Mixin` classes to add additional properties to the subclass. - -```javascript -import EmberObject from '@ember/object'; -import Mixin from '@ember/object/mixin'; - -const Person = EmberObject.extend({ - say(thing) { - alert(`${this.get('name')} says: ${thing}`); - } -}); - -const SingingMixin = Mixin.create({ - sing(thing) { - alert(`${this.get('name')} sings: la la la ${thing}`); - } -}); - -const BroadwayStar = Person.extend(SingingMixin, { - dance() { - alert(`${this.get('name')} dances: tap tap tap tap `); - } -}); -``` - -The `BroadwayStar` class contains three methods: `say()`, `sing()`, and `dance()`. - -#### Type Parameters - -##### Statics - -`Statics` - -##### Instance - -`Instance` - -##### M - -`M` *extends* `unknown`[] - -#### Parameters - -##### this - -`Statics` & `EmberClassConstructor`\<`Instance`\> - -##### mixins? - -...`M` - -One or more Mixin classes - -#### Returns - -`Readonly`\<`Statics`\> & `EmberClassConstructor`\<`Instance`\> & `MergeArray`\<`M`\> - -#### Method - -extend - -#### Static - -#### For - -@ember/object - -#### Inherited from - -```ts -Helper.extend -``` - -*** - -### keys() - -```ts -static keys(): Set; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:157 - -**`Internal`** - -#### Returns - -`Set`\<`string`\> - -#### Inherited from - -```ts -Helper.keys -``` - -*** - -### proto() - -```ts -readonly static proto(): CoreObject; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:631 - -#### Returns - -`CoreObject` - -#### Inherited from - -```ts -Helper.proto -``` - -*** - -### reopenClass() - -```ts -readonly static reopenClass(this, ...mixins): C; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:583 - -Augments a constructor's own properties and functions: - -```javascript -import EmberObject from '@ember/object'; - -const MyObject = EmberObject.extend({ - name: 'an object' -}); - -MyObject.reopenClass({ - canBuild: false -}); - -MyObject.canBuild; // false -o = MyObject.create(); -``` - -In other words, this creates static properties and functions for the class. -These are only available on the class and not on any instance of that class. - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend({ - name: '', - sayHello() { - alert(`Hello. My name is ${this.get('name')}`); - } -}); - -Person.reopenClass({ - species: 'Homo sapiens', - - createPerson(name) { - return Person.create({ name }); - } -}); - -let tom = Person.create({ - name: 'Tom Dale' -}); -let yehuda = Person.createPerson('Yehuda Katz'); - -tom.sayHello(); // "Hello. My name is Tom Dale" -yehuda.sayHello(); // "Hello. My name is Yehuda Katz" -alert(Person.species); // "Homo sapiens" -``` - -Note that `species` and `createPerson` are *not* valid on the `tom` and `yehuda` -variables. They are only valid on `Person`. - -To add functions and properties to instances of -a constructor by extending the constructor's prototype -see `reopen` - -#### Type Parameters - -##### C - -`C` *extends* *typeof* `CoreObject` - -#### Parameters - -##### this - -`C` - -##### mixins - -...(`Record`\<`string`, `unknown`\> \| `Mixin`)[] - -#### Returns - -`C` - -#### Method - -reopenClass - -#### For - -@ember/object - -#### Static - -#### Inherited from - -```ts -Helper.reopenClass -``` - -*** - -### toString() - -```ts -static toString(): string; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:632 - -#### Returns - -`string` - -#### Inherited from - -```ts -Helper.toString -``` - -*** - -### willReopen() - -```ts -readonly static willReopen(): void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:521 - -#### Returns - -`void` - -#### Inherited from - -```ts -Helper.willReopen -``` - -*** - -### without() - -```ts -static without(...args): Mixin; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:155 - -**`Internal`** - -#### Parameters - -##### args - -...`any`[] - -#### Returns - -`Mixin` - -#### Inherited from - -```ts -Helper.without -``` diff --git a/docs/framework/ember/reference/classes/useHotkeySequences.md b/docs/framework/ember/reference/classes/useHotkeySequences.md deleted file mode 100644 index 67975edb..00000000 --- a/docs/framework/ember/reference/classes/useHotkeySequences.md +++ /dev/null @@ -1,2518 +0,0 @@ ---- -id: useHotkeySequences -title: useHotkeySequences ---- - -Defined in: [packages/ember-hotkeys/src/useHotkeySequences.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeySequences.ts#L13) - -Registers a reactive list of sequences with the helper's template lifecycle. - -## Extends - -- `default`\<\{ - `Args`: \{ - `Named`: `SequenceOptions`; - `Positional`: \[`HotkeySequenceDefinition`[]\]; - \}; - `Return`: `void`; -\}\> - -## Constructors - -### Constructor - -```ts -new useHotkeySequences(owner?): UseHotkeySequences; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/index.d.ts:28 - -#### Parameters - -##### owner? - -`Owner` - -#### Returns - -`UseHotkeySequences` - -#### Inherited from - -```ts -Helper<{ - Args: { - Positional: [Array] - Named: SequenceOptions - } - Return: void -}>.constructor -``` - -## Properties - -### \[Invoke\] - -```ts -[Invoke]: (...args) => void; -``` - -Defined in: node\_modules/.pnpm/@glint+template@1.9.0/node\_modules/@glint/template/-private/integration.d.ts:22 - -#### Parameters - -##### args - -...\[`HotkeySequenceDefinition`[], `NamedArgs`\<`SequenceOptions`\>\] - -#### Returns - -`void` - -#### Inherited from - -```ts -Helper.[Invoke] -``` - -*** - -### \[OWNER\]? - -```ts -optional [OWNER]?: Owner; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:77 - -**`Internal`** - -#### Inherited from - -```ts -Helper.[OWNER] -``` - -*** - -### \[RECOMPUTE\_TAG\] - -```ts -[RECOMPUTE_TAG]: DirtyableTag; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/-internals/glimmer/lib/helper.d.ts:103 - -#### Inherited from - -```ts -Helper.[RECOMPUTE_TAG] -``` - -*** - -### concatenatedProperties? - -```ts -optional concatenatedProperties?: string | string[]; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:637 - -#### Inherited from - -```ts -Helper.concatenatedProperties -``` - -*** - -### mergedProperties? - -```ts -optional mergedProperties?: unknown[]; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:638 - -#### Inherited from - -```ts -Helper.mergedProperties -``` - -*** - -### \_lazyInjections? - -```ts -readonly static optional _lazyInjections?: () => void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:636 - -#### Returns - -`void` - -#### Inherited from - -```ts -Helper._lazyInjections -``` - -*** - -### \_onLookup? - -```ts -readonly static optional _onLookup?: (debugContainerKey) => void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:635 - -#### Parameters - -##### debugContainerKey - -`string` - -#### Returns - -`void` - -#### Inherited from - -```ts -Helper._onLookup -``` - -*** - -### \_without - -```ts -static _without: any[] | undefined; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:113 - -**`Internal`** - -#### Inherited from - -```ts -Helper._without -``` - -*** - -### \[INIT\_FACTORY\]? - -```ts -static optional [INIT_FACTORY]?: null; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:114 - -#### Inherited from - -```ts -Helper.[INIT_FACTORY] -``` - -*** - -### \[IS\_CLASSIC\_HELPER\] - -```ts -static [IS_CLASSIC_HELPER]: boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/-internals/glimmer/lib/helper.d.ts:100 - -#### Inherited from - -```ts -Helper.[IS_CLASSIC_HELPER] -``` - -*** - -### ~~helper~~ - -```ts -static helper: { - (helperFn): FunctionBasedHelper<{ - Args: { - Named: N; - Positional: P; - }; - Return: R; -}>; - (helperFn): FunctionBasedHelper; -}; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/-internals/glimmer/lib/helper.d.ts:102 - -#### Call Signature - -```ts -(helperFn): FunctionBasedHelper<{ - Args: { - Named: N; - Positional: P; - }; - Return: R; -}>; -``` - -In many cases it is not necessary to use the full `Helper` class. -The `helper` method create pure-function helpers without instances. -For example: - -```app/helpers/format-currency.js -import { helper } from '@ember/component/helper'; - -export default helper(function([cents], {currency}) { - return `${currency}${cents * 0.01}`; -}); -``` - -##### Type Parameters - -###### P - -`P` *extends* `DefaultPositional` - -###### N - -`N` *extends* `object` - -###### R - -`R` = `unknown` - -##### Parameters - -###### helperFn - -(`positional`, `named`) => `R` - -##### Returns - -`FunctionBasedHelper`\<\{ - `Args`: \{ - `Named`: `N`; - `Positional`: `P`; - \}; - `Return`: `R`; -\}\> - -##### Static - -##### Method - -helper - -##### For - -@ember/component/helper - -##### Since - -1.13.0 - -#### Call Signature - -```ts -(helperFn): FunctionBasedHelper; -``` - -In many cases it is not necessary to use the full `Helper` class. -The `helper` method create pure-function helpers without instances. -For example: - -```app/helpers/format-currency.js -import { helper } from '@ember/component/helper'; - -export default helper(function([cents], {currency}) { - return `${currency}${cents * 0.01}`; -}); -``` - -##### Type Parameters - -###### S - -`S` - -##### Parameters - -###### helperFn - -(`positional`, `named`) => `GetOr`\<`S`\> - -##### Returns - -`FunctionBasedHelper`\<`S`\> - -##### Static - -##### Method - -helper - -##### For - -@ember/component/helper - -##### Since - -1.13.0 - -#### Deprecated - -#### Inherited from - -```ts -Helper.helper -``` - -*** - -### isClass - -```ts -readonly static isClass: boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:633 - -#### Inherited from - -```ts -Helper.isClass -``` - -*** - -### isHelperFactory - -```ts -static isHelperFactory: boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/-internals/glimmer/lib/helper.d.ts:99 - -#### Inherited from - -```ts -Helper.isHelperFactory -``` - -*** - -### isMethod - -```ts -readonly static isMethod: boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:634 - -#### Inherited from - -```ts -Helper.isMethod -``` - -*** - -### mixins - -```ts -static mixins: Mixin[] | undefined; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:105 - -**`Internal`** - -#### Inherited from - -```ts -Helper.mixins -``` - -*** - -### ownerConstructor - -```ts -static ownerConstructor: any; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:111 - -**`Internal`** - -#### Inherited from - -```ts -Helper.ownerConstructor -``` - -*** - -### properties - -```ts -static properties: - | { -[key: string]: any; -} - | undefined; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:107 - -**`Internal`** - -#### Inherited from - -```ts -Helper.properties -``` - -*** - -### PrototypeMixin - -```ts -static PrototypeMixin: any; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:629 - -#### Inherited from - -```ts -Helper.PrototypeMixin -``` - -*** - -### superclass - -```ts -static superclass: any; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:630 - -#### Inherited from - -```ts -Helper.superclass -``` - -## Accessors - -### \_debugContainerKey - -#### Get Signature - -```ts -get _debugContainerKey(): false | `${string}:${string}`; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/index.d.ts:30 - -##### Returns - -`false` \| `` `${string}:${string}` `` - -#### Inherited from - -```ts -Helper._debugContainerKey -``` - -*** - -### isDestroyed - -#### Get Signature - -```ts -get isDestroyed(): boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:270 - -Destroyed object property flag. - -if this property is `true` the observers and bindings were already -removed by the effect of calling the `destroy()` method. - -##### Default - -```ts -false -@public -``` - -##### Returns - -`boolean` - -#### Set Signature - -```ts -set isDestroyed(_value): void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:271 - -##### Parameters - -###### \_value - -`boolean` - -##### Returns - -`void` - -#### Inherited from - -```ts -Helper.isDestroyed -``` - -*** - -### isDestroying - -#### Get Signature - -```ts -get isDestroying(): boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:282 - -Destruction scheduled flag. The `destroy()` method has been called. - -The object stays intact until the end of the run loop at which point -the `isDestroyed` flag is set. - -##### Default - -```ts -false -@public -``` - -##### Returns - -`boolean` - -#### Set Signature - -```ts -set isDestroying(_value): void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:283 - -##### Parameters - -###### \_value - -`boolean` - -##### Returns - -`void` - -#### Inherited from - -```ts -Helper.isDestroying -``` - -## Methods - -### \_super() - -```ts -_super(...args): any; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:73 - -**`Internal`** - -#### Parameters - -##### args - -...`any`[] - -#### Returns - -`any` - -#### Inherited from - -```ts -Helper._super -``` - -*** - -### addObserver() - -#### Call Signature - -```ts -addObserver( - key, - target, - method): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:322 - -Adds an observer on a property. - -This is the core method used to register an observer for a property. - -Once you call this method, any time the key's value is set, your observer -will be notified. Note that the observers are triggered any time the -value is set, regardless of whether it has actually changed. Your -observer should be prepared to handle that. - -There are two common invocation patterns for `.addObserver()`: - -- Passing two arguments: - - the name of the property to observe (as a string) - - the function to invoke (an actual function) -- Passing three arguments: - - the name of the property to observe (as a string) - - the target object (will be used to look up and invoke a - function on) - - the name of the function to invoke on the target object - (as a string). - -```app/components/my-component.js -import Component from '@ember/component'; - -export default Component.extend({ - init() { - this._super(...arguments); - - // the following are equivalent: - - // using three arguments - this.addObserver('foo', this, 'fooDidChange'); - - // using two arguments - this.addObserver('foo', (...args) => { - this.fooDidChange(...args); - }); - }, - - fooDidChange() { - // your custom logic code - } -}); -``` - -### Observer Methods - -Observer methods have the following signature: - -```app/components/my-component.js -import Component from '@ember/component'; - -export default Component.extend({ - init() { - this._super(...arguments); - this.addObserver('foo', this, 'fooDidChange'); - }, - - fooDidChange(sender, key, value, rev) { - // your code - } -}); -``` - -The `sender` is the object that changed. The `key` is the property that -changes. The `value` property is currently reserved and unused. The `rev` -is the last property revision of the object when it changed, which you can -use to detect if the key value has really changed or not. - -Usually you will not need the value or revision parameters at -the end. In this case, it is common to write observer methods that take -only a sender and key value as parameters or, if you aren't interested in -any of these values, to write an observer that has no parameters at all. - -While observers are still supported, there are [plans to deprecate them](https://github.com/emberjs/rfcs/pull/1115) -See the [in-progress deprecation guide](https://github.com/ember-learn/deprecation-app/pull/1407) -for guidance on how to avoid using observers. - -##### Type Parameters - -###### Target - -`Target` - -##### Parameters - -###### key - -keyof `UseHotkeySequences` - -The key to observe - -###### target - -`Target` - -The target object to invoke - -###### method - -`ObserverMethod`\<`Target`, `UseHotkeySequences`\> - -The method to invoke - -##### Returns - -`this` - -##### Method - -addObserver - -##### Inherited from - -```ts -Helper.addObserver -``` - -#### Call Signature - -```ts -addObserver(key, method): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:323 - -##### Parameters - -###### key - -keyof `UseHotkeySequences` - -###### method - -`ObserverMethod`\<`UseHotkeySequences`, `UseHotkeySequences`\> - -##### Returns - -`this` - -##### Inherited from - -```ts -Helper.addObserver -``` - -*** - -### cacheFor() - -```ts -cacheFor(key): unknown; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:394 - -Returns the cached value of a computed property, if it exists. -This allows you to inspect the value of a computed property -without accidentally invoking it if it is intended to be -generated lazily. - -#### Type Parameters - -##### K - -`K` *extends* keyof `UseHotkeySequences` - -#### Parameters - -##### key - -`K` - -#### Returns - -`unknown` - -The cached value of the computed property, if any - -#### Method - -cacheFor - -#### Inherited from - -```ts -Helper.cacheFor -``` - -*** - -### compute() - -```ts -compute(positional, options): void; -``` - -Defined in: [packages/ember-hotkeys/src/useHotkeySequences.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeySequences.ts#L25) - -Override this function when writing a class-based helper. - -#### Parameters - -##### positional - -\[`HotkeySequenceDefinition`[]\] - -The positional arguments to the helper - -##### options - -`SequenceOptions` - -#### Returns - -`void` - -#### Method - -compute - -#### Since - -1.13.0 - -#### Overrides - -```ts -Helper.compute -``` - -*** - -### decrementProperty() - -```ts -decrementProperty(keyName, decrement?): number; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:368 - -Set the value of a property to the current value minus some amount. - -```javascript -player.decrementProperty('lives'); -orc.decrementProperty('health', 5); -``` - -#### Parameters - -##### keyName - -keyof `UseHotkeySequences` - -The name of the property to decrement - -##### decrement? - -`number` - -The amount to decrement by. Defaults to 1 - -#### Returns - -`number` - -The new property value - -#### Method - -decrementProperty - -#### Inherited from - -```ts -Helper.decrementProperty -``` - -*** - -### destroy() - -```ts -destroy(): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:298 - -Destroys an object by setting the `isDestroyed` flag and removing its -metadata, which effectively destroys observers and bindings. - -If you try to set a property on a destroyed object, an exception will be -raised. - -Note that destruction is scheduled for the end of the run loop and does not -happen immediately. It will set an isDestroying flag immediately. - -#### Returns - -`this` - -receiver - -#### Method - -destroy - -#### Inherited from - -```ts -Helper.destroy -``` - -*** - -### get() - -#### Call Signature - -```ts -get(key): UseHotkeySequences[K]; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:119 - -Retrieves the value of a property from the object. - -This method is usually similar to using `object[keyName]` or `object.keyName`, -however it supports both computed properties and the unknownProperty -handler. - -Because `get` unifies the syntax for accessing all these kinds -of properties, it can make many refactorings easier, such as replacing a -simple property with a computed property, or vice versa. - -### Computed Properties - -Computed properties are methods defined with the `property` modifier -declared at the end, such as: - -```javascript -import { computed } from '@ember/object'; - -fullName: computed('firstName', 'lastName', function() { - return this.get('firstName') + ' ' + this.get('lastName'); -}) -``` - -When you call `get` on a computed property, the function will be -called and the return value will be returned instead of the function -itself. - -### Unknown Properties - -Likewise, if you try to call `get` on a property whose value is -`undefined`, the `unknownProperty()` method will be called on the object. -If this method returns any value other than `undefined`, it will be returned -instead. This allows you to implement "virtual" properties that are -not defined upfront. - -##### Type Parameters - -###### K - -`K` *extends* keyof `UseHotkeySequences` - -##### Parameters - -###### key - -`K` - -##### Returns - -`UseHotkeySequences`\[`K`\] - -The property value or undefined. - -##### Method - -get - -##### Inherited from - -```ts -Helper.get -``` - -#### Call Signature - -```ts -get(key): unknown; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:120 - -##### Parameters - -###### key - -`string` - -##### Returns - -`unknown` - -##### Inherited from - -```ts -Helper.get -``` - -*** - -### getProperties() - -#### Call Signature - -```ts -getProperties(list): { [Key in keyof useHotkeySequences]: useHotkeySequences[Key] }; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:142 - -To get the values of multiple properties at once, call `getProperties` -with a list of strings or an array: - -```javascript -record.getProperties('firstName', 'lastName', 'zipCode'); -// { firstName: 'John', lastName: 'Doe', zipCode: '10011' } -``` - -is equivalent to: - -```javascript -record.getProperties(['firstName', 'lastName', 'zipCode']); -// { firstName: 'John', lastName: 'Doe', zipCode: '10011' } -``` - -##### Type Parameters - -###### L - -`L` *extends* keyof `UseHotkeySequences`[] - -##### Parameters - -###### list - -`L` - -of keys to get - -##### Returns - -`{ [Key in keyof useHotkeySequences]: useHotkeySequences[Key] }` - -##### Method - -getProperties - -##### Inherited from - -```ts -Helper.getProperties -``` - -#### Call Signature - -```ts -getProperties(...list): { [Key in keyof useHotkeySequences]: useHotkeySequences[Key] }; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:145 - -##### Type Parameters - -###### L - -`L` *extends* keyof `UseHotkeySequences`[] - -##### Parameters - -###### list - -...`L` - -##### Returns - -`{ [Key in keyof useHotkeySequences]: useHotkeySequences[Key] }` - -##### Inherited from - -```ts -Helper.getProperties -``` - -#### Call Signature - -```ts -getProperties(list): { [Key in string]: unknown }; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:148 - -##### Type Parameters - -###### L - -`L` *extends* `string`[] - -##### Parameters - -###### list - -`L` - -##### Returns - -`{ [Key in string]: unknown }` - -##### Inherited from - -```ts -Helper.getProperties -``` - -#### Call Signature - -```ts -getProperties(...list): { [Key in string]: unknown }; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:151 - -##### Type Parameters - -###### L - -`L` *extends* `string`[] - -##### Parameters - -###### list - -...`L` - -##### Returns - -`{ [Key in string]: unknown }` - -##### Inherited from - -```ts -Helper.getProperties -``` - -*** - -### incrementProperty() - -```ts -incrementProperty(keyName, increment?): number; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:353 - -Set the value of a property to the current value plus some amount. - -```javascript -person.incrementProperty('age'); -team.incrementProperty('score', 2); -``` - -#### Parameters - -##### keyName - -keyof `UseHotkeySequences` - -The name of the property to increment - -##### increment? - -`number` - -The amount to increment by. Defaults to 1 - -#### Returns - -`number` - -The new property value - -#### Method - -incrementProperty - -#### Inherited from - -```ts -Helper.incrementProperty -``` - -*** - -### init() - -```ts -init(properties): void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/-internals/glimmer/lib/helper.d.ts:105 - -An overridable method called when objects are instantiated. By default, -does nothing unless it is overridden during class definition. - -Example: - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend({ - init() { - alert(`Name is ${this.get('name')}`); - } -}); - -let steve = Person.create({ - name: 'Steve' -}); - -// alerts 'Name is Steve'. -``` - -NOTE: If you do override `init` for a framework class like `Component` -from `@ember/component`, be sure to call `this._super(...arguments)` -in your `init` declaration! -If you don't, Ember may not have an opportunity to -do important setup work, and you'll see strange behavior in your -application. - -#### Parameters - -##### properties - -`object` \| `undefined` - -#### Returns - -`void` - -#### Method - -init - -#### Inherited from - -```ts -Helper.init -``` - -*** - -### notifyPropertyChange() - -```ts -notifyPropertyChange(keyName): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:234 - -Convenience method to call `propertyWillChange` and `propertyDidChange` in -succession. - -Notify the observer system that a property has just changed. - -Sometimes you need to change a value directly or indirectly without -actually calling `get()` or `set()` on it. In this case, you can use this -method instead. Calling this method will notify all observers that the -property has potentially changed value. - -#### Parameters - -##### keyName - -`string` - -The property key to be notified about. - -#### Returns - -`this` - -#### Method - -notifyPropertyChange - -#### Inherited from - -```ts -Helper.notifyPropertyChange -``` - -*** - -### recompute() - -```ts -recompute(): void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/-internals/glimmer/lib/helper.d.ts:142 - -On a class-based helper, it may be useful to force a recomputation of that -helpers value. This is akin to `rerender` on a component. - -In most cases, `recompute` is not needed because accessing tracked -properties in `compute` will automatically re-run the helper when -those properties change. Use `recompute` only when you need to -trigger a recomputation imperatively, for example in response to an -external event: - -```app/helpers/current-time.js -import Helper from '@ember/component/helper'; - -export default class CurrentTimeHelper extends Helper { - interval = null; - - compute() { - return new Date().toLocaleTimeString(); - } - - constructor() { - super(...arguments); - this.interval = setInterval(() => this.recompute(), 1000); - } - - willDestroy() { - super.willDestroy(); - clearInterval(this.interval); - } -} -``` - -#### Returns - -`void` - -#### Method - -recompute - -#### Since - -1.13.0 - -#### Inherited from - -```ts -Helper.recompute -``` - -*** - -### removeObserver() - -#### Call Signature - -```ts -removeObserver( - key, - target, - method): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:337 - -Remove an observer you have previously registered on this object. Pass -the same key, target, and method you passed to `addObserver()` and your -target will no longer receive notifications. - -##### Type Parameters - -###### Target - -`Target` - -##### Parameters - -###### key - -keyof `UseHotkeySequences` - -The key to observe - -###### target - -`Target` - -The target object to invoke - -###### method - -`ObserverMethod`\<`Target`, `UseHotkeySequences`\> - -The method to invoke - -##### Returns - -`this` - -##### Method - -removeObserver - -##### Inherited from - -```ts -Helper.removeObserver -``` - -#### Call Signature - -```ts -removeObserver(key, method): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:338 - -##### Parameters - -###### key - -keyof `UseHotkeySequences` - -###### method - -`ObserverMethod`\<`UseHotkeySequences`, `UseHotkeySequences`\> - -##### Returns - -`this` - -##### Inherited from - -```ts -Helper.removeObserver -``` - -*** - -### reopen() - -```ts -reopen(...args): this; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:79 - -#### Parameters - -##### args - -...(`Record`\<`string`, `unknown`\> \| `Mixin`)[] - -#### Returns - -`this` - -#### Inherited from - -```ts -Helper.reopen -``` - -*** - -### set() - -#### Call Signature - -```ts -set(key, value): T; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:198 - -Sets the provided key or path to the value. - -```javascript -record.set("key", value); -``` - -This method is generally very similar to calling `object["key"] = value` or -`object.key = value`, except that it provides support for computed -properties, the `setUnknownProperty()` method and property observers. - -### Computed Properties - -If you try to set a value on a key that has a computed property handler -defined (see the `get()` method for an example), then `set()` will call -that method, passing both the value and key instead of simply changing -the value itself. This is useful for those times when you need to -implement a property that is composed of one or more member -properties. - -### Unknown Properties - -If you try to set a value on a key that is undefined in the target -object, then the `setUnknownProperty()` handler will be called instead. This -gives you an opportunity to implement complex "virtual" properties that -are not predefined on the object. If `setUnknownProperty()` returns -undefined, then `set()` will simply set the value on the object. - -### Property Observers - -In addition to changing the property, `set()` will also register a property -change with the object. Unless you have placed this call inside of a -`beginPropertyChanges()` and `endPropertyChanges(),` any "local" observers -(i.e. observer methods declared on the same object), will be called -immediately. Any "remote" observers (i.e. observer methods declared on -another object) will be placed in a queue and called at a later time in a -coalesced manner. - -##### Type Parameters - -###### K - -`K` *extends* keyof `UseHotkeySequences` - -###### T - -`T` *extends* - \| `string` - \| `boolean` - \| `unknown`[] - \| `string`[] - \| ((...`args`) => `void`) - \| ((`positional`, `options`) => `void`) - \| `Owner` - \| `DirtyableTag` - \| ((`properties`) => `void`) - \| (() => `void`) - \| ((...`args`) => `this`) - \| (() => `this`) - \| (\{ -\<`K`\> (`key`): `UseHotkeySequences`\[`K`\]; - (`key`): `unknown`; -\}) - \| (\{ -\<`L`\> (`list`): `{ [Key in keyof useHotkeySequences]: useHotkeySequences[Key] }`; -\<`L`\> (...`list`): `{ [Key in keyof useHotkeySequences]: useHotkeySequences[Key] }`; -\<`L`\> (`list`): `{ [Key in string]: unknown }`; -\<`L`\> (...`list`): `{ [Key in string]: unknown }`; -\}) - \| (\{ -\<`K`, `T`\> (`key`, `value`): `T`; -\<`T`\> (`key`, `value`): `T`; -\}) - \| (\{ -\<`K`, `P`\> (`hash`): `P`; -\<`T`\> (`hash`): `T`; -\}) - \| ((`keyName`) => `this`) - \| (\{ -\<`Target`\> (`key`, `target`, `method`): `this`; - (`key`, `method`): `this`; -\}) - \| (\{ -\<`Target`\> (`key`, `target`, `method`): `this`; - (`key`, `method`): `this`; -\}) - \| ((`keyName`, `increment?`) => `number`) - \| ((`keyName`, `decrement?`) => `number`) - \| ((`keyName`) => `boolean`) - \| (\<`K`\>(`key`) => `unknown`) - \| ((...`args`) => `any`) - \| (() => `void`) - \| (() => `string`) - \| `undefined` - -##### Parameters - -###### key - -`K` - -###### value - -`T` - -The value to set or `null`. - -##### Returns - -`T` - -The passed value - -##### Method - -set - -##### Inherited from - -```ts -Helper.set -``` - -#### Call Signature - -```ts -set(key, value): T; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:199 - -##### Type Parameters - -###### T - -`T` - -##### Parameters - -###### key - -`string` - -###### value - -`T` - -##### Returns - -`T` - -##### Inherited from - -```ts -Helper.set -``` - -*** - -### setProperties() - -#### Call Signature - -```ts -setProperties(hash): P; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:214 - -Sets a list of properties at once. These properties are set inside -a single `beginPropertyChanges` and `endPropertyChanges` batch, so -observers will be buffered. - -```javascript -record.setProperties({ firstName: 'Charles', lastName: 'Jolley' }); -``` - -##### Type Parameters - -###### K - -`K` *extends* keyof `UseHotkeySequences` - -###### P - -`P` *extends* `{ [Key in keyof useHotkeySequences]: useHotkeySequences[Key] }` - -##### Parameters - -###### hash - -`P` - -the hash of keys and values to set - -##### Returns - -`P` - -The passed in hash - -##### Method - -setProperties - -##### Inherited from - -```ts -Helper.setProperties -``` - -#### Call Signature - -```ts -setProperties(hash): T; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:217 - -##### Type Parameters - -###### T - -`T` *extends* `Record`\<`string`, `unknown`\> - -##### Parameters - -###### hash - -`T` - -##### Returns - -`T` - -##### Inherited from - -```ts -Helper.setProperties -``` - -*** - -### toggleProperty() - -```ts -toggleProperty(keyName): boolean; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/observable.d.ts:382 - -Set the value of a boolean property to the opposite of its -current value. - -```javascript -starship.toggleProperty('warpDriveEngaged'); -``` - -#### Parameters - -##### keyName - -keyof `UseHotkeySequences` - -The name of the property to toggle - -#### Returns - -`boolean` - -The new property value - -#### Method - -toggleProperty - -#### Inherited from - -```ts -Helper.toggleProperty -``` - -*** - -### toString() - -```ts -toString(): string; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:345 - -Returns a string representation which attempts to provide more information -than Javascript's `toString` typically does, in a generic way for all Ember -objects. - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend(); -person = Person.create(); -person.toString(); //=> "" -``` - -If the object's class is not defined on an Ember namespace, it will -indicate it is a subclass of the registered superclass: - -```javascript -const Student = Person.extend(); -let student = Student.create(); -student.toString(); //=> "<(subclass of Person):ember1025>" -``` - -If the method `toStringExtension` is defined, its return value will be -included in the output. - -```javascript -const Teacher = Person.extend({ - toStringExtension() { - return this.get('fullName'); - } -}); -teacher = Teacher.create(); -teacher.toString(); //=> "" -``` - -#### Returns - -`string` - -string representation - -#### Method - -toString - -#### Inherited from - -```ts -Helper.toString -``` - -*** - -### willDestroy() - -```ts -willDestroy(): void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:305 - -Override to implement teardown. - -#### Returns - -`void` - -#### Method - -willDestroy - -#### Inherited from - -```ts -Helper.willDestroy -``` - -*** - -### applyPartial() - -```ts -static applyPartial(obj): Record; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:145 - -**`Internal`** - -#### Parameters - -##### obj - -`object` - -#### Returns - -`Record`\<`string`, `any`\> - -#### Inherited from - -```ts -Helper.applyPartial -``` - -*** - -### create() - -#### Call Signature - -```ts -readonly static create(this): InstanceType; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:482 - -Creates an instance of a class. Accepts either no arguments, or an object -containing values to initialize the newly instantiated object with. - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend({ - helloWorld() { - alert(`Hi, my name is ${this.get('name')}`); - } -}); - -let tom = Person.create({ - name: 'Tom Dale' -}); - -tom.helloWorld(); // alerts "Hi, my name is Tom Dale". -``` - -`create` will call the `init` function if defined during -`AnyObject.extend` - -If no arguments are passed to `create`, it will not set values to the new -instance during initialization: - -```javascript -let noName = Person.create(); -noName.helloWorld(); // alerts undefined -``` - -NOTE: For performance reasons, you cannot declare methods or computed -properties during `create`. You should instead declare methods and computed -properties when using `extend`. - -##### Type Parameters - -###### C - -`C` *extends* *typeof* `CoreObject` - -##### Parameters - -###### this - -`C` - -##### Returns - -`InstanceType`\<`C`\> - -##### Method - -create - -##### For - -@ember/object - -##### Static - -##### Inherited from - -```ts -Helper.create -``` - -#### Call Signature - -```ts -readonly static create(this, ...args): InstanceType & MergeArray; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:483 - -Creates an instance of a class. Accepts either no arguments, or an object -containing values to initialize the newly instantiated object with. - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend({ - helloWorld() { - alert(`Hi, my name is ${this.get('name')}`); - } -}); - -let tom = Person.create({ - name: 'Tom Dale' -}); - -tom.helloWorld(); // alerts "Hi, my name is Tom Dale". -``` - -`create` will call the `init` function if defined during -`AnyObject.extend` - -If no arguments are passed to `create`, it will not set values to the new -instance during initialization: - -```javascript -let noName = Person.create(); -noName.helloWorld(); // alerts undefined -``` - -NOTE: For performance reasons, you cannot declare methods or computed -properties during `create`. You should instead declare methods and computed -properties when using `extend`. - -##### Type Parameters - -###### C - -`C` *extends* *typeof* `CoreObject` - -###### I - -`I` *extends* `CoreObject` - -###### K - -`K` *extends* `string` \| `number` \| `symbol` - -###### Args - -`Args` *extends* `Partial`\<\{ \[Key in string \| number \| symbol\]: I\[Key\] \}\>[] - -##### Parameters - -###### this - -`C` - -###### args - -...`Args` - -##### Returns - -`InstanceType`\<`C`\> & `MergeArray`\<`Args`\> - -##### Method - -create - -##### For - -@ember/object - -##### Static - -##### Inherited from - -```ts -Helper.create -``` - -*** - -### detectInstance() - -```ts -readonly static detectInstance(obj): obj is CoreObject; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:585 - -#### Parameters - -##### obj - -`unknown` - -#### Returns - -`obj is CoreObject` - -#### Inherited from - -```ts -Helper.detectInstance -``` - -*** - -### extend() - -```ts -readonly static extend(this, ...mixins?): Readonly & EmberClassConstructor & MergeArray; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:440 - -Creates a new subclass. - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend({ - say(thing) { - alert(thing); - } -}); -``` - -This defines a new subclass of EmberObject: `Person`. It contains one method: `say()`. - -You can also create a subclass from any existing class by calling its `extend()` method. -For example, you might want to create a subclass of Ember's built-in `Component` class: - -```javascript -import Component from '@ember/component'; - -const PersonComponent = Component.extend({ - tagName: 'li', - classNameBindings: ['isAdministrator'] -}); -``` - -When defining a subclass, you can override methods but still access the -implementation of your parent class by calling the special `_super()` method: - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend({ - say(thing) { - let name = this.get('name'); - alert(`${name} says: ${thing}`); - } -}); - -const Soldier = Person.extend({ - say(thing) { - this._super(`${thing}, sir!`); - }, - march(numberOfHours) { - alert(`${this.get('name')} marches for ${numberOfHours} hours.`); - } -}); - -let yehuda = Soldier.create({ - name: 'Yehuda Katz' -}); - -yehuda.say('Yes'); // alerts "Yehuda Katz says: Yes, sir!" -``` - -The `create()` on line #17 creates an *instance* of the `Soldier` class. -The `extend()` on line #8 creates a *subclass* of `Person`. Any instance -of the `Person` class will *not* have the `march()` method. - -You can also pass `Mixin` classes to add additional properties to the subclass. - -```javascript -import EmberObject from '@ember/object'; -import Mixin from '@ember/object/mixin'; - -const Person = EmberObject.extend({ - say(thing) { - alert(`${this.get('name')} says: ${thing}`); - } -}); - -const SingingMixin = Mixin.create({ - sing(thing) { - alert(`${this.get('name')} sings: la la la ${thing}`); - } -}); - -const BroadwayStar = Person.extend(SingingMixin, { - dance() { - alert(`${this.get('name')} dances: tap tap tap tap `); - } -}); -``` - -The `BroadwayStar` class contains three methods: `say()`, `sing()`, and `dance()`. - -#### Type Parameters - -##### Statics - -`Statics` - -##### Instance - -`Instance` - -##### M - -`M` *extends* `unknown`[] - -#### Parameters - -##### this - -`Statics` & `EmberClassConstructor`\<`Instance`\> - -##### mixins? - -...`M` - -One or more Mixin classes - -#### Returns - -`Readonly`\<`Statics`\> & `EmberClassConstructor`\<`Instance`\> & `MergeArray`\<`M`\> - -#### Method - -extend - -#### Static - -#### For - -@ember/object - -#### Inherited from - -```ts -Helper.extend -``` - -*** - -### keys() - -```ts -static keys(): Set; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:157 - -**`Internal`** - -#### Returns - -`Set`\<`string`\> - -#### Inherited from - -```ts -Helper.keys -``` - -*** - -### proto() - -```ts -readonly static proto(): CoreObject; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:631 - -#### Returns - -`CoreObject` - -#### Inherited from - -```ts -Helper.proto -``` - -*** - -### reopenClass() - -```ts -readonly static reopenClass(this, ...mixins): C; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:583 - -Augments a constructor's own properties and functions: - -```javascript -import EmberObject from '@ember/object'; - -const MyObject = EmberObject.extend({ - name: 'an object' -}); - -MyObject.reopenClass({ - canBuild: false -}); - -MyObject.canBuild; // false -o = MyObject.create(); -``` - -In other words, this creates static properties and functions for the class. -These are only available on the class and not on any instance of that class. - -```javascript -import EmberObject from '@ember/object'; - -const Person = EmberObject.extend({ - name: '', - sayHello() { - alert(`Hello. My name is ${this.get('name')}`); - } -}); - -Person.reopenClass({ - species: 'Homo sapiens', - - createPerson(name) { - return Person.create({ name }); - } -}); - -let tom = Person.create({ - name: 'Tom Dale' -}); -let yehuda = Person.createPerson('Yehuda Katz'); - -tom.sayHello(); // "Hello. My name is Tom Dale" -yehuda.sayHello(); // "Hello. My name is Yehuda Katz" -alert(Person.species); // "Homo sapiens" -``` - -Note that `species` and `createPerson` are *not* valid on the `tom` and `yehuda` -variables. They are only valid on `Person`. - -To add functions and properties to instances of -a constructor by extending the constructor's prototype -see `reopen` - -#### Type Parameters - -##### C - -`C` *extends* *typeof* `CoreObject` - -#### Parameters - -##### this - -`C` - -##### mixins - -...(`Record`\<`string`, `unknown`\> \| `Mixin`)[] - -#### Returns - -`C` - -#### Method - -reopenClass - -#### For - -@ember/object - -#### Static - -#### Inherited from - -```ts -Helper.reopenClass -``` - -*** - -### toString() - -```ts -static toString(): string; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:632 - -#### Returns - -`string` - -#### Inherited from - -```ts -Helper.toString -``` - -*** - -### willReopen() - -```ts -readonly static willReopen(): void; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/core.d.ts:521 - -#### Returns - -`void` - -#### Inherited from - -```ts -Helper.willReopen -``` - -*** - -### without() - -```ts -static without(...args): Mixin; -``` - -Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_supports-color@8.1.1\_\_supports-color@8.1.1/node\_modules/ember-source/types/stable/@ember/object/mixin.d.ts:155 - -**`Internal`** - -#### Parameters - -##### args - -...`any`[] - -#### Returns - -`Mixin` - -#### Inherited from - -```ts -Helper.without -``` diff --git a/docs/framework/ember/reference/classes/useHotkeys.md b/docs/framework/ember/reference/classes/useHotkeys.md index 222e6be9..d0818b4a 100644 --- a/docs/framework/ember/reference/classes/useHotkeys.md +++ b/docs/framework/ember/reference/classes/useHotkeys.md @@ -3,16 +3,16 @@ id: useHotkeys title: useHotkeys --- -Defined in: [packages/ember-hotkeys/src/useHotkeys.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeys.ts#L13) +Defined in: [packages/ember-hotkeys/src/useHotkeys.ts:6](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeys.ts#L6) -Registers a reactive list of shortcuts with the helper's template lifecycle. +Registers a reactive list of hotkeys with the helper's template lifecycle. ## Extends - `default`\<\{ `Args`: \{ - `Named`: `HotkeyOptions`; - `Positional`: \[`HotkeyDefinition`[]\]; + `Named`: [`EmberHotkeyCommonOptions`](../type-aliases/EmberHotkeyCommonOptions.md); + `Positional`: \[`ReadonlyArray`\<[`EmberHotkeyDefinition`](../type-aliases/EmberHotkeyDefinition.md)\>\]; \}; `Return`: `void`; \}\> @@ -41,7 +41,10 @@ Defined in: node\_modules/.pnpm/ember-source@7.3.0\_@glimmer+component@2.1.1\_su ```ts Helper<{ - Args: { Positional: [Array]; Named: HotkeyOptions } + Args: { + Positional: [ReadonlyArray] + Named: EmberHotkeyCommonOptions + } Return: void }>.constructor ``` @@ -60,7 +63,7 @@ Defined in: node\_modules/.pnpm/@glint+template@1.9.0/node\_modules/@glint/templ ##### args -...\[`HotkeyDefinition`[], `NamedArgs`\<`HotkeyOptions`\>\] +...\[readonly [`EmberHotkeyDefinition`](../type-aliases/EmberHotkeyDefinition.md)[], `NamedArgs`\<[`EmberHotkeyCommonOptions`](../type-aliases/EmberHotkeyCommonOptions.md)\>\] #### Returns @@ -881,7 +884,7 @@ Helper.cacheFor compute(positional, options): void; ``` -Defined in: [packages/ember-hotkeys/src/useHotkeys.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeys.ts#L22) +Defined in: [packages/ember-hotkeys/src/useHotkeys.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeys.ts#L15) Override this function when writing a class-based helper. @@ -889,13 +892,13 @@ Override this function when writing a class-based helper. ##### positional -\[`HotkeyDefinition`[]\] +\[readonly [`EmberHotkeyDefinition`](../type-aliases/EmberHotkeyDefinition.md)[]\] The positional arguments to the helper ##### options -`HotkeyOptions` +[`EmberHotkeyCommonOptions`](../type-aliases/EmberHotkeyCommonOptions.md) #### Returns diff --git a/docs/framework/ember/reference/functions/createHotkeysScope.md b/docs/framework/ember/reference/functions/createHotkeysScope.md index d964013f..33a83615 100644 --- a/docs/framework/ember/reference/functions/createHotkeysScope.md +++ b/docs/framework/ember/reference/functions/createHotkeysScope.md @@ -7,7 +7,7 @@ title: createHotkeysScope function createHotkeysScope(defaultOptions?): EmberHotkeysScope; ``` -Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L29) +Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L24) Creates contextual helpers, modifiers, and recorders with shared, optionally reactive defaults. Pass the scope to child components through arguments to share configuration. diff --git a/docs/framework/ember/reference/functions/useAppHotkeyList.md b/docs/framework/ember/reference/functions/useAppHotkeyList.md new file mode 100644 index 00000000..9492e474 --- /dev/null +++ b/docs/framework/ember/reference/functions/useAppHotkeyList.md @@ -0,0 +1,24 @@ +--- +id: useAppHotkeyList +title: useAppHotkeyList +--- + +```ts +function useAppHotkeyList(owner): EmberAppHotkeyList; +``` + +Defined in: packages/ember-hotkeys/src/useAppHotkeys.ts:35 + +Lists every app hotkey with its live status, for cheat sheets and settings +screens. App hotkeys whose components have not rendered are `'inactive'`; +those without a hotkey are `'unbound'`. + +## Parameters + +### owner + +`object` + +## Returns + +[`EmberAppHotkeyList`](../type-aliases/EmberAppHotkeyList.md) diff --git a/docs/framework/ember/reference/functions/useAppHotkeys.md b/docs/framework/ember/reference/functions/useAppHotkeys.md new file mode 100644 index 00000000..3271f681 --- /dev/null +++ b/docs/framework/ember/reference/functions/useAppHotkeys.md @@ -0,0 +1,22 @@ +--- +id: useAppHotkeys +title: useAppHotkeys +--- + +```ts +function useAppHotkeys(owner): EmberHotkeyState>>>; +``` + +Defined in: packages/ember-hotkeys/src/useAppHotkeys.ts:19 + +Reads the effective app hotkeys from `provideAppHotkeys`, typed by `Register`. + +## Parameters + +### owner + +`object` + +## Returns + +[`EmberHotkeyState`](../interfaces/EmberHotkeyState.md)\<`Readonly`\<`Record`\<`string`, `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>\>\> diff --git a/docs/framework/ember/reference/functions/useCustomHotkeys.md b/docs/framework/ember/reference/functions/useCustomHotkeys.md new file mode 100644 index 00000000..aacfc299 --- /dev/null +++ b/docs/framework/ember/reference/functions/useCustomHotkeys.md @@ -0,0 +1,24 @@ +--- +id: useCustomHotkeys +title: useCustomHotkeys +--- + +```ts +function useCustomHotkeys(owner): EmberCustomHotkeys; +``` + +Defined in: packages/ember-hotkeys/src/useAppHotkeys.ts:50 + +Reads and changes the user's custom hotkeys. Changes apply to every +registered app hotkey immediately and are passed to +`onCustomHotkeysChange`, ready to save. + +## Parameters + +### owner + +`object` + +## Returns + +[`EmberCustomHotkeys`](../interfaces/EmberCustomHotkeys.md) diff --git a/docs/framework/ember/reference/functions/useHotkeyHint.md b/docs/framework/ember/reference/functions/useHotkeyHint.md index b7348f62..581185ae 100644 --- a/docs/framework/ember/reference/functions/useHotkeyHint.md +++ b/docs/framework/ember/reference/functions/useHotkeyHint.md @@ -10,9 +10,11 @@ function useHotkeyHint( options?): EmberHotkeyState; ``` -Defined in: [packages/ember-hotkeys/src/useHotkeyHint.ts:8](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeyHint.ts#L8) +Defined in: [packages/ember-hotkeys/src/useHotkeyHint.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeyHint.ts#L21) -Recomputes for held modifiers and tracked changes to the binding or options. +Recomputes for held modifiers and tracked changes to the hotkey or options. +A sequence reveals its hint from its first step, and an app hotkey uses its +effective hotkey (false while unbound). ## Parameters @@ -22,7 +24,9 @@ Recomputes for held modifiers and tracked changes to the binding or options. ### hotkey -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`RegisterableHotkey`\> +[`MaybeGetter`](../type-aliases/MaybeGetter.md)\< + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\>\> ### options? diff --git a/docs/framework/ember/reference/functions/useHotkeyRecorder.md b/docs/framework/ember/reference/functions/useHotkeyRecorder.md index a988aa62..07d03939 100644 --- a/docs/framework/ember/reference/functions/useHotkeyRecorder.md +++ b/docs/framework/ember/reference/functions/useHotkeyRecorder.md @@ -3,24 +3,142 @@ id: useHotkeyRecorder title: useHotkeyRecorder --- +## Call Signature + +```ts +function useHotkeyRecorder(owner, options): EmberHotkeyRecorder; +``` + +Defined in: [packages/ember-hotkeys/src/useHotkeyRecorder.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeyRecorder.ts#L24) + +Creates an owned recorder with autotracked state and current callback +options. With the default `maxSteps: 1`, the first valid chord is recorded. +With a larger `maxSteps`, each chord adds a step; the recording commits on +Enter (unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at +`maxSteps`, and `onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### owner + +`object` + +#### options + +`HotkeyChordRecorderOptions` + +### Returns + +[`EmberHotkeyRecorder`](../interfaces/EmberHotkeyRecorder.md)\<`HotkeyChord`\> + +## Call Signature + +```ts +function useHotkeyRecorder(owner, options): EmberHotkeyRecorder; +``` + +Defined in: [packages/ember-hotkeys/src/useHotkeyRecorder.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeyRecorder.ts#L28) + +Creates an owned recorder with autotracked state and current callback +options. With the default `maxSteps: 1`, the first valid chord is recorded. +With a larger `maxSteps`, each chord adds a step; the recording commits on +Enter (unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at +`maxSteps`, and `onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### owner + +`object` + +#### options + +`HotkeySequenceRecorderOptions` + +### Returns + +[`EmberHotkeyRecorder`](../interfaces/EmberHotkeyRecorder.md) + +## Call Signature + +```ts +function useHotkeyRecorder(owner, options): EmberHotkeyRecorder; +``` + +Defined in: [packages/ember-hotkeys/src/useHotkeyRecorder.ts:34](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeyRecorder.ts#L34) + +Creates an owned recorder with autotracked state and current callback +options. With the default `maxSteps: 1`, the first valid chord is recorded. +With a larger `maxSteps`, each chord adds a step; the recording commits on +Enter (unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at +`maxSteps`, and `onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### owner + +`object` + +#### options + +() => `HotkeySequenceRecorderOptions` + +### Returns + +[`EmberHotkeyRecorder`](../interfaces/EmberHotkeyRecorder.md) + +## Call Signature + +```ts +function useHotkeyRecorder(owner, options): EmberHotkeyRecorder; +``` + +Defined in: [packages/ember-hotkeys/src/useHotkeyRecorder.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeyRecorder.ts#L38) + +Creates an owned recorder with autotracked state and current callback +options. With the default `maxSteps: 1`, the first valid chord is recorded. +With a larger `maxSteps`, each chord adds a step; the recording commits on +Enter (unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at +`maxSteps`, and `onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### owner + +`object` + +#### options + +() => `HotkeyChordRecorderOptions` + +### Returns + +[`EmberHotkeyRecorder`](../interfaces/EmberHotkeyRecorder.md)\<`HotkeyChord`\> + +## Call Signature + ```ts function useHotkeyRecorder(owner, options): EmberHotkeyRecorder; ``` -Defined in: [packages/ember-hotkeys/src/useHotkeyRecorder.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeyRecorder.ts#L13) +Defined in: [packages/ember-hotkeys/src/useHotkeyRecorder.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeyRecorder.ts#L42) -Creates an owned recorder with autotracked state and current callback options. +Creates an owned recorder with autotracked state and current callback +options. With the default `maxSteps: 1`, the first valid chord is recorded. +With a larger `maxSteps`, each chord adds a step; the recording commits on +Enter (unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at +`maxSteps`, and `onRecord` receives a chord (one step) or a sequence. -## Parameters +### Parameters -### owner +#### owner `object` -### options +#### options -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`HotkeyRecorderOptions`\> +() => `HotkeyRecorderOptions` -## Returns +### Returns [`EmberHotkeyRecorder`](../interfaces/EmberHotkeyRecorder.md) diff --git a/docs/framework/ember/reference/functions/useHotkeyRegistrations.md b/docs/framework/ember/reference/functions/useHotkeyRegistrations.md index dee5036e..fc91f814 100644 --- a/docs/framework/ember/reference/functions/useHotkeyRegistrations.md +++ b/docs/framework/ember/reference/functions/useHotkeyRegistrations.md @@ -7,9 +7,9 @@ title: useHotkeyRegistrations function useHotkeyRegistrations(owner): HotkeyRegistrationsResult; ``` -Defined in: [packages/ember-hotkeys/src/useHotkeyRegistrations.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeyRegistrations.ts#L10) +Defined in: [packages/ember-hotkeys/src/useHotkeyRegistrations.ts:6](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeyRegistrations.ts#L6) -Reads public snapshots of hotkey and sequence registrations. +Reads public snapshots of every registration, chords and sequences. ## Parameters diff --git a/docs/framework/ember/reference/functions/useHotkeySequenceRecorder.md b/docs/framework/ember/reference/functions/useHotkeySequenceRecorder.md deleted file mode 100644 index 81779b0c..00000000 --- a/docs/framework/ember/reference/functions/useHotkeySequenceRecorder.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -id: useHotkeySequenceRecorder -title: useHotkeySequenceRecorder ---- - -```ts -function useHotkeySequenceRecorder(owner, options): EmberHotkeySequenceRecorder; -``` - -Defined in: [packages/ember-hotkeys/src/useHotkeySequenceRecorder.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/useHotkeySequenceRecorder.ts#L13) - -Creates an owned sequence recorder with explicit commit and cancel controls. - -## Parameters - -### owner - -`object` - -### options - -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`HotkeySequenceRecorderOptions`\> - -## Returns - -[`EmberHotkeySequenceRecorder`](../interfaces/EmberHotkeySequenceRecorder.md) diff --git a/docs/framework/ember/reference/index.md b/docs/framework/ember/reference/index.md index c8d57d98..67e7195f 100644 --- a/docs/framework/ember/reference/index.md +++ b/docs/framework/ember/reference/index.md @@ -9,14 +9,12 @@ title: "@tanstack/ember-hotkeys" - [onHotkeys](classes/onHotkeys.md) - [useHotkey](classes/useHotkey.md) - [useHotkeys](classes/useHotkeys.md) -- [useHotkeySequence](classes/useHotkeySequence.md) -- [useHotkeySequences](classes/useHotkeySequences.md) ## Interfaces - [DefaultHotkeysOptions](interfaces/DefaultHotkeysOptions.md) +- [EmberCustomHotkeys](interfaces/EmberCustomHotkeys.md) - [EmberHotkeyRecorder](interfaces/EmberHotkeyRecorder.md) -- [EmberHotkeySequenceRecorder](interfaces/EmberHotkeySequenceRecorder.md) - [EmberHotkeysScope](interfaces/EmberHotkeysScope.md) - [EmberHotkeyState](interfaces/EmberHotkeyState.md) - [HotkeyRegistrationsResult](interfaces/HotkeyRegistrationsResult.md) @@ -24,15 +22,21 @@ title: "@tanstack/ember-hotkeys" ## Type Aliases - [ElementHotkeyOptions](type-aliases/ElementHotkeyOptions.md) +- [EmberAppHotkeyList](type-aliases/EmberAppHotkeyList.md) +- [EmberHotkeyCommonOptions](type-aliases/EmberHotkeyCommonOptions.md) +- [EmberHotkeyDefinition](type-aliases/EmberHotkeyDefinition.md) +- [EmberHotkeyTarget](type-aliases/EmberHotkeyTarget.md) - [MaybeGetter](type-aliases/MaybeGetter.md) ## Functions - [createHotkeysScope](functions/createHotkeysScope.md) +- [useAppHotkeyList](functions/useAppHotkeyList.md) +- [useAppHotkeys](functions/useAppHotkeys.md) +- [useCustomHotkeys](functions/useCustomHotkeys.md) - [useHeldKeyCodes](functions/useHeldKeyCodes.md) - [useHeldKeys](functions/useHeldKeys.md) - [useHotkeyHint](functions/useHotkeyHint.md) - [useHotkeyRecorder](functions/useHotkeyRecorder.md) - [useHotkeyRegistrations](functions/useHotkeyRegistrations.md) -- [useHotkeySequenceRecorder](functions/useHotkeySequenceRecorder.md) - [useKeyHold](functions/useKeyHold.md) diff --git a/docs/framework/ember/reference/interfaces/DefaultHotkeysOptions.md b/docs/framework/ember/reference/interfaces/DefaultHotkeysOptions.md index 29341e94..a76a339f 100644 --- a/docs/framework/ember/reference/interfaces/DefaultHotkeysOptions.md +++ b/docs/framework/ember/reference/interfaces/DefaultHotkeysOptions.md @@ -3,7 +3,7 @@ id: DefaultHotkeysOptions title: DefaultHotkeysOptions --- -Defined in: [packages/ember-hotkeys/src/types.ts:56](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L56) +Defined in: [packages/ember-hotkeys/src/types.ts:85](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L85) Shared defaults; call-specific options take precedence. @@ -12,37 +12,21 @@ Shared defaults; call-specific options take precedence. ### hotkey? ```ts -optional hotkey?: HotkeyOptions; +optional hotkey?: EmberHotkeyCommonOptions; ``` -Defined in: [packages/ember-hotkeys/src/types.ts:57](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L57) +Defined in: [packages/ember-hotkeys/src/types.ts:87](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L87) -*** - -### hotkeyRecorder? - -```ts -optional hotkeyRecorder?: Partial; -``` - -Defined in: [packages/ember-hotkeys/src/types.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L59) +Defaults for every hotkey. `requireReset` applies to chords and `timeout` to sequences. *** -### hotkeySequence? +### hotkeyRecorder? ```ts -optional hotkeySequence?: SequenceOptions; +optional hotkeyRecorder?: Partial>; ``` -Defined in: [packages/ember-hotkeys/src/types.ts:58](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L58) - -*** - -### hotkeySequenceRecorder? - -```ts -optional hotkeySequenceRecorder?: Partial; -``` +Defined in: [packages/ember-hotkeys/src/types.ts:89](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L89) -Defined in: [packages/ember-hotkeys/src/types.ts:60](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L60) +Defaults for every recorder. diff --git a/docs/framework/ember/reference/interfaces/EmberCustomHotkeys.md b/docs/framework/ember/reference/interfaces/EmberCustomHotkeys.md new file mode 100644 index 00000000..2a211341 --- /dev/null +++ b/docs/framework/ember/reference/interfaces/EmberCustomHotkeys.md @@ -0,0 +1,111 @@ +--- +id: EmberCustomHotkeys +title: EmberCustomHotkeys +--- + +Defined in: [packages/ember-hotkeys/src/types.ts:62](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L62) + +Custom hotkey state and actions. + +## Properties + +### findConflicts + +```ts +findConflicts: (id, hotkey) => AppHotkey[]; +``` + +Defined in: [packages/ember-hotkeys/src/types.ts:78](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L78) + +Other app hotkeys that would collide if `id` used `hotkey`. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] + +*** + +### resetCustomHotkey + +```ts +resetCustomHotkey: (id) => void; +``` + +Defined in: [packages/ember-hotkeys/src/types.ts:74](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L74) + +Restores the default hotkey for an app hotkey. + +#### Parameters + +##### id + +`string` + +#### Returns + +`void` + +*** + +### resetCustomHotkeys + +```ts +resetCustomHotkeys: () => void; +``` + +Defined in: [packages/ember-hotkeys/src/types.ts:76](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L76) + +Restores every default hotkey. + +#### Returns + +`void` + +*** + +### setCustomHotkey + +```ts +setCustomHotkey: (id, hotkey) => AppHotkey[]; +``` + +Defined in: [packages/ember-hotkeys/src/types.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L69) + +Sets the user's hotkey for an app hotkey. `null` unbinds it. +Returns conflicting app hotkeys; when any exist, nothing changes. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` \| `null` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] + +*** + +### value + +```ts +readonly value: CustomHotkeys; +``` + +Defined in: [packages/ember-hotkeys/src/types.ts:64](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L64) + +The user's hotkeys that differ from the defaults. diff --git a/docs/framework/ember/reference/interfaces/EmberHotkeyRecorder.md b/docs/framework/ember/reference/interfaces/EmberHotkeyRecorder.md index 2efc077c..3ddb561e 100644 --- a/docs/framework/ember/reference/interfaces/EmberHotkeyRecorder.md +++ b/docs/framework/ember/reference/interfaces/EmberHotkeyRecorder.md @@ -3,10 +3,16 @@ id: EmberHotkeyRecorder title: EmberHotkeyRecorder --- -Defined in: [packages/ember-hotkeys/src/types.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L18) +Defined in: [packages/ember-hotkeys/src/types.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L33) Recording state and controls owned by the containing component. +## Type Parameters + +### THotkey + +`THotkey` *extends* `Hotkey` = `Hotkey` + ## Properties ### cancelRecording @@ -15,7 +21,7 @@ Recording state and controls owned by the containing component. cancelRecording: () => void; ``` -Defined in: [packages/ember-hotkeys/src/types.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L28) +Defined in: [packages/ember-hotkeys/src/types.ts:45](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L45) Stop, clear recorder state, and call onCancel. @@ -25,13 +31,29 @@ Stop, clear recorder state, and call onCancel. *** +### commitRecording + +```ts +commitRecording: () => void; +``` + +Defined in: [packages/ember-hotkeys/src/types.ts:47](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L47) + +Commit the current steps (multi-step recordings). + +#### Returns + +`void` + +*** + ### isRecording ```ts readonly isRecording: boolean; ``` -Defined in: [packages/ember-hotkeys/src/types.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L20) +Defined in: [packages/ember-hotkeys/src/types.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L35) Whether the recorder is listening for a shortcut. @@ -40,12 +62,12 @@ Whether the recorder is listening for a shortcut. ### recordedHotkey ```ts -readonly recordedHotkey: Hotkey | null; +readonly recordedHotkey: THotkey | null; ``` -Defined in: [packages/ember-hotkeys/src/types.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L22) +Defined in: [packages/ember-hotkeys/src/types.ts:39](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L39) -The last recorded binding, including brackets for physical codes. +The last committed hotkey, including brackets for physical codes. *** @@ -55,7 +77,7 @@ The last recorded binding, including brackets for physical codes. startRecording: () => void; ``` -Defined in: [packages/ember-hotkeys/src/types.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L24) +Defined in: [packages/ember-hotkeys/src/types.ts:41](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L41) Start a new recording session. @@ -65,13 +87,25 @@ Start a new recording session. *** +### steps + +```ts +readonly steps: HotkeySequence; +``` + +Defined in: [packages/ember-hotkeys/src/types.ts:37](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L37) + +Chords captured so far in a multi-step recording. + +*** + ### stopRecording ```ts stopRecording: () => void; ``` -Defined in: [packages/ember-hotkeys/src/types.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L26) +Defined in: [packages/ember-hotkeys/src/types.ts:43](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L43) Stop and clear recorder state without calling onRecord or onCancel. diff --git a/docs/framework/ember/reference/interfaces/EmberHotkeySequenceRecorder.md b/docs/framework/ember/reference/interfaces/EmberHotkeySequenceRecorder.md deleted file mode 100644 index 6d12be30..00000000 --- a/docs/framework/ember/reference/interfaces/EmberHotkeySequenceRecorder.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -id: EmberHotkeySequenceRecorder -title: EmberHotkeySequenceRecorder ---- - -Defined in: [packages/ember-hotkeys/src/types.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L32) - -Sequence recording state and controls. - -## Properties - -### cancelRecording - -```ts -cancelRecording: () => void; -``` - -Defined in: [packages/ember-hotkeys/src/types.ts:44](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L44) - -Discard the current session and call onCancel. - -#### Returns - -`void` - -*** - -### commitRecording - -```ts -commitRecording: () => void; -``` - -Defined in: [packages/ember-hotkeys/src/types.ts:46](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L46) - -Commit the current steps. Does nothing when no steps are recorded. - -#### Returns - -`void` - -*** - -### isRecording - -```ts -readonly isRecording: boolean; -``` - -Defined in: [packages/ember-hotkeys/src/types.ts:34](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L34) - -Whether the recorder is listening for chords. - -*** - -### recordedSequence - -```ts -readonly recordedSequence: HotkeySequence | null; -``` - -Defined in: [packages/ember-hotkeys/src/types.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L38) - -The last committed sequence. - -*** - -### startRecording - -```ts -startRecording: () => void; -``` - -Defined in: [packages/ember-hotkeys/src/types.ts:40](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L40) - -Start a new recording session. - -#### Returns - -`void` - -*** - -### steps - -```ts -readonly steps: HotkeySequence; -``` - -Defined in: [packages/ember-hotkeys/src/types.ts:36](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L36) - -Chords captured in the current session. - -*** - -### stopRecording - -```ts -stopRecording: () => void; -``` - -Defined in: [packages/ember-hotkeys/src/types.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L42) - -Stop without committing or calling onCancel. - -#### Returns - -`void` diff --git a/docs/framework/ember/reference/interfaces/EmberHotkeyState.md b/docs/framework/ember/reference/interfaces/EmberHotkeyState.md index c3e01298..e4968cfb 100644 --- a/docs/framework/ember/reference/interfaces/EmberHotkeyState.md +++ b/docs/framework/ember/reference/interfaces/EmberHotkeyState.md @@ -3,7 +3,7 @@ id: EmberHotkeyState title: EmberHotkeyState --- -Defined in: [packages/ember-hotkeys/src/types.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L13) +Defined in: [packages/ember-hotkeys/src/types.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L19) A reactive value. Read `.value` where the framework tracks dependencies. @@ -21,4 +21,4 @@ A reactive value. Read `.value` where the framework tracks dependencies. readonly value: T; ``` -Defined in: [packages/ember-hotkeys/src/types.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L14) +Defined in: [packages/ember-hotkeys/src/types.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L20) diff --git a/docs/framework/ember/reference/interfaces/EmberHotkeysScope.md b/docs/framework/ember/reference/interfaces/EmberHotkeysScope.md index 06479b11..1c298884 100644 --- a/docs/framework/ember/reference/interfaces/EmberHotkeysScope.md +++ b/docs/framework/ember/reference/interfaces/EmberHotkeysScope.md @@ -3,7 +3,7 @@ id: EmberHotkeysScope title: EmberHotkeysScope --- -Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L14) +Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L12) Helpers, modifiers, and recorder factories with shared defaults. Owners retain their own cleanup. @@ -15,7 +15,7 @@ Helpers, modifiers, and recorder factories with shared defaults. Owners retain t onHotkey: typeof onHotkey; ``` -Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L15) +Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L13) *** @@ -25,7 +25,7 @@ Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:15](https://github onHotkeys: typeof onHotkeys; ``` -Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L16) +Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L14) *** @@ -35,86 +35,160 @@ Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:16](https://github useHotkey: typeof useHotkey; ``` -Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L17) +Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L15) *** ### useHotkeyRecorder ```ts -useHotkeyRecorder: (owner, options) => EmberHotkeyRecorder; +useHotkeyRecorder: { + (owner, options): EmberHotkeyRecorder; + (owner, options): EmberHotkeyRecorder; + (owner, options): EmberHotkeyRecorder; + (owner, options): EmberHotkeyRecorder; + (owner, options): EmberHotkeyRecorder; +}; ``` -Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L21) +Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L17) -Creates an owned recorder with autotracked state and current callback options. +#### Call Signature -#### Parameters +```ts +(owner, options): EmberHotkeyRecorder; +``` -##### owner +Creates an owned recorder with autotracked state and current callback +options. With the default `maxSteps: 1`, the first valid chord is recorded. +With a larger `maxSteps`, each chord adds a step; the recording commits on +Enter (unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at +`maxSteps`, and `onRecord` receives a chord (one step) or a sequence. -`object` +##### Parameters -##### options +###### owner -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`HotkeyRecorderOptions`\> +`object` -#### Returns +###### options -[`EmberHotkeyRecorder`](EmberHotkeyRecorder.md) +`HotkeyChordRecorderOptions` -*** +##### Returns -### useHotkeys +[`EmberHotkeyRecorder`](EmberHotkeyRecorder.md)\<`HotkeyChord`\> + +#### Call Signature ```ts -useHotkeys: typeof useHotkeys; +(owner, options): EmberHotkeyRecorder; ``` -Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L18) +Creates an owned recorder with autotracked state and current callback +options. With the default `maxSteps: 1`, the first valid chord is recorded. +With a larger `maxSteps`, each chord adds a step; the recording commits on +Enter (unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at +`maxSteps`, and `onRecord` receives a chord (one step) or a sequence. -*** +##### Parameters + +###### owner + +`object` + +###### options + +`HotkeySequenceRecorderOptions` + +##### Returns + +[`EmberHotkeyRecorder`](EmberHotkeyRecorder.md) -### useHotkeySequence +#### Call Signature ```ts -useHotkeySequence: typeof useHotkeySequence; +(owner, options): EmberHotkeyRecorder; ``` -Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L19) +Creates an owned recorder with autotracked state and current callback +options. With the default `maxSteps: 1`, the first valid chord is recorded. +With a larger `maxSteps`, each chord adds a step; the recording commits on +Enter (unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at +`maxSteps`, and `onRecord` receives a chord (one step) or a sequence. -*** +##### Parameters + +###### owner + +`object` -### useHotkeySequenceRecorder +###### options + +() => `HotkeySequenceRecorderOptions` + +##### Returns + +[`EmberHotkeyRecorder`](EmberHotkeyRecorder.md) + +#### Call Signature ```ts -useHotkeySequenceRecorder: (owner, options) => EmberHotkeySequenceRecorder; +(owner, options): EmberHotkeyRecorder; ``` -Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L22) +Creates an owned recorder with autotracked state and current callback +options. With the default `maxSteps: 1`, the first valid chord is recorded. +With a larger `maxSteps`, each chord adds a step; the recording commits on +Enter (unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at +`maxSteps`, and `onRecord` receives a chord (one step) or a sequence. + +##### Parameters + +###### owner + +`object` -Creates an owned sequence recorder with explicit commit and cancel controls. +###### options -#### Parameters +() => `HotkeyChordRecorderOptions` -##### owner +##### Returns + +[`EmberHotkeyRecorder`](EmberHotkeyRecorder.md)\<`HotkeyChord`\> + +#### Call Signature + +```ts +(owner, options): EmberHotkeyRecorder; +``` + +Creates an owned recorder with autotracked state and current callback +options. With the default `maxSteps: 1`, the first valid chord is recorded. +With a larger `maxSteps`, each chord adds a step; the recording commits on +Enter (unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at +`maxSteps`, and `onRecord` receives a chord (one step) or a sequence. + +##### Parameters + +###### owner `object` -##### options +###### options -[`MaybeGetter`](../type-aliases/MaybeGetter.md)\<`HotkeySequenceRecorderOptions`\> +() => `HotkeyRecorderOptions` -#### Returns +##### Returns -[`EmberHotkeySequenceRecorder`](EmberHotkeySequenceRecorder.md) +[`EmberHotkeyRecorder`](EmberHotkeyRecorder.md) *** -### useHotkeySequences +### useHotkeys ```ts -useHotkeySequences: typeof useHotkeySequences; +useHotkeys: typeof useHotkeys; ``` -Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L20) +Defined in: [packages/ember-hotkeys/src/createHotkeysScope.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/createHotkeysScope.ts#L16) diff --git a/docs/framework/ember/reference/interfaces/HotkeyRegistrationsResult.md b/docs/framework/ember/reference/interfaces/HotkeyRegistrationsResult.md index 3c0b6c70..e9bbf96e 100644 --- a/docs/framework/ember/reference/interfaces/HotkeyRegistrationsResult.md +++ b/docs/framework/ember/reference/interfaces/HotkeyRegistrationsResult.md @@ -3,9 +3,9 @@ id: HotkeyRegistrationsResult title: HotkeyRegistrationsResult --- -Defined in: [packages/ember-hotkeys/src/types.ts:50](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L50) +Defined in: [packages/ember-hotkeys/src/types.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L51) -Live registration views from the shared hotkey and sequence managers. +Live registration views from the shared hotkey manager. ## Properties @@ -15,14 +15,6 @@ Live registration views from the shared hotkey and sequence managers. readonly hotkeys: HotkeyRegistrationView[]; ``` -Defined in: [packages/ember-hotkeys/src/types.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L51) - -*** - -### sequences - -```ts -readonly sequences: SequenceRegistrationView[]; -``` +Defined in: [packages/ember-hotkeys/src/types.ts:53](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L53) -Defined in: [packages/ember-hotkeys/src/types.ts:52](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L52) +Every registration, chords and sequences. Narrow each view with `kind`. diff --git a/docs/framework/ember/reference/type-aliases/ElementHotkeyOptions.md b/docs/framework/ember/reference/type-aliases/ElementHotkeyOptions.md index 2491e869..3765aab3 100644 --- a/docs/framework/ember/reference/type-aliases/ElementHotkeyOptions.md +++ b/docs/framework/ember/reference/type-aliases/ElementHotkeyOptions.md @@ -4,9 +4,9 @@ title: ElementHotkeyOptions --- ```ts -type ElementHotkeyOptions = Omit; +type ElementHotkeyOptions = Omit; ``` -Defined in: [packages/ember-hotkeys/src/onHotkeys.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/onHotkeys.ts#L13) +Defined in: [packages/ember-hotkeys/src/onHotkeys.ts:6](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/onHotkeys.ts#L6) Options for a modifier whose target is always its containing element. diff --git a/docs/framework/ember/reference/type-aliases/EmberAppHotkeyList.md b/docs/framework/ember/reference/type-aliases/EmberAppHotkeyList.md new file mode 100644 index 00000000..330b726e --- /dev/null +++ b/docs/framework/ember/reference/type-aliases/EmberAppHotkeyList.md @@ -0,0 +1,12 @@ +--- +id: EmberAppHotkeyList +title: EmberAppHotkeyList +--- + +```ts +type EmberAppHotkeyList = EmberHotkeyState[]>; +``` + +Defined in: [packages/ember-hotkeys/src/types.ts:57](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L57) + +Every app hotkey with its live status. diff --git a/docs/framework/ember/reference/type-aliases/EmberHotkeyCommonOptions.md b/docs/framework/ember/reference/type-aliases/EmberHotkeyCommonOptions.md new file mode 100644 index 00000000..e408ab9a --- /dev/null +++ b/docs/framework/ember/reference/type-aliases/EmberHotkeyCommonOptions.md @@ -0,0 +1,12 @@ +--- +id: EmberHotkeyCommonOptions +title: EmberHotkeyCommonOptions +--- + +```ts +type EmberHotkeyCommonOptions = AdapterCommonOptions; +``` + +Defined in: [packages/ember-hotkeys/src/types.ts:30](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L30) + +Options for every kind; each applies to the kinds that support it. diff --git a/docs/framework/ember/reference/type-aliases/EmberHotkeyDefinition.md b/docs/framework/ember/reference/type-aliases/EmberHotkeyDefinition.md new file mode 100644 index 00000000..0b91263d --- /dev/null +++ b/docs/framework/ember/reference/type-aliases/EmberHotkeyDefinition.md @@ -0,0 +1,12 @@ +--- +id: EmberHotkeyDefinition +title: EmberHotkeyDefinition +--- + +```ts +type EmberHotkeyDefinition = AdapterHotkeyDefinition; +``` + +Defined in: [packages/ember-hotkeys/src/types.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L27) + +One hotkey for `useHotkeys` and `onHotkeys`: a chord, a sequence, or an app hotkey. diff --git a/docs/framework/ember/reference/type-aliases/EmberHotkeyTarget.md b/docs/framework/ember/reference/type-aliases/EmberHotkeyTarget.md new file mode 100644 index 00000000..b16c4a3d --- /dev/null +++ b/docs/framework/ember/reference/type-aliases/EmberHotkeyTarget.md @@ -0,0 +1,12 @@ +--- +id: EmberHotkeyTarget +title: EmberHotkeyTarget +--- + +```ts +type EmberHotkeyTarget = Target | null; +``` + +Defined in: [packages/ember-hotkeys/src/types.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/types.ts#L24) + +The element to listen on. A `null` target skips registration until it is set. diff --git a/docs/framework/ember/reference/type-aliases/MaybeGetter.md b/docs/framework/ember/reference/type-aliases/MaybeGetter.md index 3cac1a9e..ed97ffdb 100644 --- a/docs/framework/ember/reference/type-aliases/MaybeGetter.md +++ b/docs/framework/ember/reference/type-aliases/MaybeGetter.md @@ -7,7 +7,7 @@ title: MaybeGetter type MaybeGetter = T | (() => T); ``` -Defined in: [packages/ember-hotkeys/src/utils.ts:5](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/utils.ts#L5) +Defined in: [packages/ember-hotkeys/src/utils.ts:8](https://github.com/TanStack/hotkeys/blob/main/packages/ember-hotkeys/src/utils.ts#L8) ## Type Parameters diff --git a/docs/framework/lit/guides/app-hotkeys.md b/docs/framework/lit/guides/app-hotkeys.md new file mode 100644 index 00000000..0369e4c9 --- /dev/null +++ b/docs/framework/lit/guides/app-hotkeys.md @@ -0,0 +1,197 @@ +--- +title: App Hotkeys Guide +id: app-hotkeys +--- + +`@hotkey` and `HotkeyController` register a hotkey while its component is mounted. A cheat sheet built from live registrations therefore misses hotkeys on routes that have not rendered yet, and a settings screen has nowhere to change them. App hotkeys solve both: you declare every hotkey in one place, publish them with `provideAppHotkeys`, and attach callbacks where the work happens. Users can then customize any of them, and your app decides how to save their choices. + +A declaration holds a hotkey's id, default hotkey, metadata, and options. It never holds a callback; `@hotkey` and `HotkeyController` still attach the callbacks. + +## Declare app hotkeys + +Use `defineAppHotkeys` in a module that contains only declarations. Keep it free of components and handlers, because it loads with your app shell: + +```ts +// app-hotkeys.ts +import { defineAppHotkeys } from '@tanstack/lit-hotkeys' + +export const appHotkeys = defineAppHotkeys({ + 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save', group: 'Document' } }, + 'doc.find': { + hotkey: 'Mod+F', + meta: { name: 'Find', group: 'Document' }, + context: 'editor', + }, + 'nav.home': { + hotkey: ['G', 'H'], + meta: { name: 'Go home', group: 'Navigation' }, + options: { timeout: 800 }, + }, + 'palette.open': { hotkey: null, meta: { name: 'Command palette' } }, +}) + +declare module '@tanstack/lit-hotkeys' { + interface Register { + appHotkeys: typeof appHotkeys + } +} +``` + +- The object key is the app hotkey's id. Treat ids as a stable contract, because saved custom hotkeys use them as keys. +- `hotkey` is the default: a chord, a sequence, or `null` for an action without a default. An unbound app hotkey registers nothing until the user assigns one. +- `options` describe the hotkey itself: `conflictBehavior`, `eventType`, `ignoreInputs`, `preventDefault`, `stopPropagation`, plus `requireReset` for a chord default or `timeout` for a sequence default. Options that don't match the default's kind are type errors. Pass `target` and `enabled` where you register the hotkey. +- `context` labels where a hotkey applies, such as `'editor'`. App hotkeys with different contexts may share a hotkey. It is used only for conflict checks and does not affect matching. + +The `Register` augmentation types the app hotkeys you read back, the ids accepted when changing custom hotkeys, and the `CustomHotkeys` type. + +## Provide app hotkeys + +```ts +// main.ts, before your elements render +import { provideAppHotkeys } from '@tanstack/lit-hotkeys' +import { appHotkeys } from './app-hotkeys' + +provideAppHotkeys(appHotkeys) +``` + +Lit has no provider component. `provideAppHotkeys` creates the shared `AppHotkeysStore` and publishes it for controllers, decorators, and devtools. Call it once at startup; it returns `{ store, dispose }`. + +## Register app hotkeys + +Pass an app hotkey wherever you pass a hotkey: + +```ts +import { LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import { HotkeyController, hotkey } from '@tanstack/lit-hotkeys' +import { appHotkeys } from './app-hotkeys' + +class Editor extends LitElement { + @property({ type: Boolean }) dirty = false + + @hotkey(appHotkeys['nav.home']) + goHome() {} + + constructor() { + super() + this.addController( + new HotkeyController(this, appHotkeys['doc.save'], () => this.save(), () => ({ + enabled: this.dirty, + })), + ) + } +} +``` + +Controllers and decorators look up the published store, so the registration follows the user's custom hotkey even though you import the declaration directly. Without provided app hotkeys, the declared default is used. + +The registration takes `meta` and `options` from the declaration. Options merge in this order: default options, then declared options, then call-site options. `meta` comes only from the declaration, so the cheat sheet and the registration cannot disagree. Call-site options are typed by the declared default's kind, so `timeout` is accepted for `nav.home` but not for `doc.save`. + +When the user changes a hotkey, registered app hotkeys move to the new hotkey immediately, even if it changes from a chord to a sequence. + +## Build a cheat sheet + +`AppHotkeysController.list` returns every app hotkey in declaration order, with its live status: + +| Status | Meaning | +| --- | --- | +| `'active'` | At least one enabled registration | +| `'disabled'` | Registered, but every registration has `enabled: false` | +| `'inactive'` | Has a hotkey, but nothing has registered it | +| `'unbound'` | No hotkey assigned | + +```ts +import { AppHotkeysController, formatForDisplay } from '@tanstack/lit-hotkeys' + +class CheatSheet extends LitElement { + private app = new AppHotkeysController(this) + + render() { + return html`
      + ${this.app.list.map( + ({ appHotkey, status, isCustom }) => html` +
    • + ${appHotkey.meta?.name}: + ${appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned'} + ${isCustom ? '(custom)' : ''} +
    • + `, + )} +
    ` + } +} +``` + +`AppHotkeysController` also exposes the effective `appHotkeys` and the custom hotkey actions below, and requests an update whenever any of them change. + +An app hotkey registered by many components, such as one per list row, appears once. Its `registrations` array holds each live registration. Devtools also list declared app hotkeys that are not registered. + +## Customize and persist hotkeys + +TanStack Hotkeys does not store anything. Pass the user's saved custom hotkeys in and save them when they change: + +```ts +import { provideAppHotkeys } from '@tanstack/lit-hotkeys' + +provideAppHotkeys(appHotkeys, { + customHotkeys: JSON.parse(localStorage.getItem('hotkeys') ?? '{}'), + onCustomHotkeysChange: (custom) => + localStorage.setItem('hotkeys', JSON.stringify(custom)), +}) +``` + +- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them. +- `customHotkeys` is read once; afterwards the shared store owns the state. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning. +- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile. + +Recorded hotkeys use `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others. + +### Change hotkeys + +`AppHotkeysController` reads and changes the user's custom hotkeys: + +```ts +import { + AppHotkeysController, + HotkeyRecorderController, + formatForDisplay, +} from '@tanstack/lit-hotkeys' +import type { AppHotkeyId } from '@tanstack/lit-hotkeys' + +class ShortcutRow extends LitElement { + @property() hotkeyId!: AppHotkeyId + private app = new AppHotkeysController(this) + + private recorder = new HotkeyRecorderController(this, () => ({ + maxSteps: 3, + validate: (hotkey) => { + const conflicts = this.app.findConflicts(this.hotkeyId, hotkey) + return conflicts.length === 0 || `Used by ${conflicts[0]?.meta?.name}` + }, + onRecord: (hotkey) => this.app.setCustomHotkey(this.hotkeyId, hotkey), + onClear: () => this.app.setCustomHotkey(this.hotkeyId, null), + })) + + render() { + const appHotkey = this.app.appHotkeys[this.hotkeyId] + return html` + ${appHotkey.meta?.name}: + ${appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned'} + + + ` + } +} +``` + +| Member | Description | +| --- | --- | +| `customHotkeys` | The hotkeys that differ from the defaults | +| `setCustomHotkey(id, hotkey)` | Sets a hotkey, or unbinds with `null`. Returns conflicting app hotkeys; when there are any, nothing changes. | +| `findConflicts(id, hotkey)` | App hotkeys that would collide if `id` used `hotkey` | +| `resetCustomHotkey(id)` | Restores one default | +| `resetCustomHotkeys()` | Restores every default | + +Two app hotkeys conflict when their contexts match or either has no `context`, and their hotkeys collide: equal chords, or one sequence starts with the other, including a sequence whose first step equals a chord. Conflicts are checked across every declaration, whether or not it is registered. + +Each app hotkey's `defaultHotkey` holds its declared default, and each cheat sheet item's `isCustom` reports whether the effective hotkey differs from it. diff --git a/docs/framework/lit/guides/formatting-display.md b/docs/framework/lit/guides/formatting-display.md index 1632eb43..bbe32328 100644 --- a/docs/framework/lit/guides/formatting-display.md +++ b/docs/framework/lit/guides/formatting-display.md @@ -3,7 +3,7 @@ title: Formatting & Display Guide id: formatting-display --- -Use `formatForDisplay` whenever a binding appears in a menu, button, hint, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, and parsed bindings. Store the original binding and format it at render time: the display label is not a registration string. +Use `formatForDisplay` whenever a hotkey appears in a menu, button, hint, cheat sheet, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, parsed bindings, and sequences. Store the original binding and format it at render time: the display label is not a registration string. ## Format a binding @@ -32,16 +32,18 @@ const parts = formatForDisplay(binding, { platform: 'mac', parts: true }) With `parts` omitted or false, the result is a string. A runtime boolean returns `string | string[]`. Parts preserve literal plus keys and ignore `separatorToken`. -Sequences are arrays of bindings. Format their steps individually: +## Format sequences -```ts -import type { HotkeySequence } from '@tanstack/lit-hotkeys' +Pass a sequence to format each step and join the steps with `sequenceSeparator`, a space by default: -const sequence: HotkeySequence = ['Mod+[KeyK]', 'C'] -const label = sequence.map((step) => formatForDisplay(step)).join(' → ') +```ts +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac' }) // '⌘ K C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' → ' }) // 'G → G' +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac', parts: true }) +// [['⌘', 'K'], ['C']] — one array of keycaps per step ``` -`formatHotkeySequence` only joins stored strings with spaces. It intentionally retains brackets and code names, so use the code above for user-facing labels. +With `parts: true`, a sequence returns one array per step, so you can render each step as a group of keycaps. ## Choose symbols and separators @@ -63,7 +65,7 @@ formatForDisplay('Control++', { }) // 'Ctrl · +' ``` -An empty separator joins labels directly. `undefined` or `null` uses the platform default. `formatWithLabels(binding, options)` is the shorthand for `formatForDisplay` with `useSymbols: false`. +An empty separator joins labels directly. `undefined` or `null` uses the platform default. `separatorToken` applies within a chord; `sequenceSeparator` applies between steps. ## Supply layout labels @@ -88,6 +90,10 @@ formatForDisplay('Mod+[KeyQ]', { The precedence is `keyLabels`, then a layout entry, then the fallback label. Layout entries receive normal letter casing and key symbols; missing or empty entries fall back. Explicit labels are final. All of these options affect display only. +## Compare hotkeys + +`areHotkeysEqual(a, b)` compares chords or sequences after resolving modifiers, so `Mod+S` equals `Meta+S` on macOS. Sequences compare step by step. `isHotkeySequence` narrows a `Hotkey` to a sequence, and `simplifyHotkey` turns a one-step sequence into its chord. + ## Parse and store bindings `parseHotkey` returns either a logical `key` or a physical `code`, plus resolved modifier flags. Narrow the union before inspecting the identity: @@ -105,6 +111,6 @@ formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S' Parsed modifiers are already resolved. To display a portable `Mod` binding on another platform, serialize with the original platform first, as above. `normalizeRegisterableHotkey` accepts strings or raw objects and preserves the logical/physical distinction. Do not store display labels or put a bracketed code in a logical `key` field. -Use `validateHotkey` when accepting strings from an external source. It returns `valid`, `errors`, and `warnings`; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). +Use `validateHotkey` when accepting a chord or sequence from an external source. It returns `valid`, `errors`, and `warnings`, and names the failing step of a sequence; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). Try these options together in the [vanilla formatter playground](../../../framework/vanilla/examples/formatForDisplay). diff --git a/docs/framework/lit/guides/hotkey-recording.md b/docs/framework/lit/guides/hotkey-recording.md index b68ffcbf..c8dcae4b 100644 --- a/docs/framework/lit/guides/hotkey-recording.md +++ b/docs/framework/lit/guides/hotkey-recording.md @@ -3,139 +3,129 @@ title: Hotkey Recording Guide id: hotkey-recording --- -TanStack Hotkeys provides the `HotkeyRecorderController` for building keyboard shortcut customization UIs. This lets users record their own shortcuts by pressing the desired key combination, similar to how system preferences or IDE shortcut editors work. +`HotkeyRecorderController` is for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the key combination they want, the same way system preferences or IDE shortcut editors work. By default it records one chord; set `maxSteps` to let users record a [sequence](./sequences) too. Recorders default to physical codes: recording a shortcut stores a string such as `Mod+[KeyS]`. Pass it directly to your hotkey registration and use `formatForDisplay` for the label. Set `recordBy: 'key'` when you intentionally want the produced character instead. -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Recorder controls read current options directly. Explicit `setOptions` values override constructor options for the supplied keys; other getters stay live. +TanStack Hotkeys automatically suppresses registered hotkey callbacks while any recorder is active. You do not need to disable hotkeys while recording. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. ## Basic usage ```ts -import { LitElement, html, nothing } from 'lit' -import { customElement } from 'lit/decorators.js' import { HotkeyRecorderController, formatForDisplay } from '@tanstack/lit-hotkeys' -@customElement('shortcut-recorder') class ShortcutRecorder extends LitElement { private recorder = new HotkeyRecorderController(this, { - onRecord: (hotkey) => { - console.log('Recorded:', hotkey) // e.g., "Mod+Shift+[KeyS]" - }, + onRecord: (hotkey) => console.log('Recorded:', hotkey), // e.g. 'Mod+Shift+[KeyS]' }) render() { const { isRecording, recordedHotkey } = this.recorder return html` -
    - + ` - : nothing} -
    + ? 'Press a key combination...' + : recordedHotkey + ? formatForDisplay(recordedHotkey) + : 'Click to record'} + ` } } ``` -## Controller API +## Recorder state and actions -`HotkeyRecorderController` exposes the following reactive getters and methods: +| Member | Description | +| --- | --- | +| `isRecording` | Whether the recorder is currently listening for key presses | +| `steps` | Chords captured so far in a multi-step recording | +| `recordedHotkey` | The last recorded hotkey, or `null` if nothing recorded | +| `startRecording()` | Start listening for key presses | +| `stopRecording()` | Stop listening without calling `onRecord` or `onCancel` | +| `cancelRecording()` | Stop listening and call `onCancel` | +| `commitRecording()` | Commit the current `steps` now (multi-step recordings) | -| Member | Type | Description | -|--------|------|-------------| -| `isRecording` | `boolean` (getter) | Whether the recorder is currently listening for key presses | -| `recordedHotkey` | `Hotkey \| null` (getter) | The last recorded hotkey string, or `null` if nothing recorded | -| `startRecording()` | `() => void` | Start listening for key presses | -| `stopRecording()` | `() => void` | Stop listening and reset recorder state without calling `onRecord` | -| `cancelRecording()` | `() => void` | Stop listening and discard any recorded hotkey | -| `setOptions(opts)` | `(Partial) => void` | Update callbacks at runtime | - -The controller registers itself with the host in its constructor, subscribes to the underlying `HotkeyRecorder` store on `hostConnected`, and cleans up on `hostDisconnected`. +The controller adds itself to the host and requests an update whenever its state changes. ## Options ### `recordBy` -Recorders default to `recordBy: 'code'`. Option+S producing `ß` on macOS records `Alt+[KeyS]`; Option+2 producing `™` records `Alt+[Digit2]`. The stored brackets preserve physical identity through state, serialization, and registration; `formatForDisplay` produces a readable label. There is no code-to-letter conversion. Existing authored strings such as `Mod+S` remain logical bindings. - -Set `recordBy: 'key'` to intentionally record the produced logical character. A code-mode event with no usable code is rejected rather than silently switching modes. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the produced character without synthetic Control/Alt while retaining Shift. Recording events, repeats, and their releases do not trigger registered hotkeys or sequences. - -Pass options as the second argument to the constructor: +Recorders default to `recordBy: 'code'`. Option+S producing `ß` on macOS records `Alt+[KeyS]`; Option+2 producing `™` records `Alt+[Digit2]`. The stored brackets preserve physical identity through state, serialization, and registration; `formatForDisplay` produces a readable label. Existing authored strings such as `Mod+S` remain logical hotkeys. -```ts -new HotkeyRecorderController(this, { - recordBy: 'code', // default; choose 'key' for logical characters - onRecord: (hotkey) => { /* called when a hotkey is recorded */ }, - onCancel: () => { /* called when recording is cancelled */ }, - onClear: () => { /* called when the recorded hotkey is cleared */ }, -}) -``` +Set `recordBy: 'key'` to intentionally record the produced logical character. A code-mode event with no usable code is rejected rather than silently switching modes. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the produced character without synthetic Control/Alt while retaining Shift. ### `onRecord` -Called when the user presses a valid key combination (a modifier + a non-modifier key, or a single non-modifier key). Receives the recorded `Hotkey` string. +Called when a recording commits. With the default `maxSteps: 1`, it receives a chord string such as `'Mod+[KeyS]'` as soon as the user presses a valid combination (a modifier + a non-modifier key, or a single non-modifier key). With a larger `maxSteps`, it receives a chord when one step was recorded, or a sequence otherwise. ### `onCancel` -Called when recording is cancelled (either by pressing Escape or calling `cancelRecording()`). +Called when recording is cancelled, by pressing Escape or calling `cancelRecording()`. ### `onClear` -Called when the recorded hotkey is cleared (by pressing Backspace or Delete during recording). +Called when the recording is cleared: Backspace or Delete with no modifiers and no recorded steps. Clearing calls only `onClear`; your app decides whether that removes a hotkey or restores a default. -## Recording behavior +### `ignoreInputs` -The recorder has specific behavior for different keys: +Defaults to `true`. The recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Escape still cancels recording even while an input is focused. Set `ignoreInputs: false` to record from inside input elements. -| Key | Behavior | -|-----|----------| -| Modifier only (Shift, Ctrl, etc.) | Waits for a non-modifier key; modifier-only presses don't complete a recording | -| Modifier + key (e.g., Ctrl+S) | Records the full combination | -| Single key (e.g., Escape, F1) | Records the single key | -| Escape | Cancels the recording | -| Backspace / Delete | Clears the currently recorded hotkey | +Options can also be a getter, such as `new HotkeyRecorderController(this, () => ({ maxSteps: 3, recordBy: this.mode, onRecord }))`, read during recording. -### `ignoreInputs` +## Recording sequences -The `HotkeyRecorderOptions` supports an `ignoreInputs` option (defaults to `true`). When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements. Keystrokes pass through to the input as usual. Pressing Escape still cancels recording even when focused on an input. Set `ignoreInputs: false` if you want the recorder to capture keys from within input elements. +Set `maxSteps` above 1 to record up to that many chords. Each chord adds a step, and the recording commits when the user presses Enter, when it reaches `maxSteps`, or after `idleTimeoutMs` without a new step: ```ts -new HotkeyRecorderController(this, { - ignoreInputs: false, // record even from inside inputs - onRecord: (hotkey) => console.log(hotkey), +private recorder = new HotkeyRecorderController(this, { + maxSteps: 3, + onRecord: (hotkey) => console.log('Recorded:', hotkey), // 'Mod+[KeyS]' or ['[KeyG]', '[KeyG]'] }) + +// Live preview while recording +get label() { + return this.recorder.isRecording + ? formatForDisplay(this.recorder.steps) || 'Press chords, then Enter…' + : formatForDisplay(this.recorder.recordedHotkey ?? []) +} ``` -### Mod auto-conversion +A one-step recording commits as a chord, so the result can always be passed straight to your hotkey registration. Use `maxSteps: Infinity` for no limit. -Recorded hotkeys automatically use the portable `Mod` format. If a user on macOS presses Command+S, the recorded hotkey is `Mod+[KeyS]` rather than `Meta+[KeyS]`, so the shortcut keeps working when it syncs to a machine on another OS. +| Option | Description | +| --- | --- | +| `maxSteps` | Maximum chords to record. Reaching it commits. Default `1`. | +| `commitKeys` | `'enter'` (default) commits on plain Enter. With `'none'`, plain Enter can be recorded as a step, and only `commitRecording()`, `idleTimeoutMs`, or `maxSteps` commit. | +| `idleTimeoutMs` | Commit automatically after this many milliseconds without a new step. The timer does not run before the first chord. | + +`commitKeys` and `idleTimeoutMs` are only accepted when `maxSteps` is set. With `maxSteps` above 1, `onRecord` and `validate` receive `Hotkey` (a chord or a sequence) instead of a chord. + +## Recording behavior + +| Key | Behavior | +| --- | --- | +| Modifier only (Shift, Ctrl, etc.) | Waits for a non-modifier key; modifier-only presses don't complete a step | +| Modifier + key (e.g., Ctrl+S) | Records the full combination | +| Single key (e.g., Escape, F1) | Records the single key | +| Enter (multi-step, `commitKeys: 'enter'`) | Commits the recorded steps | +| Escape | Cancels the recording | +| Backspace / Delete | Removes the last step; with no steps, clears and calls `onClear` | + +Repeats and IME composition never add a step. Recorded hotkeys use the portable `Mod` format: Command+S on macOS records `Mod+[KeyS]` rather than `Meta+[KeyS]`, so the primary modifier becomes Control if the hotkey is later used on Windows. ## Validation and conflicts -Both recorder APIs accept `detectConflicts`, `validate`, and `onReject`. For a single-hotkey recorder, these options can be supplied alongside `onRecord`: +The recorder accepts `detectConflicts`, `validate`, and `onReject` alongside `onRecord`: ```ts const options = { detectConflicts: { - // Use registration IDs to exclude the binding being edited: + // Use registration IDs to exclude the hotkey being edited: excludeIds: [registrationId], target: document, eventType: 'keydown', @@ -149,112 +139,86 @@ const options = { } ``` -`validate` returns true to accept, or false/a message to reject. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the candidate when available, and conflicting registration views for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. +`validate(hotkey, context)` returns true to accept, or false/a message to reject. The context holds the last chord's `event` and `parsedHotkey`, plus `events` and `parsedSequence` for every step. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the rejected `hotkey` when available, and conflicting registrations for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. A rejected multi-step recording keeps its steps so the user can edit them with Backspace. -`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a binding. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Both single bindings and sequence prefixes are checked. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can affect actual dispatch. +`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a hotkey. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Chords and sequence prefixes are checked: a recorded chord conflicts with a sequence that starts with it, and a recorded sequence conflicts with a registered chord equal to its first step. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute. -For checks outside recording, use `findHotkeyConflicts(bindingOrSequence, options)`. Without source `events`, it compares binding identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. +For checks outside recording, use `findHotkeyConflicts(hotkey, options)`. Without source `events`, it compares hotkey identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. ## Building a shortcut settings UI -Here's a more complete example of a shortcut customization panel: +For an app-wide settings screen, declare the hotkeys with [app hotkeys](./app-hotkeys) and save the recorder's result with `setCustomHotkey`. That keeps cheat sheets, conflicts, and persistence in one place. + +For a single component, ordinary state is enough: ```ts import { LitElement, html } from 'lit' -import { customElement, state } from 'lit/decorators.js' +import { state } from 'lit/decorators.js' import { HotkeyRecorderController, - HotkeyController, formatForDisplay, + getHotkeyManager, } from '@tanstack/lit-hotkeys' -import type { Hotkey } from '@tanstack/lit-hotkeys' +import type { Hotkey, HotkeyRegistrationHandle } from '@tanstack/lit-hotkeys' -interface ShortcutMap { - save: Hotkey - undo: Hotkey - search: Hotkey -} +type Action = 'save' | 'undo' | 'search' -@customElement('shortcut-settings') class ShortcutSettings extends LitElement { - @state() private shortcuts: ShortcutMap = { + @state() shortcuts: Record = { save: 'Mod+S', undo: 'Mod+Z', - search: 'Mod+K', + search: ['G', 'S'], } - - @state() private editingAction: keyof ShortcutMap | null = null + @state() editing: Action | null = null + private handles: Array = [] private recorder = new HotkeyRecorderController(this, { + maxSteps: 2, onRecord: (hotkey) => { - if (this.editingAction) { - this.shortcuts = { ...this.shortcuts, [this.editingAction]: hotkey } - this.editingAction = null - } - }, - onCancel: () => { - this.editingAction = null + if (this.editing) this.shortcuts = { ...this.shortcuts, [this.editing]: hotkey } + this.editing = null }, + onCancel: () => (this.editing = null), }) - private saveCtrl?: HotkeyController - private undoCtrl?: HotkeyController - private searchCtrl?: HotkeyController - - connectedCallback() { - super.connectedCallback() - this._registerHotkeys() - } - - updated() { - this._unregisterHotkeys() - this._registerHotkeys() - } - - private _registerHotkeys() { - this.saveCtrl = new HotkeyController(this, this.shortcuts.save, () => save()) - this.undoCtrl = new HotkeyController(this, this.shortcuts.undo, () => undo()) - this.searchCtrl = new HotkeyController(this, this.shortcuts.search, () => openSearch()) - } - - private _unregisterHotkeys() { - this.saveCtrl?.hostDisconnected() - this.undoCtrl?.hostDisconnected() - this.searchCtrl?.hostDisconnected() + // Re-register whenever the shortcuts change + updated(changed: Map) { + if (!changed.has('shortcuts')) return + this.handles.forEach((handle) => handle.unregister()) + this.handles = Object.entries(this.shortcuts).map(([action, hotkey]) => + getHotkeyManager().register(hotkey, () => this.run(action as Action)), + ) } disconnectedCallback() { super.disconnectedCallback() - this._unregisterHotkeys() + this.handles.forEach((handle) => handle.unregister()) } render() { - return html` -
    -

    Keyboard Shortcuts

    - ${(Object.entries(this.shortcuts) as Array<[keyof ShortcutMap, Hotkey]>).map( - ([action, hotkey]) => html` -
    - ${action} - -
    - `, - )} -
    - ` + return Object.entries(this.shortcuts).map( + ([action, hotkey]) => html` + + `, + ) + } + + run(action: Action) { + /* ... */ } } ``` ## Under the hood -The `HotkeyRecorderController` creates a `HotkeyRecorder` class instance and subscribes to its reactive state via the recorder's TanStack Store. The class manages its own keyboard event listeners and state, and the controller handles cleanup on disconnect. +`HotkeyRecorderController` wraps a `HotkeyRecorder` from `@tanstack/hotkeys`, subscribes the host to its state, and destroys it when the host disconnects. diff --git a/docs/framework/lit/guides/hotkeys.md b/docs/framework/lit/guides/hotkeys.md index 22376dc7..3e12d360 100644 --- a/docs/framework/lit/guides/hotkeys.md +++ b/docs/framework/lit/guides/hotkeys.md @@ -7,6 +7,23 @@ The `@hotkey` decorator is the primary way to register keyboard shortcuts in Lit Both approaches wrap the singleton `HotkeyManager` with automatic lifecycle management tied to Lit's `connectedCallback` / `disconnectedCallback`. +## Chords and sequences + +A hotkey is either a **chord**, keys pressed together such as `'Mod+S'`, or a **sequence**, chords pressed one after another such as `['G', 'G']`. Every API in this guide accepts both, and a one-step array such as `['Mod+S']` is the chord `'Mod+S'`. + +```ts +@hotkey('Mod+S') // chord +save() {} + +@hotkey(['G', 'G'], { timeout: 500 }) // sequence +scrollToTop() {} + +@hotkey('Escape', { requireReset: true }) // chord-only option +close() {} +``` + +Most options apply to both kinds. `requireReset` applies only to chords and `timeout` only to sequences, and TypeScript rejects options that don't match the hotkey. When the kind isn't known statically, such as a `Hotkey` loaded from settings, only the shared options are accepted. Defaults apply only to the kinds that support them. See the [Sequences Guide](./sequences) for timing and matching, and the [App Hotkeys Guide](./app-hotkeys) to declare hotkeys up front and let users customize them. + ## Logical keys and physical positions Use a logical binding when the shortcut should follow the character on the active layout. Use a physical binding when it should follow a keyboard position: @@ -96,7 +113,7 @@ Pass a new logical or physical binding through your framework's normal state mec ## Property getters -`HotkeyController` and `HotkeySequenceController` accept property getters or functions returning options. Controllers read options when the host connects and after each host update, so scoped targets can become available during rendering. +`HotkeyController` accepts property getters or functions returning options. Controllers read options when the host connects and after each host update, so scoped targets can become available during rendering. ```ts import { LitElement, html } from 'lit' @@ -383,7 +400,7 @@ Group is descriptive metadata, not an execution scope. A shortcuts panel can gro ## Introspecting registrations -Use `HotkeyRegistrationsController` to get a live view of all hotkey and sequence registrations. Use it to build shortcut palettes, help dialogs, or devtools. +Use the `HotkeyRegistrationsController` controller to get a live view of every registration, chords and sequences together. It's the building block for shortcut palettes and help dialogs. ```ts import { LitElement, html } from 'lit' @@ -395,42 +412,26 @@ class ShortcutPalette extends LitElement { private registrations = new HotkeyRegistrationsController(this) render() { - const { hotkeys, sequences } = this.registrations - - return html` -

    Keyboard Shortcuts

    -
      - ${hotkeys.map( - (reg) => html` -
    • - ${formatForDisplay(reg.hotkey)} - ${reg.options.meta?.name ? html` — ${reg.options.meta.name}` : ''} - ${reg.options.meta?.description ? html`

      ${reg.options.meta.description}

      ` : ''} -
    • - `, - )} -
    - ${sequences.length > 0 - ? html` -

    Sequences

    -
      - ${sequences.map( - (reg) => html` -
    • - ${reg.sequence.map((step) => formatForDisplay(step)).join(' → ')} - ${reg.options.meta?.name ? html` — ${reg.options.meta.name}` : ''} -
    • - `, - )} -
    - ` - : ''} - ` + return html`
      + ${this.registrations.hotkeys.map( + (reg) => html` +
    • + ${formatForDisplay(reg.hotkey)} + ${reg.options.meta?.name ? html` — ${reg.options.meta.name}` : ''} + ${reg.kind === 'sequence' + ? html`${reg.matchedStepCount}/${reg.hotkey.length} steps` + : ''} +
    • + `, + )} +
    ` } } ``` -The controller exposes `hotkeys` and `sequences` arrays. The `hotkeys` array contains registration objects with the hotkey string, options (including `meta`), and enabled state. The `sequences` array contains sequence registrations with the same structure. +The controller's `hotkeys` getter holds one array of registration views. Each view has an `id`, the `hotkey`, its `options` (including `meta`), its `target`, and a `triggerCount`. Narrow with `kind`: chord views add `parsedHotkey`; sequence views add `parsedSequence` and `matchedStepCount`, the steps matched so far in the current attempt. Disabled entries stay listed; unmounted registrations disappear. Format any hotkey with `formatForDisplay`, and group entries by `options.meta?.group` when building a help panel. + +Registrations only exist while their components are mounted. To list hotkeys from routes that have not rendered, declare them with [app hotkeys](./app-hotkeys). ## The hotkey manager diff --git a/docs/framework/lit/guides/key-state-tracking.md b/docs/framework/lit/guides/key-state-tracking.md index eb346f25..ef6cd7c5 100644 --- a/docs/framework/lit/guides/key-state-tracking.md +++ b/docs/framework/lit/guides/key-state-tracking.md @@ -233,6 +233,6 @@ tracker.areAllKeysHeld(['Shift', 'Control']) // boolean private hint = new HotkeyHintController(this, () => this.binding) // hint.value ``` -For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. +For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. A sequence reveals its hint from its first step, and an app hotkey uses its effective hotkey (no hint while unbound). Combine the boolean with the action's enabled state. The helper does not register a shortcut or determine whether its target is focused. The core equivalent is `matchesHeldModifiers(binding, heldKeys, options)`. diff --git a/docs/framework/lit/guides/sequence-recording.md b/docs/framework/lit/guides/sequence-recording.md deleted file mode 100644 index ba6f0d7a..00000000 --- a/docs/framework/lit/guides/sequence-recording.md +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: Sequence Recording Guide -id: sequence-recording ---- - -TanStack Hotkeys provides the `HotkeySequenceRecorderController` for building UIs where users record multi-chord sequences (Vim-style shortcuts). Each step is captured like a single hotkey chord; users finish with Enter by default, or you can use manual commit and optional idle timeout. - -Sequence recording uses `recordBy: 'code'` by default, preserving every step as a physical string such as `['[KeyG]', 'Alt+[KeyS]']`. Set `recordBy: 'key'` for logical characters. The shared rejection and conflict options follow the [hotkey recording guide](./hotkey-recording.md#validation-and-conflicts). - -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Recorder controls read current options directly. Explicit `setOptions` values override constructor options for the supplied keys; other getters stay live. - -## Basic usage - -```ts -import { LitElement, html, nothing } from 'lit' -import { customElement } from 'lit/decorators.js' -import { HotkeySequenceRecorderController, formatForDisplay } from '@tanstack/lit-hotkeys' -import type { HotkeySequence } from '@tanstack/lit-hotkeys' - -@customElement('sequence-recorder') -class SequenceRecorder extends LitElement { - private recorder = new HotkeySequenceRecorderController(this, { - onRecord: (sequence: HotkeySequence) => { - console.log('Recorded:', sequence) // e.g., ["G", "G"] - }, - }) - - render() { - const { isRecording, steps, recordedSequence } = this.recorder - return html` -
    - - ${isRecording - ? html`` - : nothing} -
    - ` - } -} -``` - -## Controller API - -`HotkeySequenceRecorderController` exposes the following reactive getters and methods: - -| Member | Type | Description | -|--------|------|-------------| -| `isRecording` | `boolean` | Whether the recorder is currently listening | -| `steps` | `HotkeySequence` | Chords captured in the current session | -| `recordedSequence` | `HotkeySequence \| null` | Last committed sequence | -| `startRecording()` | `() => void` | Start a new recording session | -| `stopRecording()` | `() => void` | Stop without calling `onRecord` | -| `cancelRecording()` | `() => void` | Stop and call `onCancel` | -| `commitRecording()` | `() => void` | Commit current `steps` (no-op if empty) | -| `setOptions(opts)` | `(Partial) => void` | Update options at runtime | - -The controller registers itself with the host in its constructor, subscribes to the underlying `HotkeySequenceRecorder` store on `hostConnected`, and cleans up on `hostDisconnected`. - -## Options - -`recordBy` defaults to `'code'`; choose `'key'` for logical characters. Repeats and IME composition never append steps. Modifier-only presses wait for a complete chord. - -Pass options as the second argument to the constructor: - -```ts -new HotkeySequenceRecorderController(this, { - onRecord: (sequence) => { /* called when a sequence is committed */ }, - onCancel: () => { /* called when recording is cancelled */ }, - onClear: () => { /* called when cleared via Backspace with no steps */ }, - commitKeys: 'enter', // or 'none' - idleTimeoutMs: 2000, -}) -``` - -### `onRecord` - -Called when a nonempty sequence is committed. Clearing calls only `onClear`. Receives the recorded `HotkeySequence` array. - -### `onCancel` - -Called when recording is cancelled (either by pressing Escape or calling `cancelRecording()`). - -### `onClear` - -Called when the sequence is cleared (by pressing Backspace or Delete during recording when no steps remain). - -### `commitKeys` - -Controls how the user finishes recording from the keyboard: - -- `'enter'` (default): plain Enter (no modifiers) commits when at least one step exists. -- `'none'`: only `commitRecording()` or `idleTimeoutMs` finishes recording; plain Enter can be recorded as a chord. - -### `commitOnEnter` - -When `commitKeys` is `'enter'`, set to `false` to treat Enter as a normal chord. Use `commitRecording()` or idle timeout to finish instead. - -### `idleTimeoutMs` - -Milliseconds of inactivity after the last completed chord to auto-commit. The timer does not run while waiting for the first chord. - -### `ignoreInputs` - -The `HotkeySequenceRecorderOptions` supports an `ignoreInputs` option (defaults to `true`). When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements. Keystrokes pass through to the input as usual. Pressing Escape still cancels recording even when focused on an input. Set `ignoreInputs: false` if you want the recorder to capture keys from within input elements. - -```ts -new HotkeySequenceRecorderController(this, { - ignoreInputs: false, // record even from inside inputs - onRecord: (sequence) => console.log(sequence), -}) -``` - -## Recording behavior - -| Input | Behavior | -|-------|----------| -| Valid chord | Appended to `steps`; listener stays active | -| Enter (no modifiers), `commitKeys: 'enter'`, `steps.length >= 1` | Commits and calls `onRecord` | -| Escape | Cancels; calls `onCancel` | -| Backspace / Delete (no modifiers) | Removes last step, or if empty runs only `onClear` and stops | - -Recorded chords use portable `Mod` format, same as `HotkeyRecorderController`. - -## Validation and conflicts - -Sequence `validate(sequence, { events, parsedSequence })` runs when committing and returns true, false, or a rejection message. `detectConflicts` checks sequence prefixes as well as single bindings. Rejected commits keep recording active with the steps intact so the user can edit them with Backspace. Empty Backspace/Delete clears and emits `onClear` only; removing a nonempty step does not commit. Recorded steps, commit keys, and releases are isolated from application handlers. - -## Under the hood - -The `HotkeySequenceRecorderController` creates a `HotkeySequenceRecorder` class instance and subscribes to its reactive state via `@tanstack/store`. The class manages its own keyboard event listeners and state, and the controller handles cleanup on disconnect. diff --git a/docs/framework/lit/guides/sequences.md b/docs/framework/lit/guides/sequences.md index aea3ab48..ac23fd45 100644 --- a/docs/framework/lit/guides/sequences.md +++ b/docs/framework/lit/guides/sequences.md @@ -3,331 +3,157 @@ title: Sequences Guide id: sequences --- -TanStack Hotkeys supports multi-key sequences: shortcuts where you press keys one after another rather than simultaneously. Sequences are common in Vim-style navigation, cheat codes, and multi-step commands. +A sequence is a hotkey made of chords pressed one after another rather than together. Think Vim-style navigation, cheat codes, or multi-step commands. Sequences are ordinary hotkeys: register them with `@hotkey` and `HotkeyController`, list them with `HotkeyRegistrationsController`, record them with `HotkeyRecorderController`, and format them with `formatForDisplay`. -In Lit, registration is declarative via the `@hotkeySequence` decorator, or imperative via `HotkeySequenceController` when the sequence or options are built at runtime. Both use the same singleton `SequenceManager`. - -Sequence steps use the same string syntax as single hotkeys. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. Display steps with `sequence.map((step) => formatForDisplay(step)).join(' → ')`. - -## Reactive options - -Sequence options support the same [property getter patterns](./hotkeys.md#property-getters) as hotkey options, including `enabled`, `timeout`, and `target`. Updates preserve the registration when its sequence, target, and platform stay the same. Use `HotkeySequenceController` for options that depend on the host instance. +Sequence steps use the same syntax as chords. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. ## Basic usage -### The `@hotkeySequence` decorator - -Decorate a method to run when the user completes the key sequence: +Pass an array of chords wherever you pass a hotkey: ```ts import { LitElement, html } from 'lit' import { customElement } from 'lit/decorators.js' -import { hotkeySequence } from '@tanstack/lit-hotkeys' +import { hotkey } from '@tanstack/lit-hotkeys' -@customElement('vim-view') -class VimView extends LitElement { - @hotkeySequence(['G', 'G']) - scrollTop() { +@customElement('my-app') +class MyApp extends LitElement { + // Vim-style: press g then g to scroll to top + @hotkey(['G', 'G']) + scrollToTop() { window.scrollTo({ top: 0, behavior: 'smooth' }) } - - render() { - return html`
    Press g then g to scroll to top
    ` - } } ``` -The first argument is an array of `Hotkey` strings representing each step in the sequence. The user must press them in order within the timeout window. - -The method receives the `KeyboardEvent` and [`HotkeyCallbackContext`](../../../reference/interfaces/HotkeyCallbackContext.md) like `@hotkey`: - -```ts -import type { HotkeyCallbackContext } from '@tanstack/lit-hotkeys' - -@hotkeySequence(['G', 'G']) -scrollTop(event: KeyboardEvent, context: HotkeyCallbackContext) { - console.log(context.hotkey) -} -``` +The user must press the steps in order, each within the timeout window. A one-step array such as `['Escape']` is the chord `'Escape'`. -### `HotkeySequenceController` +## Many sequences at once -Use the controller when the sequence or callback cannot be expressed as a static decorator (e.g. data-driven shortcuts): +Add a `HotkeyController` for each hotkey. Chords and sequences use the same controller: ```ts -import { LitElement, html } from 'lit' -import { customElement } from 'lit/decorators.js' -import { HotkeySequenceController } from '@tanstack/lit-hotkeys' - -@customElement('vim-view') -class VimView extends LitElement { - private scrollTopSeq = new HotkeySequenceController( - this, - ['G', 'G'], - () => this.scrollTop(), - ) +import { HotkeyController } from '@tanstack/lit-hotkeys' +class Editor extends LitElement { constructor() { super() - this.addController(this.scrollTopSeq) - } - - private scrollTop() { - window.scrollTo({ top: 0, behavior: 'smooth' }) - } - - render() { - return html`
    Press g then g to scroll to top
    ` - } -} -``` - -## Many sequences at once - -Register several sequences on the same element by applying multiple `@hotkeySequence` decorators (one per method), or by adding multiple `HotkeySequenceController` instances with `addController`. There is no Lit equivalent to React's `useHotkeySequences` hook; several decorators on one class are the idiomatic pattern. - -```ts -@customElement('vim-navigation') -class VimNavigation extends LitElement { - @hotkeySequence(['G', 'G']) - goTop() { - scrollToTop() - } - - @hotkeySequence(['G', 'Shift+G']) - goBottom() { - scrollToBottom() - } - - @hotkeySequence(['D', 'D'], { timeout: 500 }) - deleteLine() { - deleteCurrentLine() - } - - render() { - return html`
    ` + this.addController(new HotkeyController(this, ['G', 'G'], () => this.scrollToTop())) + this.addController( + new HotkeyController(this, ['D', 'D'], () => this.deleteLine(), { timeout: 500 }), + ) + this.addController(new HotkeyController(this, 'Mod+S', () => this.save())) } } ``` ## Sequence options -Pass options as the second argument to `@hotkeySequence` (or to `HotkeySequenceController`): - -```ts -@hotkeySequence(['G', 'G'], { - timeout: 1000, // Time allowed between keys (ms) - enabled: true, // Whether the sequence is active at connect time -}) -scrollTop() { - window.scrollTo({ top: 0, behavior: 'smooth' }) -} -``` +Sequences accept the same options as chords, except `requireReset`, plus `timeout`. ### `timeout` -The maximum time (in milliseconds) allowed between consecutive key presses. If the user takes longer than this between any two keys, the sequence resets. Defaults to `1000` (1 second). +The maximum time (in milliseconds) allowed between consecutive steps. If the user takes longer than this between any two steps, the sequence resets. Defaults to `1000` (1 second). ```ts -@hotkeySequence(['D', 'D'], { timeout: 500 }) -deleteLine() { - deleteCurrentLine() -} - -@hotkeySequence(['Shift+Z', 'Shift+Z'], { timeout: 2000 }) -forceQuit() { - quitWithoutSaving() -} -``` - -### `enabled` - -Controls whether the sequence is registered when the element connects. Defaults to `true`. If `enabled` is `false` at connection time, registration is skipped. To turn sequences on or off later, reconnect the element, or create a new `HotkeySequenceController` when your state changes. +// Fast sequence - user must type quickly +@hotkey(['D', 'D'], { timeout: 500 }) +deleteLine() {} -```ts -@hotkeySequence(['G', 'G'], { enabled: true }) -scrollToTop() { - window.scrollTo({ top: 0, behavior: 'smooth' }) -} +// Slow sequence - user has more time between keys +@hotkey(['Shift+Z', 'Shift+Z'], { timeout: 2000 }) +forceQuit() {} ``` -### Default options - -When you omit options, the library uses the same defaults as the core [`SequenceOptions`](../../../reference/interfaces/SequenceOptions.md): `timeout: 1000`, `preventDefault` / `stopPropagation` enabled, smart `ignoreInputs`, and platform auto-detection. If you omit `target`, the Lit adapter resolves it to `document` when the controller connects in the browser. +`timeout` is only accepted when the hotkey is a sequence. -### `meta` +### `enabled` and `meta` -Sequences support the same `meta` option as hotkeys. Attach a `name` and `description` for use in shortcut palettes and devtools. +These work exactly as they do for chords. Disabled sequences stay registered and visible in devtools, and `meta` flows to registrations, devtools, and cheat sheets. See the [Hotkeys Guide](./hotkeys). ```ts -@hotkeySequence(['G', 'G'], { meta: { name: 'Go to Top', description: 'Scroll to the top of the page' } }) -scrollTop() { window.scrollTo({ top: 0, behavior: 'smooth' }) } - -// Or with HotkeySequenceController: -new HotkeySequenceController(this, ['G', 'G'], () => this.scrollTop(), { - meta: { name: 'Go to Top', description: 'Scroll to the top of the page' }, -}) +private vimMode = true + +constructor() { + super() + this.addController( + new HotkeyController(this, ['G', 'G'], () => this.scrollToTop(), () => ({ + enabled: this.vimMode, + meta: { name: 'Go to top', description: 'Scroll to the top of the page' }, + })), + ) +} ``` -See the [Hotkeys Guide](./hotkeys.md#metadata-name--description) for details on declaration merging and introspecting registrations. - ## Sequences with modifiers -Each step in a sequence can include modifiers: +Each step in a sequence can include modifiers, and consecutive steps can repeat the same modifier: ```ts -@hotkeySequence(['Mod+K', 'Mod+C']) -commentSelection() { - commentSelection() -} - -@hotkeySequence(['G', 'Shift+G']) -scrollBottom() { - window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }) -} -``` +// Ctrl+K followed by Ctrl+C (VS Code-style comment) +@hotkey(['Mod+K', 'Mod+C']) +commentSelection() {} -## Chained modifier chords - -This example follows physical R and T positions. Brackets retain those positions even when the keys produce different letters. Other sequences can continue using logical characters. - -You can repeat the same modifier across consecutive steps, for example `Shift+R` then `Shift+T`: - -```ts -@hotkeySequence(['Shift+[KeyR]', 'Shift+[KeyT]']) -chordSequence() { - runAfterChords() -} +// Physical R then T positions, both with Shift +@hotkey(['Shift+[KeyR]', 'Shift+[KeyT]']) +doNextAction() {} ``` -### Modifier-only keys between steps - -While a sequence is in progress, the manager ignores modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone, with no letter or other key). They neither advance the sequence nor reset progress. That way a user can tap Shift (or hold it) between chords such as `Shift+R` and `Shift+T` without breaking the sequence, much like Vim-style flows where a modifier may be pressed before the next chord. +Brackets retain physical positions even when the keys produce different letters. While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored. They neither advance the sequence nor reset progress, so a user can tap or hold Shift between `Shift+R` and `Shift+T`. -## Common sequence patterns - -### Vim-style navigation +## Vim-style navigation ```ts -@customElement('vim-navigation') class VimNavigation extends LitElement { - @hotkeySequence(['G', 'G']) - goTop() { - scrollToTop() - } - - @hotkeySequence(['G', 'Shift+G']) - goBottom() { - scrollToBottom() - } - - @hotkeySequence(['D', 'D']) - deleteLine() { - deleteCurrentLine() - } - - @hotkeySequence(['D', 'W']) - deleteWord() { - deleteCurrentWord() - } - - @hotkeySequence(['C', 'I', 'W']) - changeInnerWord() { - changeInnerWordImpl() - } - - render() { - return html`
    ` - } + @hotkey(['G', 'G']) scrollToTop() {} + @hotkey(['G', 'Shift+G']) scrollToBottom() {} + @hotkey(['D', 'D']) deleteLine() {} + @hotkey(['D', 'W']) deleteWord() {} + @hotkey(['C', 'I', 'W']) changeInnerWord() {} } ``` -### Konami code +## Displaying sequences -```ts -@hotkeySequence( - [ - 'ArrowUp', - 'ArrowUp', - 'ArrowDown', - 'ArrowDown', - 'ArrowLeft', - 'ArrowRight', - 'ArrowLeft', - 'ArrowRight', - 'B', - 'A', - ], - { timeout: 2000 }, -) -enableEasterEgg() { - showEasterEgg() -} -``` - -### Multi-step commands +`formatForDisplay` formats each step and joins them with `sequenceSeparator`, a space by default: ```ts -@hotkeySequence(['H', 'E', 'L', 'P']) -openHelp() { - showHelp() -} +formatForDisplay(['Mod+K', 'Mod+C'], { platform: 'mac' }) // '⌘ K ⌘ C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' then ' }) // 'G then G' ``` -## How sequences work +Registration views report each sequence's progress in `matchedStepCount`, so a help panel can show how far the user has typed. See the [Hotkeys Guide](./hotkeys). -Both `SequenceManager` and `createSequenceMatcher` ignore modifier-only events, IME composition, and automatic keydown repeats. These events neither advance the sequence nor refresh its timeout: holding G does not complete a two-press G sequence. The manager prefers exact matches over weaker logical-key fallbacks while preserving equally strong matches. +## How sequences work -The `SequenceManager` (singleton) handles all sequence registrations. When a key is pressed: +The `HotkeyManager` tracks progress for every sequence registration. When a key is pressed: -1. It checks if the key matches the next expected step in any registered sequence -2. If it matches, the sequence advances to the next step +1. It checks whether the key matches the next expected step of each sequence +2. If it matches, that sequence advances 3. If the timeout expires between steps, the sequence resets 4. When all steps are completed, the callback fires -5. Modifier-only keydowns are ignored (they neither advance nor reset the sequence) - -### Overlapping sequences - -Multiple sequences can share the same prefix. The manager tracks progress for each sequence independently: -```ts -@hotkeySequence(['D', 'D']) -dd() { - deleteLine() -} +Modifier-only events, IME composition, and automatic keydown repeats neither advance a sequence nor refresh its timeout: holding G does not complete a two-press G sequence. Exact matches take priority over weaker logical-key fallbacks. -@hotkeySequence(['D', 'W']) -dw() { - deleteWord() -} - -@hotkeySequence(['D', 'I', 'W']) -diw() { - deleteInnerWord() -} -``` - -After pressing `D`, the manager waits for the next key to determine which sequence to complete. +Multiple sequences can share a prefix, such as `['D', 'D']`, `['D', 'W']`, and `['D', 'I', 'W']`. Progress is tracked for each sequence independently, so after pressing `D` the manager waits for the next key to determine which sequence completes. ### Conflicting registrations -For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate bindings. `conflictBehavior` applies to equivalent sequences without rewriting their stored strings. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection also checks prefixes and observed physical/logical overlap. +For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate hotkeys. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection and app hotkey conflicts are stricter: there, a sequence also conflicts with a chord equal to its first step and with any sequence it starts. -## The sequence manager +## Standalone matching -Under the hood, `@hotkeySequence` and `HotkeySequenceController` use the singleton `SequenceManager`. You can also use the core `createSequenceMatcher` function for standalone sequence matching without the singleton: +For sequence matching without the singleton manager, use `createSequenceMatcher`: ```ts import { createSequenceMatcher } from '@tanstack/lit-hotkeys' -const matcher = createSequenceMatcher(['G', 'G'], { - timeout: 1000, -}) +const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 }) document.addEventListener('keydown', (e) => { if (matcher.match(e)) { console.log('Sequence completed!') } - console.log('Progress:', matcher.getProgress()) // e.g., 1/2 + console.log('Progress:', matcher.getProgress()) // e.g., 1 }) ``` diff --git a/docs/framework/lit/quick-start.md b/docs/framework/lit/quick-start.md index 937519ab..ac68b9d3 100644 --- a/docs/framework/lit/quick-start.md +++ b/docs/framework/lit/quick-start.md @@ -154,21 +154,21 @@ class MyModal extends LitElement { ### Multi-key sequences -Register Vim-style key sequences with the `@hotkeySequence` decorator or `HotkeySequenceController`: +Pass an array of chords to the `@hotkey` decorator or `HotkeyController` to register a Vim-style sequence: ```ts import { LitElement } from 'lit' import { customElement } from 'lit/decorators.js' -import { hotkeySequence } from '@tanstack/lit-hotkeys' +import { hotkey } from '@tanstack/lit-hotkeys' @customElement('vim-editor') class VimEditor extends LitElement { - @hotkeySequence(['G', 'G']) + @hotkey(['G', 'G']) scrollToTop() { window.scrollTo({ top: 0 }) } - @hotkeySequence(['G', 'Shift+G']) + @hotkey(['G', 'Shift+G']) scrollToBottom() { window.scrollTo({ top: document.body.scrollHeight }) } @@ -272,7 +272,7 @@ class SaveButton extends LitElement { The Lit adapter gives you two complementary approaches: -| | Decorators (`@hotkey`, `@hotkeySequence`) | Controllers (`HotkeyController`, etc.) | +| | Decorator (`@hotkey`) | Controllers (`HotkeyController`, etc.) | |---|---|---| | **Best for** | Declarative method binding | Reactive state, dynamic hotkeys | | **Registration** | Automatic on connect/disconnect | Automatic via `hostConnected`/`hostDisconnected` | @@ -281,11 +281,14 @@ The Lit adapter gives you two complementary approaches: Use decorators when you simply want a method to fire on a key combo. Use controllers when you need reactive state (held keys, recording) or dynamic hotkey registration. +## Declaring app hotkeys + +To list every hotkey in a cheat sheet, including hotkeys whose components haven't mounted, and to let users customize them, declare them once with `defineAppHotkeys` and provide them with `provideAppHotkeys`. See the [App Hotkeys Guide](./guides/app-hotkeys). + ## Next steps - [Hotkeys Guide](./guides/hotkeys) - Deep dive into `@hotkey` decorator and `HotkeyController` options - [Sequences Guide](./guides/sequences) - Multi-key sequence handling - [Hotkey Recording Guide](./guides/hotkey-recording) - Building shortcut customization UIs -- [Sequence Recording Guide](./guides/sequence-recording) - Capture multi-step shortcuts - [Key State Tracking Guide](./guides/key-state-tracking) - Real-time key state monitoring - [Formatting & Display Guide](./guides/formatting-display) - Platform-aware hotkey formatting diff --git a/docs/framework/lit/reference/classes/AppHotkeysController.md b/docs/framework/lit/reference/classes/AppHotkeysController.md new file mode 100644 index 00000000..0dda7364 --- /dev/null +++ b/docs/framework/lit/reference/classes/AppHotkeysController.md @@ -0,0 +1,244 @@ +--- +id: AppHotkeysController +title: AppHotkeysController +--- + +Defined in: controllers/app-hotkeys.ts:41 + +A Lit ReactiveController for app hotkeys provided with `provideAppHotkeys`: +the effective app hotkeys, a cheat sheet list with live status, and the +user's custom hotkeys. Requests an update when any of them change. + +## Example + +```ts +class CheatSheet extends LitElement { + private app = new AppHotkeysController(this) + + render() { + return html`
      + ${this.app.list.map( + ({ appHotkey, status }) => html`
    • ${appHotkey.meta?.name}: ${status}
    • `, + )} +
    ` + } +} +``` + +## Implements + +- `ReactiveController` + +## Constructors + +### Constructor + +```ts +new AppHotkeysController(_host): AppHotkeysController; +``` + +Defined in: controllers/app-hotkeys.ts:44 + +#### Parameters + +##### \_host + +`ReactiveControllerHost` + +#### Returns + +`AppHotkeysController` + +## Accessors + +### appHotkeys + +#### Get Signature + +```ts +get appHotkeys(): RegisteredAppHotkeys; +``` + +Defined in: controllers/app-hotkeys.ts:49 + +Effective app hotkeys, typed by `Register`. + +##### Returns + +`RegisteredAppHotkeys` + +*** + +### customHotkeys + +#### Get Signature + +```ts +get customHotkeys(): CustomHotkeys; +``` + +Defined in: controllers/app-hotkeys.ts:65 + +The user's hotkeys that differ from the defaults. + +##### Returns + +`CustomHotkeys` + +*** + +### list + +#### Get Signature + +```ts +get list(): AppHotkeyListItem>[]; +``` + +Defined in: controllers/app-hotkeys.ts:54 + +Every app hotkey with its live status, in declaration order. + +##### Returns + +`AppHotkeyListItem`\<`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>[] + +## Methods + +### findConflicts() + +```ts +findConflicts(id, hotkey): AppHotkey[]; +``` + +Defined in: controllers/app-hotkeys.ts:91 + +Other app hotkeys that would collide if `id` used `hotkey`. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] + +*** + +### hostConnected() + +```ts +hostConnected(): void; +``` + +Defined in: controllers/app-hotkeys.ts:98 + +Called when the host is connected to the component tree. For custom +element hosts, this corresponds to the `connectedCallback()` lifecycle, +which is only called when the component is connected to the document. + +#### Returns + +`void` + +#### Implementation of + +```ts +ReactiveController.hostConnected +``` + +*** + +### hostDisconnected() + +```ts +hostDisconnected(): void; +``` + +Defined in: controllers/app-hotkeys.ts:108 + +Called when the host is disconnected from the component tree. For custom +element hosts, this corresponds to the `disconnectedCallback()` lifecycle, +which is called the host or an ancestor component is disconnected from the +document. + +#### Returns + +`void` + +#### Implementation of + +```ts +ReactiveController.hostDisconnected +``` + +*** + +### resetCustomHotkey() + +```ts +resetCustomHotkey(id): void; +``` + +Defined in: controllers/app-hotkeys.ts:81 + +Restores the default hotkey for an app hotkey. + +#### Parameters + +##### id + +`string` + +#### Returns + +`void` + +*** + +### resetCustomHotkeys() + +```ts +resetCustomHotkeys(): void; +``` + +Defined in: controllers/app-hotkeys.ts:86 + +Restores every default hotkey. + +#### Returns + +`void` + +*** + +### setCustomHotkey() + +```ts +setCustomHotkey(id, hotkey): AppHotkey[]; +``` + +Defined in: controllers/app-hotkeys.ts:73 + +Sets the user's hotkey for an app hotkey. `null` unbinds it. +Returns conflicting app hotkeys; when any exist, nothing changes. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` \| `null` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] diff --git a/docs/framework/lit/reference/classes/HotkeyController.md b/docs/framework/lit/reference/classes/HotkeyController.md index c92b58c5..f5673d25 100644 --- a/docs/framework/lit/reference/classes/HotkeyController.md +++ b/docs/framework/lit/reference/classes/HotkeyController.md @@ -3,20 +3,27 @@ id: HotkeyController title: HotkeyController --- -Defined in: [controllers/hotkey.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L26) +Defined in: [controllers/hotkey.ts:74](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L74) -A Lit ReactiveController that registers a keyboard hotkey when the host -element is connected and unregisters it when the host is disconnected. +A Lit ReactiveController that registers a hotkey when the host element is +connected and unregisters it when the host is disconnected. The hotkey can +be a chord such as `'Mod+S'`, a sequence such as `['G', 'G']`, or an app +hotkey from `defineAppHotkeys`, which uses its custom hotkey from +`provideAppHotkeys`. + +Chords accept `requireReset`; sequences accept `timeout`. ## Example ```ts class MyElement extends LitElement { - private hotkey = new HotkeyController(this, 'Mod+S', () => this.save()) + private save = new HotkeyController(this, 'Mod+S', () => this.doSave()) + private top = new HotkeyController(this, ['G', 'G'], () => this.scrollToTop()) constructor() { super() - this.addController(this.hotkey) + this.addController(this.save) + this.addController(this.top) } } ``` @@ -31,27 +38,27 @@ class MyElement extends LitElement { ```ts new HotkeyController( - _host, - _hotkey, + host, + hotkey, callback, - _options?): HotkeyController; + options?): HotkeyController; ``` -Defined in: [controllers/hotkey.ts:37](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L37) +Defined in: [controllers/hotkey.ts:86](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L86) #### Parameters -##### \_host +##### host `ReactiveControllerHost` The Lit component that owns this controller. Add the controller with `addController()`. -##### \_hotkey +##### hotkey -`RegisterableHotkey` +`AppHotkey` -The shortcut to register. +The chord, sequence, or app hotkey to register. ##### callback @@ -59,9 +66,9 @@ The shortcut to register. Called with the host as `this`. -##### \_options? +##### options? -`HotkeyOptions` \| (() => `HotkeyOptions`) +`MaybeGetter`\<[`AppHotkeyControllerOptions`](../type-aliases/AppHotkeyControllerOptions.md)\<`AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\>\>\> Options or a getter. Property getters are read on connection and after host updates. @@ -69,6 +76,40 @@ Options or a getter. Property getters are read on connection and after host upda `HotkeyController` +### Constructor + +```ts +new HotkeyController( + host, + hotkey, + callback, + options?): HotkeyController; +``` + +Defined in: [controllers/hotkey.ts:92](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L92) + +#### Parameters + +##### host + +`ReactiveControllerHost` + +##### hotkey + +`RegisterableHotkey` + +##### callback + +`HotkeyCallback` + +##### options? + +`MaybeGetter`\<[`HotkeyControllerCommonOptions`](../type-aliases/HotkeyControllerCommonOptions.md)\> + +#### Returns + +`HotkeyController` + ## Methods ### hostConnected() @@ -77,7 +118,7 @@ Options or a getter. Property getters are read on connection and after host upda hostConnected(): void; ``` -Defined in: [controllers/hotkey.ts:48](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L48) +Defined in: [controllers/hotkey.ts:108](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L108) Registers when connected and a target is available. @@ -99,7 +140,7 @@ ReactiveController.hostConnected hostDisconnected(): void; ``` -Defined in: [controllers/hotkey.ts:67](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L67) +Defined in: [controllers/hotkey.ts:141](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L141) Releases registrations when the host disconnects. @@ -121,7 +162,7 @@ ReactiveController.hostDisconnected hostUpdated(): void; ``` -Defined in: [controllers/hotkey.ts:54](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L54) +Defined in: [controllers/hotkey.ts:115](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L115) Refreshes options after rendering, when scoped targets are available. diff --git a/docs/framework/lit/reference/classes/HotkeyHintController.md b/docs/framework/lit/reference/classes/HotkeyHintController.md index dc264706..ed8337ba 100644 --- a/docs/framework/lit/reference/classes/HotkeyHintController.md +++ b/docs/framework/lit/reference/classes/HotkeyHintController.md @@ -3,7 +3,7 @@ id: HotkeyHintController title: HotkeyHintController --- -Defined in: [controllers/hotkey-hint.ts:6](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-hint.ts#L6) +Defined in: [controllers/hotkey-hint.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-hint.ts#L16) Modifier hint controller. Getter arguments can read reactive host properties. @@ -22,7 +22,7 @@ new HotkeyHintController( options?): HotkeyHintController; ``` -Defined in: [controllers/hotkey-hint.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-hint.ts#L11) +Defined in: [controllers/hotkey-hint.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-hint.ts#L21) Attaches to the host; getter arguments can read changing reactive properties. @@ -34,7 +34,9 @@ Attaches to the host; getter arguments can read changing reactive properties. ##### hotkey -`RegisterableHotkey` \| (() => `RegisterableHotkey`) + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\> + \| (() => RegisterableHotkey \| AppHotkey\) ##### options? @@ -54,7 +56,7 @@ Attaches to the host; getter arguments can read changing reactive properties. get value(): boolean; ``` -Defined in: [controllers/hotkey-hint.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-hint.ts#L20) +Defined in: [controllers/hotkey-hint.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-hint.ts#L31) Current visibility; combine with the action’s enabled state when rendering. @@ -70,7 +72,7 @@ Current visibility; combine with the action’s enabled state when rendering. hostConnected(): void; ``` -Defined in: [controllers/hotkey-hint.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-hint.ts#L25) +Defined in: [controllers/hotkey-hint.ts:36](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-hint.ts#L36) Subscribes on connection and requests renders only when hint visibility changes. @@ -92,7 +94,7 @@ ReactiveController.hostConnected hostDisconnected(): void; ``` -Defined in: [controllers/hotkey-hint.ts:39](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-hint.ts#L39) +Defined in: [controllers/hotkey-hint.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-hint.ts#L55) Releases the subscription and clears cached visibility while disconnected. @@ -114,7 +116,7 @@ ReactiveController.hostDisconnected hostUpdate(): void; ``` -Defined in: [controllers/hotkey-hint.ts:34](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-hint.ts#L34) +Defined in: [controllers/hotkey-hint.ts:50](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-hint.ts#L50) Re-evaluates getter arguments when host properties trigger an update. diff --git a/docs/framework/lit/reference/classes/HotkeyRecorderController.md b/docs/framework/lit/reference/classes/HotkeyRecorderController.md index 088fd270..70464a78 100644 --- a/docs/framework/lit/reference/classes/HotkeyRecorderController.md +++ b/docs/framework/lit/reference/classes/HotkeyRecorderController.md @@ -3,13 +3,15 @@ id: HotkeyRecorderController title: HotkeyRecorderController --- -Defined in: [controllers/hotkey-recorder.ts:40](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L40) +Defined in: [controllers/hotkey-recorder.ts:48](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L48) A Lit ReactiveController that records keyboard shortcuts. -Wraps 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. +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. Escape cancels; +Backspace/Delete removes the last step or clears. ## Example @@ -49,22 +51,22 @@ class ShortcutSettings extends LitElement { ### Constructor ```ts -new HotkeyRecorderController(_host, _options): HotkeyRecorderController; +new HotkeyRecorderController(host, options): HotkeyRecorderController; ``` -Defined in: [controllers/hotkey-recorder.ts:65](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L65) +Defined in: [controllers/hotkey-recorder.ts:80](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L80) #### Parameters -##### \_host +##### host `ReactiveControllerHost` The Lit component that owns this controller. -##### \_options +##### options -`HotkeyRecorderOptions` \| (() => `HotkeyRecorderOptions`) +`HotkeyChordRecorderOptions` Options or a getter, read during recording. @@ -72,6 +74,94 @@ Options or a getter, read during recording. `HotkeyRecorderController` +### Constructor + +```ts +new HotkeyRecorderController(host, options): HotkeyRecorderController; +``` + +Defined in: [controllers/hotkey-recorder.ts:81](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L81) + +#### Parameters + +##### host + +`ReactiveControllerHost` + +##### options + +`HotkeySequenceRecorderOptions` + +#### Returns + +`HotkeyRecorderController` + +### Constructor + +```ts +new HotkeyRecorderController(host, options): HotkeyRecorderController; +``` + +Defined in: [controllers/hotkey-recorder.ts:88](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L88) + +#### Parameters + +##### host + +`ReactiveControllerHost` + +##### options + +() => `HotkeySequenceRecorderOptions` + +#### Returns + +`HotkeyRecorderController` + +### Constructor + +```ts +new HotkeyRecorderController(host, options): HotkeyRecorderController; +``` + +Defined in: [controllers/hotkey-recorder.ts:92](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L92) + +#### Parameters + +##### host + +`ReactiveControllerHost` + +##### options + +() => `HotkeyChordRecorderOptions` + +#### Returns + +`HotkeyRecorderController` + +### Constructor + +```ts +new HotkeyRecorderController(host, options): HotkeyRecorderController; +``` + +Defined in: [controllers/hotkey-recorder.ts:96](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L96) + +#### Parameters + +##### host + +`ReactiveControllerHost` + +##### options + +() => `HotkeyRecorderOptions` + +#### Returns + +`HotkeyRecorderController` + ## Accessors ### isRecording @@ -82,7 +172,7 @@ Options or a getter, read during recording. get isRecording(): boolean; ``` -Defined in: [controllers/hotkey-recorder.ts:52](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L52) +Defined in: [controllers/hotkey-recorder.ts:62](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L62) Whether recording is currently active. @@ -100,14 +190,32 @@ Whether recording is currently active. get recordedHotkey(): Hotkey | null; ``` -Defined in: [controllers/hotkey-recorder.ts:57](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L57) +Defined in: [controllers/hotkey-recorder.ts:67](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L67) -The currently recorded hotkey (for live preview). +The last committed hotkey: a chord, or a sequence of two or more steps. ##### Returns `Hotkey` \| `null` +*** + +### steps + +#### Get Signature + +```ts +get steps(): HotkeySequence; +``` + +Defined in: [controllers/hotkey-recorder.ts:72](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L72) + +Chords recorded so far in a multi-step recording (for live preview). + +##### Returns + +`HotkeySequence` + ## Methods ### cancelRecording() @@ -116,7 +224,7 @@ The currently recorded hotkey (for live preview). cancelRecording(): void; ``` -Defined in: [controllers/hotkey-recorder.ts:122](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L122) +Defined in: [controllers/hotkey-recorder.ts:167](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L167) Cancel recording without saving. @@ -126,13 +234,29 @@ Cancel recording without saving. *** +### commitRecording() + +```ts +commitRecording(): void; +``` + +Defined in: [controllers/hotkey-recorder.ts:172](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L172) + +Commit the recorded steps now (multi-step recordings). + +#### Returns + +`void` + +*** + ### hostConnected() ```ts hostConnected(): void; ``` -Defined in: [controllers/hotkey-recorder.ts:79](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L79) +Defined in: [controllers/hotkey-recorder.ts:118](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L118) Subscribes to the recorder store and updates the internal state when changes occur. @@ -154,7 +278,7 @@ ReactiveController.hostConnected hostDisconnected(): void; ``` -Defined in: [controllers/hotkey-recorder.ts:98](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L98) +Defined in: [controllers/hotkey-recorder.ts:139](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L139) Unsubscribes from the recorder store and destroys the recorder instance to prevent memory leaks. @@ -176,7 +300,7 @@ ReactiveController.hostDisconnected setOptions(options): void; ``` -Defined in: [controllers/hotkey-recorder.ts:107](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L107) +Defined in: [controllers/hotkey-recorder.ts:149](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L149) Updates the recorder options (e.g. callbacks). @@ -198,7 +322,7 @@ Updates the recorder options (e.g. callbacks). startRecording(): void; ``` -Defined in: [controllers/hotkey-recorder.ts:112](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L112) +Defined in: [controllers/hotkey-recorder.ts:157](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L157) Start recording a new hotkey. @@ -214,7 +338,7 @@ Start recording a new hotkey. stopRecording(): void; ``` -Defined in: [controllers/hotkey-recorder.ts:117](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L117) +Defined in: [controllers/hotkey-recorder.ts:162](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-recorder.ts#L162) Stop recording (same as cancel but without calling onCancel). diff --git a/docs/framework/lit/reference/classes/HotkeyRegistrationsController.md b/docs/framework/lit/reference/classes/HotkeyRegistrationsController.md index 703fd76b..2e2ecdef 100644 --- a/docs/framework/lit/reference/classes/HotkeyRegistrationsController.md +++ b/docs/framework/lit/reference/classes/HotkeyRegistrationsController.md @@ -3,12 +3,11 @@ id: HotkeyRegistrationsController title: HotkeyRegistrationsController --- -Defined in: [controllers/hotkey-registrations.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-registrations.ts#L35) +Defined in: [controllers/hotkey-registrations.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-registrations.ts#L25) -A Lit ReactiveController that tracks all hotkey and sequence registrations. - -Subscribes to the singleton HotkeyManager and SequenceManager stores and -triggers host updates whenever registrations change. +A Lit ReactiveController that reads every hotkey registration, chords and +sequences, from the singleton HotkeyManager and requests an update when +they change. Narrow each view with `kind`. ## Example @@ -17,13 +16,11 @@ class ShortcutPalette extends LitElement { private registrations = new HotkeyRegistrationsController(this) render() { - return html` -
      - ${this.registrations.hotkeys.map( - (reg) => html`
    • ${reg.options.meta?.name ?? reg.hotkey}
    • `, - )} -
    - ` + return html`
      + ${this.registrations.hotkeys.map( + (reg) => html`
    • ${reg.options.meta?.name}: ${formatForDisplay(reg.hotkey)}
    • `, + )} +
    ` } } ``` @@ -40,7 +37,7 @@ class ShortcutPalette extends LitElement { new HotkeyRegistrationsController(_host): HotkeyRegistrationsController; ``` -Defined in: [controllers/hotkey-registrations.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-registrations.ts#L51) +Defined in: [controllers/hotkey-registrations.ts:34](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-registrations.ts#L34) #### Parameters @@ -62,32 +59,14 @@ Defined in: [controllers/hotkey-registrations.ts:51](https://github.com/TanStack get hotkeys(): HotkeyRegistrationView[]; ``` -Defined in: [controllers/hotkey-registrations.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-registrations.ts#L42) +Defined in: [controllers/hotkey-registrations.ts:30](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-registrations.ts#L30) -All registered hotkeys (public view, no callbacks). +Every registration, chords and sequences (public view, no callbacks). ##### Returns `HotkeyRegistrationView`[] -*** - -### sequences - -#### Get Signature - -```ts -get sequences(): SequenceRegistrationView[]; -``` - -Defined in: [controllers/hotkey-registrations.ts:47](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-registrations.ts#L47) - -All registered sequences. - -##### Returns - -`SequenceRegistrationView`[] - ## Methods ### hostConnected() @@ -96,7 +75,7 @@ All registered sequences. hostConnected(): void; ``` -Defined in: [controllers/hotkey-registrations.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-registrations.ts#L55) +Defined in: [controllers/hotkey-registrations.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-registrations.ts#L38) Called when the host is connected to the component tree. For custom element hosts, this corresponds to the `connectedCallback()` lifecycle, @@ -120,7 +99,7 @@ ReactiveController.hostConnected hostDisconnected(): void; ``` -Defined in: [controllers/hotkey-registrations.ts:81](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-registrations.ts#L81) +Defined in: [controllers/hotkey-registrations.ts:52](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-registrations.ts#L52) Called when the host is disconnected from the component tree. For custom element hosts, this corresponds to the `disconnectedCallback()` lifecycle, diff --git a/docs/framework/lit/reference/classes/HotkeySequenceController.md b/docs/framework/lit/reference/classes/HotkeySequenceController.md deleted file mode 100644 index e96591c3..00000000 --- a/docs/framework/lit/reference/classes/HotkeySequenceController.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -id: HotkeySequenceController -title: HotkeySequenceController ---- - -Defined in: [controllers/hotkey-sequence.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence.ts#L26) - -A Lit ReactiveController that registers a keyboard sequence (e.g. Vim-style) -when the host element is connected and unregisters it when the host is disconnected. - -## Example - -```ts -class MyElement extends LitElement { - private seq = new HotkeySequenceController(this, ['G', 'G'], () => this.goToTop()) - - constructor() { - super() - this.addController(this.seq) - } -} -``` - -## Implements - -- `ReactiveController` - -## Constructors - -### Constructor - -```ts -new HotkeySequenceController( - _host, - _sequence, - callback, - _options?): HotkeySequenceController; -``` - -Defined in: [controllers/hotkey-sequence.ts:37](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence.ts#L37) - -#### Parameters - -##### \_host - -`ReactiveControllerHost` - -The Lit component that owns this controller. Add the controller with `addController()`. - -##### \_sequence - -`HotkeySequence` - -The sequence to register. - -##### callback - -`HotkeyCallback` - -Called with the host as `this`. - -##### \_options? - -`SequenceOptions` \| (() => `SequenceOptions`) - -Options or a getter. Property getters are read on connection and after host updates. - -#### Returns - -`HotkeySequenceController` - -## Methods - -### hostConnected() - -```ts -hostConnected(): void; -``` - -Defined in: [controllers/hotkey-sequence.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence.ts#L49) - -Registers when connected and a target is available. - -#### Returns - -`void` - -#### Implementation of - -```ts -ReactiveController.hostConnected -``` - -*** - -### hostDisconnected() - -```ts -hostDisconnected(): void; -``` - -Defined in: [controllers/hotkey-sequence.ts:68](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence.ts#L68) - -Releases registrations when the host disconnects. - -#### Returns - -`void` - -#### Implementation of - -```ts -ReactiveController.hostDisconnected -``` - -*** - -### hostUpdated() - -```ts -hostUpdated(): void; -``` - -Defined in: [controllers/hotkey-sequence.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence.ts#L55) - -Refreshes options after rendering, when scoped targets are available. - -#### Returns - -`void` - -#### Implementation of - -```ts -ReactiveController.hostUpdated -``` diff --git a/docs/framework/lit/reference/classes/HotkeySequenceRecorderController.md b/docs/framework/lit/reference/classes/HotkeySequenceRecorderController.md deleted file mode 100644 index 0e3958f2..00000000 --- a/docs/framework/lit/reference/classes/HotkeySequenceRecorderController.md +++ /dev/null @@ -1,259 +0,0 @@ ---- -id: HotkeySequenceRecorderController -title: HotkeySequenceRecorderController ---- - -Defined in: [controllers/hotkey-sequence-recorder.ts:45](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence-recorder.ts#L45) - -A Lit ReactiveController that records multi-chord sequences (Vim-style shortcuts). - -Wraps the framework-agnostic `HotkeySequenceRecorder` class, managing store -subscriptions and host update lifecycle automatically. - -## Example - -```ts -class ShortcutSettings extends LitElement { - private recorder = new HotkeySequenceRecorderController(this, { - onRecord: (sequence) => { - this.sequence = sequence - this.requestUpdate() - }, - onCancel: () => { - console.log('Recording cancelled') - }, - }) - - private sequence: HotkeySequence | null = null - - render() { - return html` - - ${this.recorder.steps.length - ? html`
    Steps: ${this.recorder.steps.join(' → ')}
    ` - : nothing} - ${this.recorder.recordedSequence - ? html`
    Recorded: ${this.recorder.recordedSequence.join(' → ')}
    ` - : nothing} - ` - } -} -``` - -## Implements - -- `ReactiveController` - -## Constructors - -### Constructor - -```ts -new HotkeySequenceRecorderController(_host, _options): HotkeySequenceRecorderController; -``` - -Defined in: [controllers/hotkey-sequence-recorder.ts:77](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence-recorder.ts#L77) - -#### Parameters - -##### \_host - -`ReactiveControllerHost` - -The Lit component that owns this controller. - -##### \_options - -`HotkeySequenceRecorderOptions` \| (() => `HotkeySequenceRecorderOptions`) - -Options or a getter, read during recording. - -#### Returns - -`HotkeySequenceRecorderController` - -## Accessors - -### isRecording - -#### Get Signature - -```ts -get isRecording(): boolean; -``` - -Defined in: [controllers/hotkey-sequence-recorder.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence-recorder.ts#L59) - -Whether recording is currently active. - -##### Returns - -`boolean` - -*** - -### recordedSequence - -#### Get Signature - -```ts -get recordedSequence(): HotkeySequence | null; -``` - -Defined in: [controllers/hotkey-sequence-recorder.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence-recorder.ts#L69) - -Last committed sequence, or null if none. - -##### Returns - -`HotkeySequence` \| `null` - -*** - -### steps - -#### Get Signature - -```ts -get steps(): HotkeySequence; -``` - -Defined in: [controllers/hotkey-sequence-recorder.ts:64](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence-recorder.ts#L64) - -Chords captured in the current session. - -##### Returns - -`HotkeySequence` - -## Methods - -### cancelRecording() - -```ts -cancelRecording(): void; -``` - -Defined in: [controllers/hotkey-sequence-recorder.ts:139](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence-recorder.ts#L139) - -Cancel recording without saving. - -#### Returns - -`void` - -*** - -### commitRecording() - -```ts -commitRecording(): void; -``` - -Defined in: [controllers/hotkey-sequence-recorder.ts:144](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence-recorder.ts#L144) - -Commit current steps as a sequence (no-op if empty). - -#### Returns - -`void` - -*** - -### hostConnected() - -```ts -hostConnected(): void; -``` - -Defined in: [controllers/hotkey-sequence-recorder.ts:92](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence-recorder.ts#L92) - -Subscribes to the recorder store and updates internal state when changes occur. - -#### Returns - -`void` - -#### Implementation of - -```ts -ReactiveController.hostConnected -``` - -*** - -### hostDisconnected() - -```ts -hostDisconnected(): void; -``` - -Defined in: [controllers/hotkey-sequence-recorder.ts:114](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence-recorder.ts#L114) - -Unsubscribes from the recorder store and destroys the recorder instance to prevent memory leaks. - -#### Returns - -`void` - -#### Implementation of - -```ts -ReactiveController.hostDisconnected -``` - -*** - -### setOptions() - -```ts -setOptions(options): void; -``` - -Defined in: [controllers/hotkey-sequence-recorder.ts:124](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence-recorder.ts#L124) - -Updates the recorder options. - -#### Parameters - -##### options - -`Partial`\<`HotkeySequenceRecorderOptions`\> - -#### Returns - -`void` - -*** - -### startRecording() - -```ts -startRecording(): void; -``` - -Defined in: [controllers/hotkey-sequence-recorder.ts:129](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence-recorder.ts#L129) - -Start recording a new sequence. - -#### Returns - -`void` - -*** - -### stopRecording() - -```ts -stopRecording(): void; -``` - -Defined in: [controllers/hotkey-sequence-recorder.ts:134](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey-sequence-recorder.ts#L134) - -Stop recording (same as cancel but without calling onCancel). - -#### Returns - -`void` diff --git a/docs/framework/lit/reference/classes/KeyHoldController.md b/docs/framework/lit/reference/classes/KeyHoldController.md index 27ffe6d5..b6d4dfc5 100644 --- a/docs/framework/lit/reference/classes/KeyHoldController.md +++ b/docs/framework/lit/reference/classes/KeyHoldController.md @@ -62,10 +62,14 @@ Defined in: [controllers/key-hold.ts:58](https://github.com/TanStack/hotkeys/blo `ReactiveControllerHost` +The Lit component that owns this controller. + ##### \_key `IndividualKey` +The key to track (e.g. 'Shift', 'Control', 'A'). + #### Returns `KeyHoldController` diff --git a/docs/framework/lit/reference/functions/hotkey.md b/docs/framework/lit/reference/functions/hotkey.md index 854eee53..25b167c0 100644 --- a/docs/framework/lit/reference/functions/hotkey.md +++ b/docs/framework/lit/reference/functions/hotkey.md @@ -3,43 +3,102 @@ id: hotkey title: hotkey --- +## Call Signature + ```ts -function hotkey(hotkey, options?): (proto, propertyKey, descriptor?) => void; +function hotkey(hotkey, options?): HotkeyDecorator; ``` -Defined in: [decorators/hotkey.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/decorators/hotkey.ts#L29) +Defined in: [decorators/hotkey.ts:40](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/decorators/hotkey.ts#L40) + +Decorator that registers a hotkey on the element when it connects and +unregisters when it disconnects: a chord such as `'Mod+S'`, a sequence such +as `['G', 'G']`, or an app hotkey. Uses [HotkeyController](../classes/HotkeyController.md) under the hood. + +### Type Parameters -Decorator that registers a keyboard hotkey on the element when it connects -and unregisters when it disconnects. Uses [HotkeyController](../classes/HotkeyController.md) under the hood. +#### T -## Parameters +`T` *extends* `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\> -### hotkey +### Parameters -`RegisterableHotkey` +#### hotkey -The key or key combo to listen for (e.g. `'Mod+S'` or a raw hotkey object). +`T` -### options? +The chord, sequence, or app hotkey to listen for. -`HotkeyOptions` = `HOTKEY_DEFAULT_OPTIONS` +#### options? + +[`AppHotkeyControllerOptions`](../type-aliases/AppHotkeyControllerOptions.md)\<`T`\> Optional registration options (target, platform, enabled, etc.). -## Returns +### Returns + +`HotkeyDecorator` A method decorator for use on LitElement methods. -\<`T`\>(`proto`, `propertyKey`, `descriptor?`) => `void` +### Example + +```ts +class MyElement extends LitElement { + @hotkey('Mod+S') + save() { this.doSave() } + + @hotkey(['G', 'G'], { timeout: 500 }) + goToTop() { window.scrollTo(0, 0) } +} +``` + +## Call Signature + +```ts +function hotkey(hotkey, options?): HotkeyDecorator; +``` + +Defined in: [decorators/hotkey.ts:44](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/decorators/hotkey.ts#L44) + +Decorator that registers a hotkey on the element when it connects and +unregisters when it disconnects: a chord such as `'Mod+S'`, a sequence such +as `['G', 'G']`, or an app hotkey. Uses [HotkeyController](../classes/HotkeyController.md) under the hood. + +### Type Parameters + +#### T + +`T` *extends* `RegisterableHotkey` + +### Parameters + +#### hotkey + +`T` + +The chord, sequence, or app hotkey to listen for. + +#### options? + +[`HotkeyControllerOptionsFor`](../type-aliases/HotkeyControllerOptionsFor.md)\<`T`\> + +Optional registration options (target, platform, enabled, etc.). + +### Returns + +`HotkeyDecorator` + +A method decorator for use on LitElement methods. -## Example +### Example ```ts class MyElement extends LitElement { @hotkey('Mod+S') save() { this.doSave() } - @hotkey('Escape') - close() { this.dismiss() } + @hotkey(['G', 'G'], { timeout: 500 }) + goToTop() { window.scrollTo(0, 0) } } ``` diff --git a/docs/framework/lit/reference/functions/hotkeySequence.md b/docs/framework/lit/reference/functions/hotkeySequence.md deleted file mode 100644 index e6dbb61e..00000000 --- a/docs/framework/lit/reference/functions/hotkeySequence.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -id: hotkeySequence -title: hotkeySequence ---- - -```ts -function hotkeySequence(sequence, options?): (proto, methodName, descriptor) => void; -``` - -Defined in: [decorators/hotkey-sequence.ts:30](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/decorators/hotkey-sequence.ts#L30) - -Decorator that registers a keyboard sequence (e.g. Vim-style) on the element -when it connects and unregisters when it disconnects. Uses -[HotkeySequenceController](../classes/HotkeySequenceController.md) under the hood. - -## Parameters - -### sequence - -`HotkeySequence` - -The key sequence to listen for (e.g. `['G', 'G']` for "g g"). - -### options? - -`SequenceOptions` = `HOTKEY_SEQUENCE_DEFAULT_OPTIONS` - -Optional sequence options (target, timeout, enabled, etc.). - -## Returns - -A method decorator for use on LitElement methods. - -\<`T`\>(`proto`, `methodName`, `descriptor`) => `void` - -## Example - -```ts -class MyElement extends LitElement { - @hotkeySequence(['G', 'G']) - goToTop() { window.scrollTo(0, 0) } - - @hotkeySequence(['D', 'D'], { timeout: 500 }) - deleteLine() { this.deleteCurrentLine() } -} -``` diff --git a/docs/framework/lit/reference/index.md b/docs/framework/lit/reference/index.md index 6e905069..95345ee6 100644 --- a/docs/framework/lit/reference/index.md +++ b/docs/framework/lit/reference/index.md @@ -5,17 +5,25 @@ title: "@tanstack/lit-hotkeys" ## Classes +- [AppHotkeysController](classes/AppHotkeysController.md) - [HeldKeyCodesController](classes/HeldKeyCodesController.md) - [HeldKeysController](classes/HeldKeysController.md) - [HotkeyController](classes/HotkeyController.md) - [HotkeyHintController](classes/HotkeyHintController.md) - [HotkeyRecorderController](classes/HotkeyRecorderController.md) - [HotkeyRegistrationsController](classes/HotkeyRegistrationsController.md) -- [HotkeySequenceController](classes/HotkeySequenceController.md) -- [HotkeySequenceRecorderController](classes/HotkeySequenceRecorderController.md) - [KeyHoldController](classes/KeyHoldController.md) +## Type Aliases + +- [AppHotkeyControllerOptions](type-aliases/AppHotkeyControllerOptions.md) +- [HotkeyControllerChordOptions](type-aliases/HotkeyControllerChordOptions.md) +- [HotkeyControllerCommonOptions](type-aliases/HotkeyControllerCommonOptions.md) +- [HotkeyControllerOptions](type-aliases/HotkeyControllerOptions.md) +- [HotkeyControllerOptionsFor](type-aliases/HotkeyControllerOptionsFor.md) +- [HotkeyControllerSequenceOptions](type-aliases/HotkeyControllerSequenceOptions.md) +- [HotkeyControllerTarget](type-aliases/HotkeyControllerTarget.md) + ## Functions - [hotkey](functions/hotkey.md) -- [hotkeySequence](functions/hotkeySequence.md) diff --git a/docs/framework/lit/reference/type-aliases/AppHotkeyControllerOptions.md b/docs/framework/lit/reference/type-aliases/AppHotkeyControllerOptions.md new file mode 100644 index 00000000..a2d6cd79 --- /dev/null +++ b/docs/framework/lit/reference/type-aliases/AppHotkeyControllerOptions.md @@ -0,0 +1,18 @@ +--- +id: AppHotkeyControllerOptions +title: AppHotkeyControllerOptions +--- + +```ts +type AppHotkeyControllerOptions = AdapterAppHotkeyOptions; +``` + +Defined in: [controllers/hotkey.ts:46](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L46) + +Call-site options for an app hotkey, typed by its declared default kind. + +## Type Parameters + +### T + +`T` *extends* `AppHotkey` = `AppHotkey` diff --git a/docs/framework/lit/reference/type-aliases/HotkeyControllerChordOptions.md b/docs/framework/lit/reference/type-aliases/HotkeyControllerChordOptions.md new file mode 100644 index 00000000..d0c39b07 --- /dev/null +++ b/docs/framework/lit/reference/type-aliases/HotkeyControllerChordOptions.md @@ -0,0 +1,12 @@ +--- +id: HotkeyControllerChordOptions +title: HotkeyControllerChordOptions +--- + +```ts +type HotkeyControllerChordOptions = AdapterChordOptions; +``` + +Defined in: [controllers/hotkey.ts:30](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L30) + +Options for a chord such as `'Mod+S'`. diff --git a/docs/framework/lit/reference/type-aliases/HotkeyControllerCommonOptions.md b/docs/framework/lit/reference/type-aliases/HotkeyControllerCommonOptions.md new file mode 100644 index 00000000..ea3a32ca --- /dev/null +++ b/docs/framework/lit/reference/type-aliases/HotkeyControllerCommonOptions.md @@ -0,0 +1,12 @@ +--- +id: HotkeyControllerCommonOptions +title: HotkeyControllerCommonOptions +--- + +```ts +type HotkeyControllerCommonOptions = AdapterCommonOptions; +``` + +Defined in: [controllers/hotkey.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L38) + +Options for every kind; each applies to the kinds that support it. diff --git a/docs/framework/lit/reference/type-aliases/HotkeyControllerOptions.md b/docs/framework/lit/reference/type-aliases/HotkeyControllerOptions.md new file mode 100644 index 00000000..87bfc781 --- /dev/null +++ b/docs/framework/lit/reference/type-aliases/HotkeyControllerOptions.md @@ -0,0 +1,12 @@ +--- +id: HotkeyControllerOptions +title: HotkeyControllerOptions +--- + +```ts +type HotkeyControllerOptions = AdapterHotkeyOptions; +``` + +Defined in: [controllers/hotkey.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L26) + +Options shared by chords and sequences. diff --git a/docs/framework/lit/reference/type-aliases/HotkeyControllerOptionsFor.md b/docs/framework/lit/reference/type-aliases/HotkeyControllerOptionsFor.md new file mode 100644 index 00000000..2a69ed26 --- /dev/null +++ b/docs/framework/lit/reference/type-aliases/HotkeyControllerOptionsFor.md @@ -0,0 +1,18 @@ +--- +id: HotkeyControllerOptionsFor +title: HotkeyControllerOptionsFor +--- + +```ts +type HotkeyControllerOptionsFor = AdapterOptionsFor; +``` + +Defined in: [controllers/hotkey.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L42) + +The options type for a hotkey: chord options for chords, sequence options for sequences. + +## Type Parameters + +### T + +`T` *extends* `RegisterableHotkey` diff --git a/docs/framework/lit/reference/type-aliases/HotkeyControllerSequenceOptions.md b/docs/framework/lit/reference/type-aliases/HotkeyControllerSequenceOptions.md new file mode 100644 index 00000000..1550cdac --- /dev/null +++ b/docs/framework/lit/reference/type-aliases/HotkeyControllerSequenceOptions.md @@ -0,0 +1,12 @@ +--- +id: HotkeyControllerSequenceOptions +title: HotkeyControllerSequenceOptions +--- + +```ts +type HotkeyControllerSequenceOptions = AdapterSequenceOptions; +``` + +Defined in: [controllers/hotkey.ts:34](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L34) + +Options for a sequence such as `['G', 'G']`. diff --git a/docs/framework/lit/reference/type-aliases/HotkeyControllerTarget.md b/docs/framework/lit/reference/type-aliases/HotkeyControllerTarget.md new file mode 100644 index 00000000..1034bd77 --- /dev/null +++ b/docs/framework/lit/reference/type-aliases/HotkeyControllerTarget.md @@ -0,0 +1,12 @@ +--- +id: HotkeyControllerTarget +title: HotkeyControllerTarget +--- + +```ts +type HotkeyControllerTarget = Target | null; +``` + +Defined in: [controllers/hotkey.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/lit-hotkeys/src/controllers/hotkey.ts#L23) + +The element to listen on. A `null` target skips registration until it is set. diff --git a/docs/framework/octane/guides/app-hotkeys.md b/docs/framework/octane/guides/app-hotkeys.md new file mode 100644 index 00000000..52577a00 --- /dev/null +++ b/docs/framework/octane/guides/app-hotkeys.md @@ -0,0 +1,190 @@ +--- +title: App Hotkeys Guide +id: app-hotkeys +--- + +`useHotkey` and `useHotkeys` register a hotkey while its component is mounted. A cheat sheet built from live registrations therefore misses hotkeys on routes that have not rendered yet, and a settings screen has nowhere to change them. App hotkeys solve both: you declare every hotkey in one place, pass the declarations to `HotkeysProvider`, and attach callbacks where the work happens. Users can then customize any of them, and your app decides how to save their choices. + +A declaration holds a hotkey's id, default hotkey, metadata, and options. It never holds a callback; `useHotkey` and `useHotkeys` still attach the callbacks. + +## Declare app hotkeys + +Use `defineAppHotkeys` in a module that contains only declarations. Keep it free of components and handlers, because it loads with your app shell: + +```ts +// app-hotkeys.ts +import { defineAppHotkeys } from '@tanstack/octane-hotkeys' + +export const appHotkeys = defineAppHotkeys({ + 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save', group: 'Document' } }, + 'doc.find': { + hotkey: 'Mod+F', + meta: { name: 'Find', group: 'Document' }, + context: 'editor', + }, + 'nav.home': { + hotkey: ['G', 'H'], + meta: { name: 'Go home', group: 'Navigation' }, + options: { timeout: 800 }, + }, + 'palette.open': { hotkey: null, meta: { name: 'Command palette' } }, +}) + +declare module '@tanstack/octane-hotkeys' { + interface Register { + appHotkeys: typeof appHotkeys + } +} +``` + +- The object key is the app hotkey's id. Treat ids as a stable contract, because saved custom hotkeys use them as keys. +- `hotkey` is the default: a chord, a sequence, or `null` for an action without a default. An unbound app hotkey registers nothing until the user assigns one. +- `options` describe the hotkey itself: `conflictBehavior`, `eventType`, `ignoreInputs`, `preventDefault`, `stopPropagation`, plus `requireReset` for a chord default or `timeout` for a sequence default. Options that don't match the default's kind are type errors. Pass `target` and `enabled` where you register the hotkey. +- `context` labels where a hotkey applies, such as `'editor'`. App hotkeys with different contexts may share a hotkey. It is used only for conflict checks and does not affect matching. + +The `Register` augmentation types the app hotkeys you read back, the ids accepted when changing custom hotkeys, and the `CustomHotkeys` type. + +## Provide app hotkeys + +```tsx +import { HotkeysProvider } from '@tanstack/octane-hotkeys' +import { appHotkeys } from './app-hotkeys' + +function Root() @{ + + + +} +``` + +## Register app hotkeys + +Pass an app hotkey wherever you pass a hotkey: + +```tsx +import { useAppHotkeys, useHotkey, useHotkeys } from '@tanstack/octane-hotkeys' + +function Editor() @{ + const hotkeys = useAppHotkeys() + + useHotkey(hotkeys['doc.save'], () => save(), { enabled: isDirty }) + useHotkeys([ + { hotkey: hotkeys['nav.home'], callback: () => navigate('/') }, + { hotkey: 'Escape', callback: close }, + ]) +
    ...
    +} +``` + +You can also import an app hotkey directly, such as `useHotkey(appHotkeys['doc.save'], save)`. Without provided app hotkeys, the declared default is used. + +The registration takes `meta` and `options` from the declaration. Options merge in this order: default options, then declared options, then call-site options. `meta` comes only from the declaration, so the cheat sheet and the registration cannot disagree. Call-site options are typed by the declared default's kind, so `timeout` is accepted for `nav.home` but not for `doc.save`. + +When the user changes a hotkey, registered app hotkeys move to the new hotkey immediately, even if it changes from a chord to a sequence. + +## Build a cheat sheet + +`useAppHotkeyList` returns every app hotkey in declaration order, with its live status: + +| Status | Meaning | +| --- | --- | +| `'active'` | At least one enabled registration | +| `'disabled'` | Registered, but every registration has `enabled: false` | +| `'inactive'` | Has a hotkey, but nothing has registered it | +| `'unbound'` | No hotkey assigned | + +```tsx +import { formatForDisplay, useAppHotkeyList } from '@tanstack/octane-hotkeys' + +function CheatSheet() @{ + const items = useAppHotkeyList() +
      + @for (const item of items; key item.appHotkey.id) { +
    • + {item.appHotkey.meta?.name}:{' '} + {item.appHotkey.hotkey ? formatForDisplay(item.appHotkey.hotkey) : 'Unassigned'} + {item.isCustom ? ' (custom)' : ''} +
    • + } +
    +} +``` + +An app hotkey registered by many components, such as one per list row, appears once. Its `registrations` array holds each live registration. Devtools also list declared app hotkeys that are not registered. + +## Customize and persist hotkeys + +TanStack Hotkeys does not store anything. Pass the user's saved custom hotkeys in and save them when they change: + +```tsx +import { HotkeysProvider } from '@tanstack/octane-hotkeys' +import type { CustomHotkeys } from '@tanstack/octane-hotkeys' + +const saved: CustomHotkeys = JSON.parse(localStorage.getItem('hotkeys') ?? '{}') + +function Root() @{ + + localStorage.setItem('hotkeys', JSON.stringify(custom)) + } + > + + +} +``` + +- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them. +- `customHotkeys` is read once; afterwards the shared store owns the state. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning. +- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile. + +Recorded hotkeys use `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others. + +### Change hotkeys + +`useCustomHotkeys` reads and changes the user's custom hotkeys: + +```tsx +import { + formatForDisplay, + useAppHotkeys, + useCustomHotkeys, + useHotkeyRecorder, +} from '@tanstack/octane-hotkeys' +import type { AppHotkeyId } from '@tanstack/octane-hotkeys' + +function ShortcutRow({ id }: { id: AppHotkeyId }) @{ + const appHotkey = useAppHotkeys()[id] + const { setCustomHotkey, resetCustomHotkey, findConflicts } = useCustomHotkeys() + + const recorder = useHotkeyRecorder({ + maxSteps: 3, + validate: (hotkey) => { + const conflicts = findConflicts(id, hotkey) + return conflicts.length === 0 || `Used by ${conflicts[0]?.meta?.name}` + }, + onRecord: (hotkey) => setCustomHotkey(id, hotkey), + onClear: () => setCustomHotkey(id, null), + }) + +
    + {appHotkey.meta?.name}:{' '} + {appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned'} + + +
    +} +``` + +| Member | Description | +| --- | --- | +| `customHotkeys` | The hotkeys that differ from the defaults | +| `setCustomHotkey(id, hotkey)` | Sets a hotkey, or unbinds with `null`. Returns conflicting app hotkeys; when there are any, nothing changes. | +| `findConflicts(id, hotkey)` | App hotkeys that would collide if `id` used `hotkey` | +| `resetCustomHotkey(id)` | Restores one default | +| `resetCustomHotkeys()` | Restores every default | + +Two app hotkeys conflict when their contexts match or either has no `context`, and their hotkeys collide: equal chords, or one sequence starts with the other, including a sequence whose first step equals a chord. Conflicts are checked across every declaration, whether or not it is registered. + +Each app hotkey's `defaultHotkey` holds its declared default, and each cheat sheet item's `isCustom` reports whether the effective hotkey differs from it. diff --git a/docs/framework/octane/guides/formatting-display.md b/docs/framework/octane/guides/formatting-display.md index 4b8b8d3c..61861e66 100644 --- a/docs/framework/octane/guides/formatting-display.md +++ b/docs/framework/octane/guides/formatting-display.md @@ -3,7 +3,7 @@ title: Formatting & Display Guide id: formatting-display --- -Use `formatForDisplay` whenever a binding appears in a menu, button, hint, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, and parsed bindings. Store the original binding and format it at render time: the display label is not a registration string. +Use `formatForDisplay` whenever a hotkey appears in a menu, button, hint, cheat sheet, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, parsed bindings, and sequences. Store the original binding and format it at render time: the display label is not a registration string. ## Format a binding @@ -27,21 +27,23 @@ Set `parts: true` to get one label per key instead of a joined string: ```ts const binding = 'Mod+[KeyS]' const parts = formatForDisplay(binding, { platform: 'mac', parts: true }) -// ['⌘', 'S']; render each part in its own +// ['⌘', 'S'] — render each part in its own ``` With `parts` omitted or false, the result is a string. A runtime boolean returns `string | string[]`. Parts preserve literal plus keys and ignore `separatorToken`. -Sequences are arrays of bindings. Format their steps individually: +## Format sequences -```ts -import type { HotkeySequence } from '@tanstack/octane-hotkeys' +Pass a sequence to format each step and join the steps with `sequenceSeparator`, a space by default: -const sequence: HotkeySequence = ['Mod+[KeyK]', 'C'] -const label = sequence.map((step) => formatForDisplay(step)).join(' → ') +```ts +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac' }) // '⌘ K C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' → ' }) // 'G → G' +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac', parts: true }) +// [['⌘', 'K'], ['C']] — one array of keycaps per step ``` -`formatHotkeySequence` only joins stored strings with spaces. It intentionally retains brackets and code names, so use the code above for user-facing labels. +With `parts: true`, a sequence returns one array per step, so you can render each step as a group of keycaps. ## Choose symbols and separators @@ -49,21 +51,21 @@ const label = sequence.map((step) => formatForDisplay(step)).join(' → ') ```ts formatForDisplay('Shift+[ArrowUp]', { - platform: 'mac', - useSymbols: { modifiers: false, keys: true }, - parts: true, + platform: 'mac', + useSymbols: { modifiers: false, keys: true }, + parts: true, }) // ['Shift', '↑'] formatForDisplay('Mod+[KeyS]', { - platform: 'mac', useSymbols: false, + platform: 'mac', useSymbols: false, }) // 'Cmd+S' formatForDisplay('Control++', { - platform: 'windows', separatorToken: ' · ', + platform: 'windows', separatorToken: ' · ', }) // 'Ctrl · +' ``` -An empty separator joins labels directly. `undefined` or `null` uses the platform default. `formatWithLabels(binding, options)` is the shorthand for `formatForDisplay` with `useSymbols: false`. +An empty separator joins labels directly. `undefined` or `null` uses the platform default. `separatorToken` applies within a chord; `sequenceSeparator` applies between steps. ## Supply layout labels @@ -82,12 +84,16 @@ Use `keyLabels` for explicit labels keyed by physical code or normalized logical ```ts formatForDisplay('Mod+[KeyQ]', { - platform: 'mac', layoutMap, keyLabels: { KeyQ: 'Action' }, + platform: 'mac', layoutMap, keyLabels: { KeyQ: 'Action' }, }) // '⌘ Action' ``` The precedence is `keyLabels`, then a layout entry, then the fallback label. Layout entries receive normal letter casing and key symbols; missing or empty entries fall back. Explicit labels are final. All of these options affect display only. +## Compare hotkeys + +`areHotkeysEqual(a, b)` compares chords or sequences after resolving modifiers, so `Mod+S` equals `Meta+S` on macOS. Sequences compare step by step. `isHotkeySequence` narrows a `Hotkey` to a sequence, and `simplifyHotkey` turns a one-step sequence into its chord. + ## Parse and store bindings `parseHotkey` returns either a logical `key` or a physical `code`, plus resolved modifier flags. Narrow the union before inspecting the identity: @@ -97,7 +103,7 @@ import { parseHotkey, normalizeHotkeyFromParsed } from '@tanstack/octane-hotkeys const parsed = parseHotkey('Mod+[KeyS]', 'mac') if (parsed.code !== undefined) { - console.log(parsed.code) // 'KeyS'; parsed.key is undefined + console.log(parsed.code) // 'KeyS'; parsed.key is undefined } const stored = normalizeHotkeyFromParsed(parsed, 'mac') // 'Mod+[KeyS]' formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S' @@ -105,6 +111,6 @@ formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S' Parsed modifiers are already resolved. To display a portable `Mod` binding on another platform, serialize with the original platform first, as above. `normalizeRegisterableHotkey` accepts strings or raw objects and preserves the logical/physical distinction. Do not store display labels or put a bracketed code in a logical `key` field. -Use `validateHotkey` when accepting strings from an external source. It returns `valid`, `errors`, and `warnings`; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). +Use `validateHotkey` when accepting a chord or sequence from an external source. It returns `valid`, `errors`, and `warnings`, and names the failing step of a sequence; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). Try these options together in the [vanilla formatter playground](../../../framework/vanilla/examples/formatForDisplay). diff --git a/docs/framework/octane/guides/hotkey-recording.md b/docs/framework/octane/guides/hotkey-recording.md index 4c779891..37c5cdcf 100644 --- a/docs/framework/octane/guides/hotkey-recording.md +++ b/docs/framework/octane/guides/hotkey-recording.md @@ -3,145 +3,199 @@ title: Hotkey Recording Guide id: hotkey-recording --- -Use `useHotkeyRecorder` to build a shortcut customization UI. Recording defaults to physical codes, producing values such as `Mod+[KeyS]`. Store that value directly and pass it to `useHotkey`. Use `formatForDisplay` for the label. +`useHotkeyRecorder` is for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the key combination they want, the same way system preferences or IDE shortcut editors work. By default it records one chord; set `maxSteps` to let users record a [sequence](./sequences) too. -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. +Recorders default to physical codes: recording a shortcut stores a string such as `Mod+[KeyS]`. Pass it directly to your hotkey registration and use `formatForDisplay` for the label. Set `recordBy: 'key'` when you intentionally want the produced character instead. -## Basic usage +TanStack Hotkeys automatically suppresses registered hotkey callbacks while any recorder is active. You do not need to disable hotkeys while recording. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. +## Basic usage ```tsx -import { useState } from 'octane' -import { useHotkey, useHotkeyRecorder, formatForDisplay } from '@tanstack/octane-hotkeys' -import type { Hotkey } from '@tanstack/octane-hotkeys' - -export function ShortcutSettings() @{ - const [binding, setBinding] = useState('Mod+S') - const recorder = useHotkeyRecorder({ - onRecord: setBinding, - onClear: () => setBinding('Mod+S'), - }) - useHotkey(binding, () => console.log('Saved')) - -
    - {formatForDisplay(binding)} - - @if (recorder.isRecording) { -
    -

    Press a shortcut. Escape cancels; Backspace resets the binding.

    - -
    - } -
    +import { useHotkeyRecorder, formatForDisplay } from '@tanstack/octane-hotkeys' + +function ShortcutRecorder() @{ + const recorder = useHotkeyRecorder({ + onRecord: (hotkey) => console.log('Recorded:', hotkey), // e.g. 'Mod+Shift+[KeyS]' + }) + + } ``` -This example stores the replacement binding in application state and restores `Mod+S` when the user clears it. Cancellation leaves the saved binding unchanged. - -## Return value +## Recorder state and actions -| Property | Type | Meaning | -| --- | --- | --- | -| `isRecording` | `boolean` | Whether a session is active. | -| `recordedHotkey` | `Hotkey \| null` | The recorded binding, or `null` after starting, stopping, or cancelling. | -| `startRecording` | `() => void` | Start a new session. | -| `stopRecording` | `() => void` | Stop and clear recorder state without calling `onRecord` or `onCancel`. | -| `cancelRecording` | `() => void` | Stop, clear recorder state, and call `onCancel`. | +| Member | Description | +| --- | --- | +| `isRecording` | Whether the recorder is currently listening for key presses | +| `steps` | Chords captured so far in a multi-step recording | +| `recordedHotkey` | The last recorded hotkey, or `null` if nothing recorded | +| `startRecording()` | Start listening for key presses | +| `stopRecording()` | Stop listening without calling `onRecord` or `onCancel` | +| `cancelRecording()` | Stop listening and call `onCancel` | +| `commitRecording()` | Commit the current `steps` now (multi-step recordings) | -Read the returned state on each component render. +The returned object is a snapshot for the current render. ## Options ### `recordBy` -The default is `'code'`. On macOS, Option+S producing `ß` records `Alt+[KeyS]`, and Option+2 producing `™` records `Alt+[Digit2]`. Stored brackets preserve physical identity through serialization and registration. Existing authored strings such as `Mod+S` remain logical. +Recorders default to `recordBy: 'code'`. Option+S producing `ß` on macOS records `Alt+[KeyS]`; Option+2 producing `™` records `Alt+[Digit2]`. The stored brackets preserve physical identity through state, serialization, and registration; `formatForDisplay` produces a readable label. Existing authored strings such as `Mod+S` remain logical hotkeys. -Set `recordBy: 'key'` to record the produced character. Code mode rejects an event without a usable code and never falls back to key mode. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the character without synthetic Control/Alt while retaining Shift. +Set `recordBy: 'key'` to intentionally record the produced logical character. A code-mode event with no usable code is rejected rather than silently switching modes. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the produced character without synthetic Control/Alt while retaining Shift. ### `onRecord` -Receives the recorded `Hotkey` when the user enters a valid chord. A chord can be a single non-modifier key or a key with modifiers. Update your saved binding here. +Called when a recording commits. With the default `maxSteps: 1`, it receives a chord string such as `'Mod+[KeyS]'` as soon as the user presses a valid combination (a modifier + a non-modifier key, or a single non-modifier key). With a larger `maxSteps`, it receives a chord when one step was recorded, or a sequence otherwise. ### `onCancel` -Runs when Escape cancels a session or when you call `cancelRecording()`. Use it to exit an editing state without changing the saved binding. +Called when recording is cancelled, by pressing Escape or calling `cancelRecording()`. ### `onClear` -Runs when the user presses unmodified Backspace or Delete during recording. Clearing calls only `onClear`; it does not call `onRecord`. Your application decides whether to remove the binding or restore an initial value. +Called when the recording is cleared: Backspace or Delete with no modifiers and no recorded steps. Clearing calls only `onClear`; your app decides whether that removes a hotkey or restores a default. -### Shared defaults and changing options +### `ignoreInputs` -Set `hotkeyRecorder` defaults in `HotkeysProvider`. Options passed to an individual hook override provider defaults. For a plural hook, each definition's options override its common options. See the [provider setup](../quick-start.md#default-options-provider). +Defaults to `true`. The recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Escape still cancels recording even while an input is focused. Set `ignoreInputs: false` to record from inside input elements. -Options and callbacks refresh after every commit, so callbacks see current component state. +Options are read on every render, so recording policy can follow component state. -## Recording behavior +### Default options -| Input | Behavior | -| --- | --- | -| Modifier alone | Wait for a non-modifier key. | -| Modifier plus a non-modifier key | Record the chord and finish. | -| Single non-modifier key, such as F1 | Record the key and finish. | -| Escape | Cancel. | -| Unmodified Backspace or Delete | Clear and call `onClear`. | -| Automatic key repeat or IME composition | Do not record a new chord. | +Set defaults for every recorder with `HotkeysProvider`. Per-recorder options override them. -Recording events, repeats, and their key releases do not trigger registered hotkeys or sequences. +```tsx + + + +``` -### `ignoreInputs` +## Recording sequences + +Set `maxSteps` above 1 to record up to that many chords. Each chord adds a step, and the recording commits when the user presses Enter, when it reaches `maxSteps`, or after `idleTimeoutMs` without a new step: + +```tsx +const recorder = useHotkeyRecorder({ + maxSteps: 3, + onRecord: (hotkey) => console.log('Recorded:', hotkey), // 'Mod+[KeyS]' or ['[KeyG]', '[KeyG]'] +}) + +// Live preview while recording +const label = recorder.isRecording + ? formatForDisplay(recorder.steps) || 'Press chords, then Enter…' + : formatForDisplay(recorder.recordedHotkey ?? []) +``` + +A one-step recording commits as a chord, so the result can always be passed straight to your hotkey registration. Use `maxSteps: Infinity` for no limit. + +| Option | Description | +| --- | --- | +| `maxSteps` | Maximum chords to record. Reaching it commits. Default `1`. | +| `commitKeys` | `'enter'` (default) commits on plain Enter. With `'none'`, plain Enter can be recorded as a step, and only `commitRecording()`, `idleTimeoutMs`, or `maxSteps` commit. | +| `idleTimeoutMs` | Commit automatically after this many milliseconds without a new step. The timer does not run before the first chord. | + +`commitKeys` and `idleTimeoutMs` are only accepted when `maxSteps` is set. With `maxSteps` above 1, `onRecord` and `validate` receive `Hotkey` (a chord or a sequence) instead of a chord. -This defaults to `true`. Normal typing in inputs, textareas, selects, and contentEditable elements passes through. Escape still cancels while an input is focused. Set `ignoreInputs: false` to capture shortcuts from a focused input. +## Recording behavior -### Mod auto-conversion +| Key | Behavior | +| --- | --- | +| Modifier only (Shift, Ctrl, etc.) | Waits for a non-modifier key; modifier-only presses don't complete a step | +| Modifier + key (e.g., Ctrl+S) | Records the full combination | +| Single key (e.g., Escape, F1) | Records the single key | +| Enter (multi-step, `commitKeys: 'enter'`) | Commits the recorded steps | +| Escape | Cancels the recording | +| Backspace / Delete | Removes the last step; with no steps, clears and calls `onClear` | -On macOS, Command+S becomes `Mod+[KeyS]`. Reusing that binding on Windows resolves `Mod` to Control while preserving the physical key position. Pass a `platform` option when detection must be overridden. +Repeats and IME composition never add a step. Recorded hotkeys use the portable `Mod` format: Command+S on macOS records `Mod+[KeyS]` rather than `Meta+[KeyS]`, so the primary modifier becomes Control if the hotkey is later used on Windows. ## Validation and conflicts -Supply these options alongside `onRecord`: +The recorder accepts `detectConflicts`, `validate`, and `onReject` alongside `onRecord`: ```ts -import type { HotkeyRecorderOptions } from '@tanstack/octane-hotkeys' - -const options: HotkeyRecorderOptions = { - onRecord: (hotkey) => console.log('Accepted', hotkey), - detectConflicts: { - // Replace this with the ID from the live registration being edited. - excludeIds: ['registration-being-edited'], - target: document, - eventType: 'keydown', - }, - validate: (_hotkey, { parsedHotkey }) => - parsedHotkey.modifiers.length > 0 || 'Include a modifier.', - onReject: ({ reason, message, conflicts }) => { - console.log(reason, message, conflicts) - }, +const options = { + detectConflicts: { + // Use registration IDs to exclude the hotkey being edited: + excludeIds: [registrationId], + target: document, + eventType: 'keydown', + }, + validate: (_hotkey, { parsedHotkey }) => + parsedHotkey.modifiers.length > 0 || 'Include a modifier.', + onReject: ({ reason, message, conflicts }) => { + // Show feedback; recording stays active. + console.log(reason, message, conflicts) + }, } ``` -`validate` returns `true` to accept, or `false` or a message to reject. Validation runs before commit. A rejected candidate leaves recording active. `onReject` reports `missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`, plus the candidate when available and conflicting views for a conflict. +`validate(hotkey, context)` returns true to accept, or false/a message to reject. The context holds the last chord's `event` and `parsedHotkey`, plus `events` and `parsedSequence` for every step. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the rejected `hotkey` when available, and conflicting registrations for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. A rejected multi-step recording keeps its steps so the user can edit them with Backspace. -`detectConflicts: true` checks enabled live registrations with the intended event type, defaulting to keydown, and overlapping targets, defaulting to document. Document and nested element scopes can conflict; disjoint widgets can reuse a binding. The options object supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. +`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a hotkey. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Chords and sequence prefixes are checked: a recorded chord conflicts with a sequence that starts with it, and a recorded sequence conflicts with a registered chord equal to its first step. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute. -Checks include single bindings and sequence prefixes. Source events detect physical/logical overlap on the recorded layout. This is conservative collision detection: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can change dispatch. - -For checks outside recording, call `findHotkeyConflicts(bindingOrSequence, options)`. Without source `events`, it compares identities and prefixes rather than guessing which logical character a physical position produces. +For checks outside recording, use `findHotkeyConflicts(hotkey, options)`. Without source `events`, it compares hotkey identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. ## Building a shortcut settings UI -For several actions, keep the bindings in an array or object and record the ID of the action being edited. On `onRecord`, replace that action's binding and clear the editing ID. On `onCancel`, clear only the editing ID. Use the plural registration API for the current list. +For an app-wide settings screen, declare the hotkeys with [app hotkeys](./app-hotkeys) and save the recorder's result with `setCustomHotkey`. That keeps cheat sheets, conflicts, and persistence in one place. + +For a single component, ordinary state is enough: ```tsx -useHotkeys(shortcuts.map((shortcut) => ({ - hotkey: shortcut.hotkey, - callback: () => runAction(shortcut.id), - options: { meta: { name: shortcut.name } }, -}))) -``` +import { useState } from 'octane' +import { useHotkeys, useHotkeyRecorder, formatForDisplay } from '@tanstack/octane-hotkeys' +import type { Hotkey } from '@tanstack/octane-hotkeys' -The [useHotkeyRecorder example](../examples/useHotkeyRecorder) includes multiple actions, editable names and descriptions, create/delete controls, reset and clear behavior, cancellation, and a live registry. The [kitchen sink](../examples/kitchen-sink) also demonstrates conflict feedback and physical versus logical recording. +const actions = { save, undo, search } +type Action = keyof typeof actions + +function ShortcutSettings() @{ + const [shortcuts, setShortcuts] = useState>({ + save: 'Mod+S', + undo: 'Mod+Z', + search: ['G', 'S'], + }) + const [editing, setEditing] = useState(null) + + const recorder = useHotkeyRecorder({ + maxSteps: 2, + onRecord: (hotkey) => { + if (editing) setShortcuts((prev) => ({ ...prev, [editing]: hotkey })) + setEditing(null) + }, + onCancel: () => setEditing(null), + }) + + useHotkeys( + Object.entries(shortcuts).map(([action, hotkey]) => ({ + hotkey, + callback: actions[action as Action], + })), + ) + +
    + @for (const [action, hotkey] of Object.entries(shortcuts); key action) { + + } +
    +} +``` ## Under the hood -Octane uses `@tanstack/octane-store` to subscribe to the recorder. The hook destroys its recorder on unmount. The core `HotkeyRecorder` owns the recording listeners. Store the accepted binding in application state rather than relying on recorder session state as your preferences store. +`useHotkeyRecorder` creates a `HotkeyRecorder` from `@tanstack/hotkeys` and subscribes the component to its state. diff --git a/docs/framework/octane/guides/hotkeys.md b/docs/framework/octane/guides/hotkeys.md index 1ecd932f..1fd99081 100644 --- a/docs/framework/octane/guides/hotkeys.md +++ b/docs/framework/octane/guides/hotkeys.md @@ -7,6 +7,18 @@ id: hotkeys Registration callbacks and options refresh after each commit. Registrations and recorder subscriptions are released when the component unmounts. Element targets are DOM nodes, not React ref objects. Use a callback ref that updates component state so the hook sees a newly mounted or replaced element. +## Chords and sequences + +A hotkey is either a **chord**, keys pressed together such as `'Mod+S'`, or a **sequence**, chords pressed one after another such as `['G', 'G']`. Every API in this guide accepts both, and a one-step array such as `['Mod+S']` is the chord `'Mod+S'`. + +```tsx +useHotkey('Mod+S', () => save()) // chord +useHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 }) // sequence +useHotkey('Escape', () => close(), { requireReset: true }) // chord-only option +``` + +Most options apply to both kinds. `requireReset` applies only to chords and `timeout` only to sequences, and TypeScript rejects options that don't match the hotkey. When the kind isn't known statically, such as a `Hotkey` loaded from settings, only the shared options are accepted. Defaults apply only to the kinds that support them. See the [Sequences Guide](./sequences) for timing and matching, and the [App Hotkeys Guide](./app-hotkeys) to declare hotkeys up front and let users customize them. + ## Logical keys and physical positions Use a logical binding when the shortcut should follow the character on the active layout. Use a physical binding when it should follow a keyboard position: @@ -171,7 +183,7 @@ Registration callbacks and options refresh after each commit. Registrations and ## Registering multiple hotkeys -Use `useHotkeys` to register a dynamic list. Per-definition options override common options. Removing an entry unregisters it. Each entry has the shared [HotkeyDefinition](../../../reference/adapter/interfaces/HotkeyDefinition) shape. +Use `useHotkeys` to register a dynamic list. Per-definition options override common options. Removing an entry unregisters it. Each entry has the shared [UseHotkeyDefinition](../reference/type-aliases/UseHotkeyDefinition.md) shape. ```tsx useHotkeys( @@ -181,9 +193,9 @@ useHotkeys( ``` ```ts -import type { HotkeyDefinition } from '@tanstack/octane-hotkeys' +import type { UseHotkeyDefinition } from '@tanstack/octane-hotkeys' -const shortcuts: Array = [ +const shortcuts: Array = [ { hotkey: 'Mod+S', callback: () => console.log('Save') }, { hotkey: 'Mod+Z', callback: () => console.log('Undo'), options: { enabled: false } }, ] @@ -209,7 +221,7 @@ declare module '@tanstack/hotkeys' { ## Introspecting registrations -Read `hotkeys` and `sequences` from `useHotkeyRegistrations()`. Disabled entries stay listed; destroyed registrations disappear. Both arrays include options and metadata. Hotkey views include enabled state and trigger counts; sequence views include sequence steps and progress. +Use the `useHotkeyRegistrations` hook to get a live view of every registration, chords and sequences together. It's the building block for shortcut palettes and help dialogs. ```tsx import { useHotkey, useHotkeyRegistrations, formatForDisplay } from '@tanstack/octane-hotkeys' @@ -219,17 +231,17 @@ export function Shortcuts() @{ meta: { name: 'Save', description: 'Save the document', group: 'File' }, }) const registrations = useHotkeyRegistrations() -
    -
      - @for (const registration of registrations.hotkeys) { -
    • {formatForDisplay(registration.hotkey)} {registration.options.meta?.name}
    • - } -
    -
    +
      + @for (const registration of registrations.hotkeys; key registration.id) { +
    • {formatForDisplay(registration.hotkey)} {registration.options.meta?.name}
    • + } +
    } ``` -Read `registrations.sequences` in the same way. Format each sequence step with `formatForDisplay` and join the labels with an arrow. Group entries by `registration.options.meta?.group` when building a help panel. +The returned object's `hotkeys` array holds one list of registration views. Each view has an `id`, the `hotkey`, its `options` (including `meta`), its `target`, and a `triggerCount`. Narrow with `kind`: chord views add `parsedHotkey`; sequence views add `parsedSequence` and `matchedStepCount`, the steps matched so far in the current attempt. Disabled entries stay listed; unmounted registrations disappear. Format any hotkey with `formatForDisplay`, and group entries by `options.meta?.group` when building a help panel. + +Registrations only exist while their components are mounted. To list hotkeys from routes that have not rendered, declare them with [app hotkeys](./app-hotkeys). ## The hotkey manager diff --git a/docs/framework/octane/guides/key-state-tracking.md b/docs/framework/octane/guides/key-state-tracking.md index 342df58c..a4ba69c7 100644 --- a/docs/framework/octane/guides/key-state-tracking.md +++ b/docs/framework/octane/guides/key-state-tracking.md @@ -89,7 +89,7 @@ Combine held logical names with the physical-code map. Format a logical name thr const visible = useHotkeyHint('Alt+Shift+[KeyK]') ``` -Holding Alt, Shift, or both reveals this hint. Extra Control hides it, as does releasing all modifiers or blurring the window. Nonmodifier keys are ignored. AltGraph never reveals hints. +Holding Alt, Shift, or both reveals this hint. Extra Control hides it, as does releasing all modifiers or blurring the window. Nonmodifier keys are ignored. AltGraph never reveals hints. A sequence reveals its hint from its first step, and an app hotkey uses its effective hotkey (no hint while unbound). Pass `{ exact: true }` to require every binding modifier, or `{ platform: 'mac' }` to resolve `Mod` explicitly. Supply the same platform used for registration. Combine the result with the action's enabled state. The core equivalent is `matchesHeldModifiers(binding, heldKeys, options)`. diff --git a/docs/framework/octane/guides/sequence-recording.md b/docs/framework/octane/guides/sequence-recording.md deleted file mode 100644 index 72b98e35..00000000 --- a/docs/framework/octane/guides/sequence-recording.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: Sequence Recording Guide -id: sequence-recording ---- - -Use `useHotkeySequenceRecorder` to record an ordered sequence of chords. Recording defaults to physical codes, such as `['[KeyG]', 'Alt+[KeyS]']`. Use `recordBy: 'key'` for logical characters. Pass the saved array directly to `useHotkeySequence`. - -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Basic usage - - -```tsx -import { useState } from 'octane' -import { useHotkeySequence, useHotkeySequenceRecorder, formatForDisplay } from '@tanstack/octane-hotkeys' -import type { HotkeySequence } from '@tanstack/octane-hotkeys' - -export function SequenceSettings() @{ - const [binding, setBinding] = useState(['G', 'G']) - const recorder = useHotkeySequenceRecorder({ onRecord: setBinding }) - useHotkeySequence(binding, () => console.log('Go to top')) - -
    - - @if (recorder.isRecording) { -
    -

    {recorder.steps.map((step) => formatForDisplay(step)).join(' → ')}

    - - -
    - } -
    -} -``` - -Press and release each chord, then press Enter or click Save. Cancellation leaves the saved binding unchanged. - -## Return value - -| Property | Type | Meaning | -| --- | --- | --- | -| `isRecording` | `boolean` | Whether a session is active. | -| `steps` | `HotkeySequence` | Chords captured in the current session. | -| `recordedSequence` | `HotkeySequence \| null` | The last committed sequence. | -| `startRecording` | `() => void` | Start a new session. | -| `stopRecording` | `() => void` | Stop without calling `onRecord` or `onCancel`. | -| `cancelRecording` | `() => void` | Discard the session and call `onCancel`. | -| `commitRecording` | `() => void` | Commit current steps; do nothing if empty. | - - -## Options - -- `recordBy`: `'code'` by default, or `'key'` for logical characters. Missing physical codes never fall back to logical recording. -- `onRecord(sequence)`: called when a nonempty sequence is committed. Clearing calls only `onClear`. -- `onCancel` and `onClear`: handle cancellation and removal of a saved binding. -- `commitKeys`: `'enter'` by default. Use `'none'` for manual or idle-timeout commit; plain Enter can then be recorded as a chord. -- `commitOnEnter`: with `commitKeys: 'enter'`, set this to `false` to record Enter as a chord and finish another way. -- `idleTimeoutMs`: commit after this many milliseconds of inactivity following a completed chord. No timer runs while waiting for the first chord. -- `platform`: override platform detection for portable `Mod` conversion. - -### Shared defaults - -Set `hotkeySequenceRecorder` defaults in `HotkeysProvider`. Options passed to an individual hook override provider defaults. For a plural hook, each definition's options override its common options. See the [provider setup](../quick-start.md#default-options-provider). Hook options refresh after each commit. - -### `ignoreInputs` - -The default is `true`, so normal typing in inputs, textareas, selects, and contentEditable elements passes through. Escape still cancels. Set `ignoreInputs: false` to record in a focused input. - -## Validation and conflicts - -`validate(sequence, { events, parsedSequence })` runs when committing and returns `true`, `false`, or a rejection message. `detectConflicts` checks single bindings and sequence prefixes. A rejected commit keeps the session active and the steps intact, so the user can edit them with Backspace. - -The recorder supports the same `detectConflicts`, `validate`, and `onReject` options as the [single-hotkey recorder](./hotkey-recording.md#validation-and-conflicts). Use live registration IDs to exclude the binding being edited. Physical/logical overlap is based on the captured events, not a guessed keyboard layout. - -## Behavior - -| Input | Behavior | -| --- | --- | -| Valid chord | Append to `steps` and continue listening. | -| Unmodified Enter with default commit settings and nonempty steps | Commit and call `onRecord`. | -| Escape | Cancel and call `onCancel`. | -| Unmodified Backspace or Delete with nonempty steps | Remove the last step without committing. | -| Unmodified Backspace or Delete with empty steps | Stop and call only `onClear`. | -| Modifier-only press, repeat, or IME composition | Do not append a step. | - -Recorded chords use portable `Mod`. Recording events, commit keys, and their releases are isolated from application hotkeys and sequences. Code mode rejects AltGraph character entry; key mode preserves the produced character without synthetic Control/Alt. - -## Under the hood - -Octane subscribes with `@tanstack/octane-store` and destroys the recorder on unmount. - -See the [useHotkeySequenceRecorder example](../examples/useHotkeySequenceRecorder) for editable sequence settings and the [kitchen sink](../examples/kitchen-sink) for manual commit, idle timeout, validation, and conflict feedback. diff --git a/docs/framework/octane/guides/sequences.md b/docs/framework/octane/guides/sequences.md index de203118..cdce2fb9 100644 --- a/docs/framework/octane/guides/sequences.md +++ b/docs/framework/octane/guides/sequences.md @@ -3,131 +3,144 @@ title: Sequences Guide id: sequences --- -A sequence is an ordered list of chords. Use `useHotkeySequence` for one sequence and `useHotkeySequences` for a changing list. A chord can be a logical binding such as `G`, a physical position such as `[KeyG]`, or a modifier combination such as `Mod+[KeyK]`. +A sequence is a hotkey made of chords pressed one after another rather than together. Think Vim-style navigation, cheat codes, or multi-step commands. Sequences are ordinary hotkeys: register them with `useHotkey` and `useHotkeys`, list them with `useHotkeyRegistrations`, record them with `useHotkeyRecorder`, and format them with `formatForDisplay`. + +Sequence steps use the same syntax as chords. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. ## Basic usage +Pass an array of chords wherever you pass a hotkey: ```tsx -import { useState } from 'octane' -import { useHotkeySequence, useHotkeySequences } from '@tanstack/octane-hotkeys' - -export function Navigation() @{ - const [enabled, setEnabled] = useState(true) - useHotkeySequence(['G', 'G'], () => window.scrollTo({ top: 0 }), { enabled, timeout: 1000 }) - useHotkeySequences([ - { sequence: ['D', 'D'], callback: () => console.log('Delete line') }, - { sequence: ['D', 'W'], callback: () => console.log('Delete word'), options: { timeout: 500 } }, - ], { enabled }) -
    - -
    +import { useHotkey } from '@tanstack/octane-hotkeys' + +function App() @{ + // Vim-style: press g then g to scroll to top + useHotkey(['G', 'G'], () => { + window.scrollTo({ top: 0, behavior: 'smooth' }) + }) +
    ...
    } ``` -The user must press and release each chord in order. The timeout applies between consecutive steps, rather than to the total duration of the sequence. +The user must press the steps in order, each within the timeout window. A one-step array such as `['Escape']` is the chord `'Escape'`. ## Many sequences at once -`useHotkeySequences` accepts an array of [HotkeySequenceDefinition](../../../reference/adapter/interfaces/HotkeySequenceDefinition) entries. Each contains `sequence`, `callback`, and optional `options`. An empty array removes all registrations owned by that call. Per-definition options override common options. Derive the array from component state and keep the hook call at a stable call site. - -## Sequence options +`useHotkeys` registers chords and sequences together, including dynamic lists: -### `timeout` - -The default is `1000` milliseconds between steps. A shorter timeout, such as `500`, requires faster entry; `2000` gives users more time. Expired sequences reset their progress. +```tsx +import { useHotkeys } from '@tanstack/octane-hotkeys' -### `enabled` +useHotkeys([ + { hotkey: ['G', 'G'], callback: () => scrollToTop() }, + { hotkey: ['D', 'D'], callback: () => deleteLine(), options: { timeout: 500 } }, + { hotkey: 'Mod+S', callback: () => save() }, +]) +``` -The default is `true`. Disabled sequences remain in the registry but do not execute. Pass a reactive flag to change this without discarding the registration. +## Sequence options -### Shared defaults +Sequences accept the same options as chords, except `requireReset`, plus `timeout`. -Set `hotkeySequence` defaults in `HotkeysProvider`. Options passed to an individual hook override provider defaults. For a plural hook, each definition's options override its common options. See the [provider setup](../quick-start.md#default-options-provider). +### `timeout` -### Targets, input handling, and propagation +The maximum time (in milliseconds) allowed between consecutive steps. If the user takes longer than this between any two steps, the sequence resets. Defaults to `1000` (1 second). -Sequences support `target`, `ignoreInputs`, `preventDefault`, `stopPropagation`, `platform`, and `conflictBehavior`. Use a focusable element to scope a sequence to a widget, and `null` while that element is unavailable. When omitted, `ignoreInputs` follows the first step: Control/Meta combinations and Escape work in inputs, while other first steps are ignored there. Set it explicitly to override that behavior. See the [target examples](./hotkeys.md#target). +```tsx +// Fast sequence - user must type quickly +useHotkey(['D', 'D'], () => deleteLine(), { timeout: 500 }) -### Metadata +// Slow sequence - user has more time between keys +useHotkey(['Shift+Z', 'Shift+Z'], () => forceQuit(), { timeout: 2000 }) +``` -Attach a `meta` object with a name, description, and group. Read it through live registration views to build help panels. Metadata does not change execution scope. +`timeout` is only accepted when the hotkey is a sequence. Set a default for every sequence with `HotkeysProvider`: ```tsx -useHotkeySequence(['G', 'G'], () => window.scrollTo({ top: 0 }), { meta: { name: 'Go to top', group: 'Navigation' } }) + + + ``` -## Sequences with modifiers +### `enabled` and `meta` -Use `Mod+K`, then `Mod+C` for an editor-style comment shortcut. Logical and physical steps can be mixed. +These work exactly as they do for chords. Disabled sequences stay registered and visible in devtools, and `meta` flows to registrations, devtools, and cheat sheets. See the [Hotkeys Guide](./hotkeys). ```tsx -useHotkeySequence(['Mod+K', 'Mod+C'], () => console.log("Comment selection")) +useHotkey(['G', 'G'], () => scrollToTop(), { + enabled: isVimMode, + meta: { name: 'Go to top', description: 'Scroll to the top of the page' }, +}) ``` -## Chained modifier chords +## Sequences with modifiers -Physical codes retain the same positions even when the keyboard layout changes. +Each step in a sequence can include modifiers, and consecutive steps can repeat the same modifier: ```tsx -useHotkeySequence(['Shift+[KeyR]', 'Shift+[KeyT]'], () => console.log("Next action")) +// Ctrl+K followed by Ctrl+C (VS Code-style comment) +useHotkey(['Mod+K', 'Mod+C'], () => commentSelection()) + +// Physical R then T positions, both with Shift +useHotkey(['Shift+[KeyR]', 'Shift+[KeyT]'], () => doNextAction()) ``` -### Modifier-only keys between steps +Brackets retain physical positions even when the keys produce different letters. While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored. They neither advance the sequence nor reset progress, so a user can tap or hold Shift between `Shift+R` and `Shift+T`. -Pressing Shift, Control, Alt, or Meta alone neither advances nor resets an in-progress sequence. Users can release and press Shift between the R and T chords. Modifier-only events, automatic repeats, and IME composition also do not refresh the timeout. +## Vim-style navigation -## Common sequence patterns +```tsx +useHotkeys([ + { hotkey: ['G', 'G'], callback: () => scrollToTop() }, + { hotkey: ['G', 'Shift+G'], callback: () => scrollToBottom() }, + { hotkey: ['D', 'D'], callback: () => deleteLine() }, + { hotkey: ['D', 'W'], callback: () => deleteWord() }, + { hotkey: ['C', 'I', 'W'], callback: () => changeInnerWord() }, +]) +``` -| Action | Steps | -| --- | --- | -| Go to top | `['G', 'G']` | -| Go to bottom | `['G', 'Shift+G']` | -| Delete line | `['D', 'D']` | -| Delete word | `['D', 'W']` | -| Change inner word | `['C', 'I', 'W']` | -| Open help | `['H', 'E', 'L', 'P']` | +## Displaying sequences -For a Konami-style sequence, use the full ordered list and a longer timeout: +`formatForDisplay` formats each step and joins them with `sequenceSeparator`, a space by default: -```tsx -useHotkeySequence(['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', - 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'B', 'A'], () => console.log("Unlocked"), { timeout: 2000 }) +```ts +formatForDisplay(['Mod+K', 'Mod+C'], { platform: 'mac' }) // '⌘ K ⌘ C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' then ' }) // 'G then G' ``` -## How sequences work - -The shared `SequenceManager` tracks every registration independently. Matching the next expected step advances progress. Completing all steps invokes the callback. The manager resets progress when the timeout expires and prefers exact key matches over weaker logical-key fallbacks. +Registration views report each sequence's progress in `matchedStepCount`, so a help panel can show how far the user has typed. See the [Hotkeys Guide](./hotkeys). -### Overlapping sequences +## How sequences work -`['D', 'D']`, `['D', 'W']`, and `['D', 'I', 'W']` can share a prefix. Pressing D advances each matching sequence; later steps determine which one completes. +The `HotkeyManager` tracks progress for every sequence registration. When a key is pressed: -### Conflicting registrations +1. It checks whether the key matches the next expected step of each sequence +2. If it matches, that sequence advances +3. If the timeout expires between steps, the sequence resets +4. When all steps are completed, the callback fires -Duplicate detection compares resolved steps on the same target. Modifier aliases, modifier order, and logical letter casing do not create distinct bindings. Physical and logical identities remain distinct. Sharing a prefix alone is not a duplicate registration, but recorder conflict checks also inspect prefixes and physical/logical overlap. +Modifier-only events, IME composition, and automatic keydown repeats neither advance a sequence nor refresh its timeout: holding G does not complete a two-press G sequence. Exact matches take priority over weaker logical-key fallbacks. -### Updates and cleanup +Multiple sequences can share a prefix, such as `['D', 'D']`, `['D', 'W']`, and `['D', 'I', 'W']`. Progress is tracked for each sequence independently, so after pressing `D` the manager waits for the next key to determine which sequence completes. -Write hook calls in compiler-enabled `.tsrx` components. The Octane compiler supplies hook identity, so call hooks at stable component call sites. Use the plural hooks for lists that change length. Do not supply the compiler's internal slot argument yourself. +### Conflicting registrations -Registration callbacks and options refresh after each commit. Registrations and recorder subscriptions are released when the component unmounts. Element targets are DOM nodes, not React ref objects. Use a callback ref that updates component state so the hook sees a newly mounted or replaced element. +For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate hotkeys. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection and app hotkey conflicts are stricter: there, a sequence also conflicts with a chord equal to its first step and with any sequence it starts. -## The sequence manager +## Standalone matching -Use `getSequenceManager()` for the shared registry. For standalone matching without registration, create your own matcher and remove your event listener when its owner is destroyed. +For sequence matching without the singleton manager, use `createSequenceMatcher`: ```ts import { createSequenceMatcher } from '@tanstack/octane-hotkeys' const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 }) -const onKeyDown = (event: KeyboardEvent) => { - if (matcher.match(event)) console.log('Sequence completed') - console.log(matcher.getProgress()) -} -document.addEventListener('keydown', onKeyDown) -// During teardown: -document.removeEventListener('keydown', onKeyDown) -``` -Try [useHotkeySequence](../examples/useHotkeySequence) and [useHotkeySequences](../examples/useHotkeySequences). +document.addEventListener('keydown', (e) => { + if (matcher.match(e)) { + console.log('Sequence completed!') + } + console.log('Progress:', matcher.getProgress()) // e.g., 1 +}) +``` diff --git a/docs/framework/octane/quick-start.md b/docs/framework/octane/quick-start.md index 41739cb8..85959b27 100644 --- a/docs/framework/octane/quick-start.md +++ b/docs/framework/octane/quick-start.md @@ -70,7 +70,7 @@ Use Octane component state for the enabled flag. ```tsx -useHotkeySequence(['G', 'G'], () => window.scrollTo({ top: 0 })) +useHotkey(['G', 'G'], () => window.scrollTo({ top: 0 })) ``` Release G between presses. Automatic key repeats do not advance sequences. @@ -110,10 +110,8 @@ import { HotkeysProvider } from '@tanstack/octane-hotkeys' export function Root() @{
    console.log('Cancelled') }, - hotkeySequenceRecorder: { idleTimeoutMs: 2000 }, + hotkey: { preventDefault: true, timeout: 1500 }, + hotkeyRecorder: { onCancel: () => console.log('Cancelled'), idleTimeoutMs: 2000 }, }}> @@ -121,14 +119,18 @@ export function Root() @{ } ``` -`useDefaultHotkeysOptions()` reads the nearest provider's defaults and returns an empty object outside a provider. `useHotkeysContext()` returns `{ defaultOptions }`, or `null` outside a provider. Nested providers replace the outer defaults; call-specific options and per-definition options still take precedence. +`useDefaultHotkeysOptions()` reads the nearest provider's defaults and returns an empty object outside a provider. `useHotkeysContext()` returns `{ defaultOptions, appHotkeysStore }`, or `null` outside a provider. Nested providers replace the outer defaults; call-specific options and per-definition options still take precedence. + +## Declaring app hotkeys + +To list every hotkey in a cheat sheet, including hotkeys whose components haven't mounted, and to let users customize them, declare them once with `defineAppHotkeys` and provide them with `HotkeysProvider`. See the [App Hotkeys Guide](./guides/app-hotkeys). ## Examples and next steps - [Hotkeys](./guides/hotkeys) - [Sequences](./guides/sequences) +- [App hotkeys](./guides/app-hotkeys) - [Hotkey recording](./guides/hotkey-recording) -- [Sequence recording](./guides/sequence-recording) - [Key state tracking](./guides/key-state-tracking) - [Formatting and display](./guides/formatting-display) - [Kitchen sink](./examples/kitchen-sink) diff --git a/docs/framework/octane/reference/functions/HotkeysProvider.md b/docs/framework/octane/reference/functions/HotkeysProvider.md index 85786f2e..9c845268 100644 --- a/docs/framework/octane/reference/functions/HotkeysProvider.md +++ b/docs/framework/octane/reference/functions/HotkeysProvider.md @@ -6,13 +6,17 @@ title: HotkeysProvider ```ts function HotkeysProvider(__namedParameters): ElementDescriptor<{ children: unknown; - value: DefaultHotkeysOptions; + value: { + appHotkeysStore: | AppHotkeysStore>>> + | null; + defaultOptions: DefaultHotkeysOptions; + }; }>; ``` -Defined in: [HotkeysProvider.ts:37](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L37) +Defined in: [HotkeysProvider.ts:70](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L70) -Sets registration and recorder defaults for descendant components. +Sets defaults, app hotkeys, and custom hotkeys for descendant components. ## Parameters @@ -24,5 +28,9 @@ Sets registration and recorder defaults for descendant components. `ElementDescriptor`\<\{ `children`: `unknown`; - `value`: [`DefaultHotkeysOptions`](../interfaces/DefaultHotkeysOptions.md); + `value`: \{ + `appHotkeysStore`: \| `AppHotkeysStore`\<`Readonly`\<`Record`\<`string`, `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>\>\> + \| `null`; + `defaultOptions`: [`DefaultHotkeysOptions`](../interfaces/DefaultHotkeysOptions.md); + \}; \}\> diff --git a/docs/framework/octane/reference/functions/useAppHotkeyList.md b/docs/framework/octane/reference/functions/useAppHotkeyList.md new file mode 100644 index 00000000..c340ff2a --- /dev/null +++ b/docs/framework/octane/reference/functions/useAppHotkeyList.md @@ -0,0 +1,18 @@ +--- +id: useAppHotkeyList +title: useAppHotkeyList +--- + +```ts +function useAppHotkeyList(): AppHotkeyListItem>[]; +``` + +Defined in: useAppHotkeys.ts:39 + +Lists every app hotkey with its live status, in declaration order, for +cheat sheets and settings screens. App hotkeys whose components have not +mounted are `'inactive'`; those without a hotkey are `'unbound'`. + +## Returns + +`AppHotkeyListItem`\<`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>[] diff --git a/docs/framework/octane/reference/functions/useAppHotkeys.md b/docs/framework/octane/reference/functions/useAppHotkeys.md new file mode 100644 index 00000000..2e5ca83e --- /dev/null +++ b/docs/framework/octane/reference/functions/useAppHotkeys.md @@ -0,0 +1,17 @@ +--- +id: useAppHotkeys +title: useAppHotkeys +--- + +```ts +function useAppHotkeys(): RegisteredAppHotkeys; +``` + +Defined in: useAppHotkeys.ts:29 + +Returns the effective app hotkeys from `HotkeysProvider`, typed by +`Register`: each declaration with the user's custom hotkey applied. + +## Returns + +`RegisteredAppHotkeys` diff --git a/docs/framework/octane/reference/functions/useCustomHotkeys.md b/docs/framework/octane/reference/functions/useCustomHotkeys.md new file mode 100644 index 00000000..2f86257d --- /dev/null +++ b/docs/framework/octane/reference/functions/useCustomHotkeys.md @@ -0,0 +1,18 @@ +--- +id: useCustomHotkeys +title: useCustomHotkeys +--- + +```ts +function useCustomHotkeys(): UseCustomHotkeysResult; +``` + +Defined in: useAppHotkeys.ts:57 + +Reads and changes the user's custom hotkeys. Changes apply to every +registered app hotkey immediately and are passed to the provider's +`onCustomHotkeysChange`, ready to save. + +## Returns + +[`UseCustomHotkeysResult`](../interfaces/UseCustomHotkeysResult.md) diff --git a/docs/framework/octane/reference/functions/useDefaultHotkeysOptions.md b/docs/framework/octane/reference/functions/useDefaultHotkeysOptions.md index 0a2ba8d5..15336bae 100644 --- a/docs/framework/octane/reference/functions/useDefaultHotkeysOptions.md +++ b/docs/framework/octane/reference/functions/useDefaultHotkeysOptions.md @@ -7,7 +7,7 @@ title: useDefaultHotkeysOptions function useDefaultHotkeysOptions(): DefaultHotkeysOptions; ``` -Defined in: [HotkeysProvider.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L32) +Defined in: [HotkeysProvider.ts:65](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L65) Reads the nearest provider's defaults, or an empty object. diff --git a/docs/framework/octane/reference/functions/useHotkey.md b/docs/framework/octane/reference/functions/useHotkey.md index bdf6650a..5f4eb977 100644 --- a/docs/framework/octane/reference/functions/useHotkey.md +++ b/docs/framework/octane/reference/functions/useHotkey.md @@ -3,31 +3,80 @@ id: useHotkey title: useHotkey --- +## Call Signature + ```ts -function useHotkey( +function useHotkey( hotkey, callback, options?): void; ``` -Defined in: [useHotkey.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkey.ts#L11) +Defined in: [useHotkey.ts:47](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkey.ts#L47) + +Registers one hotkey: a chord such as `'Mod+S'`, a sequence such as +`['G', 'G']`, or an app hotkey from `defineAppHotkeys`. The callback and +options are refreshed after every commit. + +### Type Parameters + +#### T + +`T` *extends* `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\> + +### Parameters + +#### hotkey + +`T` + +#### callback + +`HotkeyCallback` + +#### options? + +[`UseAppHotkeyOptions`](../type-aliases/UseAppHotkeyOptions.md)\<`T`\> + +### Returns + +`void` + +## Call Signature + +```ts +function useHotkey( + hotkey, + callback, + options?): void; +``` + +Defined in: [useHotkey.ts:52](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkey.ts#L52) + +Registers one hotkey: a chord such as `'Mod+S'`, a sequence such as +`['G', 'G']`, or an app hotkey from `defineAppHotkeys`. The callback and +options are refreshed after every commit. + +### Type Parameters + +#### T -Registers one shortcut. Callback and options are refreshed after every commit. +`T` *extends* `RegisterableHotkey` -## Parameters +### Parameters -### hotkey +#### hotkey -`RegisterableHotkey` +`T` -### callback +#### callback `HotkeyCallback` -### options? +#### options? -`HotkeyOptions` +`AdapterOptionsFor`\<`T`, [`UseHotkeyTarget`](../type-aliases/UseHotkeyTarget.md)\> -## Returns +### Returns `void` diff --git a/docs/framework/octane/reference/functions/useHotkeyHint.md b/docs/framework/octane/reference/functions/useHotkeyHint.md index 88db5a22..d858abb9 100644 --- a/docs/framework/octane/reference/functions/useHotkeyHint.md +++ b/docs/framework/octane/reference/functions/useHotkeyHint.md @@ -7,15 +7,18 @@ title: useHotkeyHint function useHotkeyHint(hotkey, options?): boolean; ``` -Defined in: [useHotkeyHint.ts:6](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkeyHint.ts#L6) +Defined in: [useHotkeyHint.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkeyHint.ts#L20) -Reports whether held modifiers reveal a shortcut hint. +Reports whether held modifiers reveal a shortcut hint. A sequence reveals +its hint from its first step, and an app hotkey uses its effective hotkey +(false while unbound). ## Parameters ### hotkey -`RegisterableHotkey` + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\> ### options? diff --git a/docs/framework/octane/reference/functions/useHotkeyRecorder.md b/docs/framework/octane/reference/functions/useHotkeyRecorder.md index 18978528..53ca5fd0 100644 --- a/docs/framework/octane/reference/functions/useHotkeyRecorder.md +++ b/docs/framework/octane/reference/functions/useHotkeyRecorder.md @@ -3,20 +3,50 @@ id: useHotkeyRecorder title: useHotkeyRecorder --- +## Call Signature + +```ts +function useHotkeyRecorder(options): OctaneHotkeyRecorder; +``` + +Defined in: [useHotkeyRecorder.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkeyRecorder.ts#L20) + +Records a shortcut and destroys the recorder on unmount. With the default +`maxSteps: 1`, the first valid chord is recorded. With a larger `maxSteps`, +each chord adds a step; the recording commits on Enter (unless +`commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### options + +`HotkeyChordRecorderOptions` + +### Returns + +[`OctaneHotkeyRecorder`](../interfaces/OctaneHotkeyRecorder.md)\<`HotkeyChord`\> + +## Call Signature + ```ts function useHotkeyRecorder(options): OctaneHotkeyRecorder; ``` -Defined in: [useHotkeyRecorder.ts:9](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkeyRecorder.ts#L9) +Defined in: [useHotkeyRecorder.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkeyRecorder.ts#L23) -Records a shortcut and destroys the recorder on unmount. +Records a shortcut and destroys the recorder on unmount. With the default +`maxSteps: 1`, the first valid chord is recorded. With a larger `maxSteps`, +each chord adds a step; the recording commits on Enter (unless +`commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. -## Parameters +### Parameters -### options +#### options -`HotkeyRecorderOptions` +`HotkeySequenceRecorderOptions` -## Returns +### Returns [`OctaneHotkeyRecorder`](../interfaces/OctaneHotkeyRecorder.md) diff --git a/docs/framework/octane/reference/functions/useHotkeyRegistrations.md b/docs/framework/octane/reference/functions/useHotkeyRegistrations.md index a155fe17..5a8ea757 100644 --- a/docs/framework/octane/reference/functions/useHotkeyRegistrations.md +++ b/docs/framework/octane/reference/functions/useHotkeyRegistrations.md @@ -7,9 +7,9 @@ title: useHotkeyRegistrations function useHotkeyRegistrations(): HotkeyRegistrationsResult; ``` -Defined in: [useHotkeyRegistrations.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkeyRegistrations.ts#L10) +Defined in: [useHotkeyRegistrations.ts:6](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkeyRegistrations.ts#L6) -Reads public registration snapshots from both managers. +Reads public snapshots of every registration, chords and sequences. ## Returns diff --git a/docs/framework/octane/reference/functions/useHotkeySequence.md b/docs/framework/octane/reference/functions/useHotkeySequence.md deleted file mode 100644 index 598f09e4..00000000 --- a/docs/framework/octane/reference/functions/useHotkeySequence.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -id: useHotkeySequence -title: useHotkeySequence ---- - -```ts -function useHotkeySequence( - sequence, - callback, - options?): void; -``` - -Defined in: [useHotkeySequence.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkeySequence.ts#L11) - -Registers one multi-chord shortcut. Empty sequences do not register. - -## Parameters - -### sequence - -`HotkeySequence` - -### callback - -`HotkeyCallback` - -### options? - -`SequenceOptions` - -## Returns - -`void` diff --git a/docs/framework/octane/reference/functions/useHotkeySequenceRecorder.md b/docs/framework/octane/reference/functions/useHotkeySequenceRecorder.md deleted file mode 100644 index 912c3f5a..00000000 --- a/docs/framework/octane/reference/functions/useHotkeySequenceRecorder.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: useHotkeySequenceRecorder -title: useHotkeySequenceRecorder ---- - -```ts -function useHotkeySequenceRecorder(options): OctaneHotkeySequenceRecorder; -``` - -Defined in: [useHotkeySequenceRecorder.ts:9](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkeySequenceRecorder.ts#L9) - -Records a sequence, including live steps and explicit commit/cancel controls. - -## Parameters - -### options - -`HotkeySequenceRecorderOptions` - -## Returns - -[`OctaneHotkeySequenceRecorder`](../interfaces/OctaneHotkeySequenceRecorder.md) diff --git a/docs/framework/octane/reference/functions/useHotkeySequences.md b/docs/framework/octane/reference/functions/useHotkeySequences.md deleted file mode 100644 index 2d776c7a..00000000 --- a/docs/framework/octane/reference/functions/useHotkeySequences.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -id: useHotkeySequences -title: useHotkeySequences ---- - -```ts -function useHotkeySequences(definitions, options?): void; -``` - -Defined in: [useHotkeySequences.ts:9](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkeySequences.ts#L9) - -Registers a changing list of sequences and releases it on unmount. - -## Parameters - -### definitions - -`HotkeySequenceDefinition`[] - -### options? - -`SequenceOptions` - -## Returns - -`void` diff --git a/docs/framework/octane/reference/functions/useHotkeys.md b/docs/framework/octane/reference/functions/useHotkeys.md index 2746b6f1..73841aa6 100644 --- a/docs/framework/octane/reference/functions/useHotkeys.md +++ b/docs/framework/octane/reference/functions/useHotkeys.md @@ -7,19 +7,22 @@ title: useHotkeys function useHotkeys(definitions, options?): void; ``` -Defined in: [useHotkeys.ts:9](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkeys.ts#L9) +Defined in: [useHotkeys.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkeys.ts#L23) -Registers a changing list of shortcuts after commit and releases it on unmount. +Registers a changing list of hotkeys (chords, sequences, and app hotkeys) +after commit and releases it on unmount. Options merge in this order: +provider defaults, app hotkey declaration, common options, per-hotkey +options. Unbound app hotkeys and empty sequences register nothing. ## Parameters ### definitions -`HotkeyDefinition`[] +readonly [`UseHotkeyDefinition`](../type-aliases/UseHotkeyDefinition.md)[] ### options? -`HotkeyOptions` +[`UseHotkeyCommonOptions`](../type-aliases/UseHotkeyCommonOptions.md) ## Returns diff --git a/docs/framework/octane/reference/functions/useHotkeysContext.md b/docs/framework/octane/reference/functions/useHotkeysContext.md index ec32f0f9..26e5c18e 100644 --- a/docs/framework/octane/reference/functions/useHotkeysContext.md +++ b/docs/framework/octane/reference/functions/useHotkeysContext.md @@ -4,20 +4,13 @@ title: useHotkeysContext --- ```ts -function useHotkeysContext(): - | { - defaultOptions: DefaultHotkeysOptions; -} - | null; +function useHotkeysContext(): HotkeysContextValue | null; ``` -Defined in: [HotkeysProvider.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L26) +Defined in: [HotkeysProvider.ts:60](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L60) Reads the nearest provider, or null outside a provider. ## Returns - \| \{ - `defaultOptions`: [`DefaultHotkeysOptions`](../interfaces/DefaultHotkeysOptions.md); -\} - \| `null` +`HotkeysContextValue` \| `null` diff --git a/docs/framework/octane/reference/index.md b/docs/framework/octane/reference/index.md index b4a62e75..0827d432 100644 --- a/docs/framework/octane/reference/index.md +++ b/docs/framework/octane/reference/index.md @@ -9,15 +9,26 @@ title: "@tanstack/octane-hotkeys" - [HotkeyRegistrationsResult](interfaces/HotkeyRegistrationsResult.md) - [HotkeysProviderProps](interfaces/HotkeysProviderProps.md) - [OctaneHotkeyRecorder](interfaces/OctaneHotkeyRecorder.md) -- [OctaneHotkeySequenceRecorder](interfaces/OctaneHotkeySequenceRecorder.md) +- [UseCustomHotkeysResult](interfaces/UseCustomHotkeysResult.md) ## Type Aliases - [HotkeysProviderOptions](type-aliases/HotkeysProviderOptions.md) +- [UseAppHotkeyOptions](type-aliases/UseAppHotkeyOptions.md) +- [UseHotkeyChordOptions](type-aliases/UseHotkeyChordOptions.md) +- [UseHotkeyCommonOptions](type-aliases/UseHotkeyCommonOptions.md) +- [UseHotkeyDefinition](type-aliases/UseHotkeyDefinition.md) +- [UseHotkeyOptions](type-aliases/UseHotkeyOptions.md) +- [UseHotkeyOptionsFor](type-aliases/UseHotkeyOptionsFor.md) +- [UseHotkeySequenceOptions](type-aliases/UseHotkeySequenceOptions.md) +- [UseHotkeyTarget](type-aliases/UseHotkeyTarget.md) ## Functions - [HotkeysProvider](functions/HotkeysProvider.md) +- [useAppHotkeyList](functions/useAppHotkeyList.md) +- [useAppHotkeys](functions/useAppHotkeys.md) +- [useCustomHotkeys](functions/useCustomHotkeys.md) - [useDefaultHotkeysOptions](functions/useDefaultHotkeysOptions.md) - [useHeldKeyCodes](functions/useHeldKeyCodes.md) - [useHeldKeys](functions/useHeldKeys.md) @@ -27,7 +38,4 @@ title: "@tanstack/octane-hotkeys" - [useHotkeyRegistrations](functions/useHotkeyRegistrations.md) - [useHotkeys](functions/useHotkeys.md) - [useHotkeysContext](functions/useHotkeysContext.md) -- [useHotkeySequence](functions/useHotkeySequence.md) -- [useHotkeySequenceRecorder](functions/useHotkeySequenceRecorder.md) -- [useHotkeySequences](functions/useHotkeySequences.md) - [useKeyHold](functions/useKeyHold.md) diff --git a/docs/framework/octane/reference/interfaces/DefaultHotkeysOptions.md b/docs/framework/octane/reference/interfaces/DefaultHotkeysOptions.md index 774d683b..09a21022 100644 --- a/docs/framework/octane/reference/interfaces/DefaultHotkeysOptions.md +++ b/docs/framework/octane/reference/interfaces/DefaultHotkeysOptions.md @@ -3,44 +3,29 @@ id: DefaultHotkeysOptions title: DefaultHotkeysOptions --- -Defined in: [HotkeysProvider.ts:9](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L9) +Defined in: [HotkeysProvider.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L18) ## Properties ### hotkey? ```ts -optional hotkey?: HotkeyOptions; +optional hotkey?: UseHotkeyCommonOptions; ``` -Defined in: [HotkeysProvider.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L10) +Defined in: [HotkeysProvider.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L23) -*** - -### hotkeyRecorder? - -```ts -optional hotkeyRecorder?: Partial; -``` - -Defined in: [HotkeysProvider.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L12) +Defaults for every hotkey registered by `useHotkey` and `useHotkeys`. +`requireReset` applies to chords and `timeout` applies to sequences. *** -### hotkeySequence? +### hotkeyRecorder? ```ts -optional hotkeySequence?: SequenceOptions; +optional hotkeyRecorder?: Partial>; ``` -Defined in: [HotkeysProvider.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L11) - -*** - -### hotkeySequenceRecorder? - -```ts -optional hotkeySequenceRecorder?: Partial; -``` +Defined in: [HotkeysProvider.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L25) -Defined in: [HotkeysProvider.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L13) +Defaults for every `useHotkeyRecorder`. diff --git a/docs/framework/octane/reference/interfaces/HotkeyRegistrationsResult.md b/docs/framework/octane/reference/interfaces/HotkeyRegistrationsResult.md index cb7d0686..264fe35e 100644 --- a/docs/framework/octane/reference/interfaces/HotkeyRegistrationsResult.md +++ b/docs/framework/octane/reference/interfaces/HotkeyRegistrationsResult.md @@ -3,9 +3,9 @@ id: HotkeyRegistrationsResult title: HotkeyRegistrationsResult --- -Defined in: [types.ts:41](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L41) +Defined in: [types.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L29) -Live registration views from the shared hotkey and sequence managers. +Live registration views from the shared hotkey manager. ## Properties @@ -15,14 +15,6 @@ Live registration views from the shared hotkey and sequence managers. hotkeys: HotkeyRegistrationView[]; ``` -Defined in: [types.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L42) +Defined in: [types.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L31) -*** - -### sequences - -```ts -sequences: SequenceRegistrationView[]; -``` - -Defined in: [types.ts:43](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L43) +Every registration, chords and sequences. Narrow each view with `kind`. diff --git a/docs/framework/octane/reference/interfaces/HotkeysProviderProps.md b/docs/framework/octane/reference/interfaces/HotkeysProviderProps.md index 9c69b104..d9fe06b6 100644 --- a/docs/framework/octane/reference/interfaces/HotkeysProviderProps.md +++ b/docs/framework/octane/reference/interfaces/HotkeysProviderProps.md @@ -3,17 +3,45 @@ id: HotkeysProviderProps title: HotkeysProviderProps --- -Defined in: [HotkeysProvider.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L20) +Defined in: [HotkeysProvider.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L42) ## Properties +### appHotkeys? + +```ts +optional appHotkeys?: Readonly>>; +``` + +Defined in: [HotkeysProvider.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L49) + +Every app hotkey, from `defineAppHotkeys`. Read when the provider first +renders; listed by `useAppHotkeyList` whether or not they are registered. + +*** + ### children? ```ts optional children?: unknown; ``` -Defined in: [HotkeysProvider.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L21) +Defined in: [HotkeysProvider.ts:43](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L43) + +*** + +### customHotkeys? + +```ts +optional customHotkeys?: + | CustomHotkeys>>> + | null; +``` + +Defined in: [HotkeysProvider.ts:54](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L54) + +Starting custom hotkeys, such as saved data. Read once; the provider owns +the state afterwards. Invalid entries are ignored with a warning. *** @@ -23,4 +51,26 @@ Defined in: [HotkeysProvider.ts:21](https://github.com/TanStack/hotkeys/blob/mai optional defaultOptions?: DefaultHotkeysOptions; ``` -Defined in: [HotkeysProvider.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L22) +Defined in: [HotkeysProvider.ts:44](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L44) + +*** + +### onCustomHotkeysChange? + +```ts +optional onCustomHotkeysChange?: (customHotkeys) => void; +``` + +Defined in: [HotkeysProvider.ts:56](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L56) + +Called with the custom hotkeys after every change, ready to save. + +#### Parameters + +##### customHotkeys + +`CustomHotkeys` + +#### Returns + +`void` diff --git a/docs/framework/octane/reference/interfaces/OctaneHotkeyRecorder.md b/docs/framework/octane/reference/interfaces/OctaneHotkeyRecorder.md index 764a1f9e..bf974926 100644 --- a/docs/framework/octane/reference/interfaces/OctaneHotkeyRecorder.md +++ b/docs/framework/octane/reference/interfaces/OctaneHotkeyRecorder.md @@ -3,10 +3,16 @@ id: OctaneHotkeyRecorder title: OctaneHotkeyRecorder --- -Defined in: [types.ts:9](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L9) +Defined in: [types.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L11) Recording state and controls owned by the containing component. +## Type Parameters + +### THotkey + +`THotkey` *extends* `Hotkey` = `Hotkey` + ## Properties ### cancelRecording @@ -15,7 +21,7 @@ Recording state and controls owned by the containing component. cancelRecording: () => void; ``` -Defined in: [types.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L19) +Defined in: [types.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L23) Stop, clear recorder state, and call onCancel. @@ -25,13 +31,29 @@ Stop, clear recorder state, and call onCancel. *** +### commitRecording + +```ts +commitRecording: () => void; +``` + +Defined in: [types.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L25) + +Commit the current steps (multi-step recordings). + +#### Returns + +`void` + +*** + ### isRecording ```ts isRecording: boolean; ``` -Defined in: [types.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L11) +Defined in: [types.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L13) Whether the recorder is listening for a shortcut. @@ -40,12 +62,12 @@ Whether the recorder is listening for a shortcut. ### recordedHotkey ```ts -recordedHotkey: Hotkey | null; +recordedHotkey: THotkey | null; ``` -Defined in: [types.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L13) +Defined in: [types.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L17) -The last recorded binding, including brackets for physical codes. +The last committed hotkey, including brackets for physical codes. *** @@ -55,7 +77,7 @@ The last recorded binding, including brackets for physical codes. startRecording: () => void; ``` -Defined in: [types.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L15) +Defined in: [types.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L19) Start a new recording session. @@ -65,13 +87,25 @@ Start a new recording session. *** +### steps + +```ts +steps: HotkeySequence; +``` + +Defined in: [types.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L15) + +Chords captured so far in a multi-step recording. + +*** + ### stopRecording ```ts stopRecording: () => void; ``` -Defined in: [types.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L17) +Defined in: [types.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L21) Stop and clear recorder state without calling onRecord or onCancel. diff --git a/docs/framework/octane/reference/interfaces/OctaneHotkeySequenceRecorder.md b/docs/framework/octane/reference/interfaces/OctaneHotkeySequenceRecorder.md deleted file mode 100644 index 4c01e693..00000000 --- a/docs/framework/octane/reference/interfaces/OctaneHotkeySequenceRecorder.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -id: OctaneHotkeySequenceRecorder -title: OctaneHotkeySequenceRecorder ---- - -Defined in: [types.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L23) - -Sequence recording state and controls. - -## Properties - -### cancelRecording - -```ts -cancelRecording: () => void; -``` - -Defined in: [types.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L35) - -Discard the current session and call onCancel. - -#### Returns - -`void` - -*** - -### commitRecording - -```ts -commitRecording: () => void; -``` - -Defined in: [types.ts:37](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L37) - -Commit the current steps. Does nothing when no steps are recorded. - -#### Returns - -`void` - -*** - -### isRecording - -```ts -isRecording: boolean; -``` - -Defined in: [types.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L25) - -Whether the recorder is listening for chords. - -*** - -### recordedSequence - -```ts -recordedSequence: HotkeySequence | null; -``` - -Defined in: [types.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L29) - -The last committed sequence. - -*** - -### startRecording - -```ts -startRecording: () => void; -``` - -Defined in: [types.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L31) - -Start a new recording session. - -#### Returns - -`void` - -*** - -### steps - -```ts -steps: HotkeySequence; -``` - -Defined in: [types.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L27) - -Chords captured in the current session. - -*** - -### stopRecording - -```ts -stopRecording: () => void; -``` - -Defined in: [types.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L33) - -Stop without committing or calling onCancel. - -#### Returns - -`void` diff --git a/docs/framework/octane/reference/interfaces/UseCustomHotkeysResult.md b/docs/framework/octane/reference/interfaces/UseCustomHotkeysResult.md new file mode 100644 index 00000000..17c6b0c3 --- /dev/null +++ b/docs/framework/octane/reference/interfaces/UseCustomHotkeysResult.md @@ -0,0 +1,111 @@ +--- +id: UseCustomHotkeysResult +title: UseCustomHotkeysResult +--- + +Defined in: [types.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L35) + +Custom hotkey state and actions returned by `useCustomHotkeys`. + +## Properties + +### customHotkeys + +```ts +customHotkeys: CustomHotkeys; +``` + +Defined in: [types.ts:37](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L37) + +The user's hotkeys that differ from the defaults. + +*** + +### findConflicts + +```ts +findConflicts: (id, hotkey) => AppHotkey[]; +``` + +Defined in: [types.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L51) + +Other app hotkeys that would collide if `id` used `hotkey`. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] + +*** + +### resetCustomHotkey + +```ts +resetCustomHotkey: (id) => void; +``` + +Defined in: [types.ts:47](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L47) + +Restores the default hotkey for an app hotkey. + +#### Parameters + +##### id + +`string` + +#### Returns + +`void` + +*** + +### resetCustomHotkeys + +```ts +resetCustomHotkeys: () => void; +``` + +Defined in: [types.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L49) + +Restores every default hotkey. + +#### Returns + +`void` + +*** + +### setCustomHotkey + +```ts +setCustomHotkey: (id, hotkey) => AppHotkey[]; +``` + +Defined in: [types.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/types.ts#L42) + +Sets the user's hotkey for an app hotkey. `null` unbinds it. +Returns conflicting app hotkeys; when any exist, nothing changes. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` \| `null` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] diff --git a/docs/framework/octane/reference/type-aliases/HotkeysProviderOptions.md b/docs/framework/octane/reference/type-aliases/HotkeysProviderOptions.md index 8e23444e..06958275 100644 --- a/docs/framework/octane/reference/type-aliases/HotkeysProviderOptions.md +++ b/docs/framework/octane/reference/type-aliases/HotkeysProviderOptions.md @@ -7,4 +7,4 @@ title: HotkeysProviderOptions type HotkeysProviderOptions = DefaultHotkeysOptions; ``` -Defined in: [HotkeysProvider.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L18) +Defined in: [HotkeysProvider.ts:40](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/HotkeysProvider.ts#L40) diff --git a/docs/framework/octane/reference/type-aliases/UseAppHotkeyOptions.md b/docs/framework/octane/reference/type-aliases/UseAppHotkeyOptions.md new file mode 100644 index 00000000..3513e5fb --- /dev/null +++ b/docs/framework/octane/reference/type-aliases/UseAppHotkeyOptions.md @@ -0,0 +1,18 @@ +--- +id: UseAppHotkeyOptions +title: UseAppHotkeyOptions +--- + +```ts +type UseAppHotkeyOptions = AdapterAppHotkeyOptions; +``` + +Defined in: [useHotkey.ts:39](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkey.ts#L39) + +Call-site options for an app hotkey, typed by its declared default kind. + +## Type Parameters + +### T + +`T` *extends* `AppHotkey` = `AppHotkey` diff --git a/docs/framework/octane/reference/type-aliases/UseHotkeyChordOptions.md b/docs/framework/octane/reference/type-aliases/UseHotkeyChordOptions.md new file mode 100644 index 00000000..b6e9b11c --- /dev/null +++ b/docs/framework/octane/reference/type-aliases/UseHotkeyChordOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeyChordOptions +title: UseHotkeyChordOptions +--- + +```ts +type UseHotkeyChordOptions = AdapterChordOptions; +``` + +Defined in: [useHotkey.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkey.ts#L26) + +Options for a chord such as `'Mod+S'`. diff --git a/docs/framework/octane/reference/type-aliases/UseHotkeyCommonOptions.md b/docs/framework/octane/reference/type-aliases/UseHotkeyCommonOptions.md new file mode 100644 index 00000000..e1b841a2 --- /dev/null +++ b/docs/framework/octane/reference/type-aliases/UseHotkeyCommonOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeyCommonOptions +title: UseHotkeyCommonOptions +--- + +```ts +type UseHotkeyCommonOptions = AdapterCommonOptions; +``` + +Defined in: [useHotkey.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkey.ts#L32) + +Options applied to every hotkey, such as provider defaults. Each applies to the kinds that support it. diff --git a/docs/framework/octane/reference/type-aliases/UseHotkeyDefinition.md b/docs/framework/octane/reference/type-aliases/UseHotkeyDefinition.md new file mode 100644 index 00000000..305cf696 --- /dev/null +++ b/docs/framework/octane/reference/type-aliases/UseHotkeyDefinition.md @@ -0,0 +1,13 @@ +--- +id: UseHotkeyDefinition +title: UseHotkeyDefinition +--- + +```ts +type UseHotkeyDefinition = AdapterHotkeyDefinition; +``` + +Defined in: [useHotkeys.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkeys.ts#L15) + +One hotkey for `useHotkeys`. Chords accept `requireReset`; sequences accept +`timeout`; hotkeys of unknown kind accept only shared options. diff --git a/docs/framework/octane/reference/type-aliases/UseHotkeyOptions.md b/docs/framework/octane/reference/type-aliases/UseHotkeyOptions.md new file mode 100644 index 00000000..2f4b8b19 --- /dev/null +++ b/docs/framework/octane/reference/type-aliases/UseHotkeyOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeyOptions +title: UseHotkeyOptions +--- + +```ts +type UseHotkeyOptions = AdapterHotkeyOptions; +``` + +Defined in: [useHotkey.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkey.ts#L23) + +Options shared by chords and sequences. diff --git a/docs/framework/octane/reference/type-aliases/UseHotkeyOptionsFor.md b/docs/framework/octane/reference/type-aliases/UseHotkeyOptionsFor.md new file mode 100644 index 00000000..43c8a502 --- /dev/null +++ b/docs/framework/octane/reference/type-aliases/UseHotkeyOptionsFor.md @@ -0,0 +1,18 @@ +--- +id: UseHotkeyOptionsFor +title: UseHotkeyOptionsFor +--- + +```ts +type UseHotkeyOptionsFor = AdapterOptionsFor; +``` + +Defined in: [useHotkey.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkey.ts#L35) + +The options type for a hotkey: chord options for chords, sequence options for sequences. + +## Type Parameters + +### T + +`T` *extends* `RegisterableHotkey` diff --git a/docs/framework/octane/reference/type-aliases/UseHotkeySequenceOptions.md b/docs/framework/octane/reference/type-aliases/UseHotkeySequenceOptions.md new file mode 100644 index 00000000..4506df22 --- /dev/null +++ b/docs/framework/octane/reference/type-aliases/UseHotkeySequenceOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeySequenceOptions +title: UseHotkeySequenceOptions +--- + +```ts +type UseHotkeySequenceOptions = AdapterSequenceOptions; +``` + +Defined in: [useHotkey.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkey.ts#L29) + +Options for a sequence such as `['G', 'G']`. diff --git a/docs/framework/octane/reference/type-aliases/UseHotkeyTarget.md b/docs/framework/octane/reference/type-aliases/UseHotkeyTarget.md new file mode 100644 index 00000000..68c4b0a1 --- /dev/null +++ b/docs/framework/octane/reference/type-aliases/UseHotkeyTarget.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeyTarget +title: UseHotkeyTarget +--- + +```ts +type UseHotkeyTarget = Target | null; +``` + +Defined in: [useHotkey.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/octane-hotkeys/src/useHotkey.ts#L20) + +The element to listen on. A `null` target skips registration until it is set. diff --git a/docs/framework/preact/guides/app-hotkeys.md b/docs/framework/preact/guides/app-hotkeys.md new file mode 100644 index 00000000..c661756b --- /dev/null +++ b/docs/framework/preact/guides/app-hotkeys.md @@ -0,0 +1,210 @@ +--- +title: App Hotkeys Guide +id: app-hotkeys +--- + +`useHotkey` registers a hotkey when its component mounts. A cheat sheet built from live registrations therefore misses hotkeys on routes that have not rendered yet, and a settings screen has nowhere to change them. App hotkeys solve both: you declare every hotkey in one place, pass the declarations to `HotkeysProvider`, and attach callbacks where the work happens. Users can then customize any of them, and your app decides how to save their choices. + +A declaration holds a hotkey's id, default hotkey, metadata, and options. It never holds a callback. `useHotkey` and `useHotkeys` still attach the callbacks. + +## Declare app hotkeys + +Use `defineAppHotkeys` in a module that contains only declarations. Keep it free of components and handlers, because it loads with your app shell: + +```ts +// app-hotkeys.ts +import { defineAppHotkeys } from '@tanstack/preact-hotkeys' + +export const appHotkeys = defineAppHotkeys({ + 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save', group: 'Document' } }, + 'doc.find': { + hotkey: 'Mod+F', + meta: { name: 'Find', group: 'Document' }, + context: 'editor', + }, + 'nav.home': { + hotkey: ['G', 'H'], + meta: { name: 'Go home', group: 'Navigation' }, + options: { timeout: 800 }, + }, + 'palette.open': { hotkey: null, meta: { name: 'Command palette' } }, +}) + +declare module '@tanstack/preact-hotkeys' { + interface Register { + appHotkeys: typeof appHotkeys + } +} +``` + +- The object key is the app hotkey's id. Treat ids as a stable contract, because saved custom hotkeys use them as keys. +- `hotkey` is the default: a chord, a sequence, or `null` for an action without a default. An unbound app hotkey registers nothing until the user assigns one. +- `options` describe the hotkey itself: `conflictBehavior`, `eventType`, `ignoreInputs`, `preventDefault`, `stopPropagation`, plus `requireReset` for a chord default or `timeout` for a sequence default. Options that don't match the default's kind are type errors. Pass `target` and `enabled` where you register the hotkey. +- `context` labels where a hotkey applies, such as `'editor'`. App hotkeys with different contexts may share a hotkey. It is used only for conflict checks and does not affect matching. + +The `Register` augmentation types `useAppHotkeys()`, the ids accepted by `useCustomHotkeys()`, and the `CustomHotkeys` type. + +## Provide app hotkeys + +Pass the declarations to `HotkeysProvider`: + +```tsx +import { HotkeysProvider } from '@tanstack/preact-hotkeys' +import { appHotkeys } from './app-hotkeys' + +function Root() { + return ( + + + + ) +} +``` + +## Register app hotkeys + +Pass an app hotkey wherever you pass a hotkey. Read it from `useAppHotkeys()`, or import it directly: + +```tsx +import { useAppHotkeys, useHotkey } from '@tanstack/preact-hotkeys' + +function Editor() { + const hotkeys = useAppHotkeys() + + useHotkey(hotkeys['doc.save'], () => save(), { enabled: isDirty }) + useHotkey(hotkeys['nav.home'], () => navigate({ to: '/' })) +} +``` + +Both forms use the provider's effective hotkey for that id, so a user's custom hotkey applies even to a direct import. Without a provider, the declared default is used. + +The registration takes `meta` and `options` from the declaration. Options merge in this order: provider `defaultOptions`, then declared options, then call-site options. `meta` comes only from the declaration, so the cheat sheet and the registration cannot disagree. Call-site options are typed by the declared default's kind, so `timeout` is accepted for `nav.home` but not for `doc.save`. + +`useHotkeys` accepts app hotkeys in the same field: + +```tsx +useHotkeys([ + { hotkey: hotkeys['doc.save'], callback: save }, + { hotkey: 'Escape', callback: close }, +]) +``` + +When the user changes a hotkey, registered app hotkeys move to the new hotkey immediately, even if it changes from a chord to a sequence. + +## Build a cheat sheet + +`useAppHotkeyList` returns every app hotkey in declaration order, with its live status: + +| Status | Meaning | +| --- | --- | +| `'active'` | At least one enabled registration | +| `'disabled'` | Registered, but every registration has `enabled: false` | +| `'inactive'` | Has a hotkey, but nothing has registered it | +| `'unbound'` | No hotkey assigned | + +```tsx +import { formatForDisplay, useAppHotkeyList } from '@tanstack/preact-hotkeys' + +function CheatSheet() { + const items = useAppHotkeyList() + + return ( +
      + {items.map(({ appHotkey, status, isCustom }) => ( +
    • + {appHotkey.meta?.name}:{' '} + {appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned'} + {isCustom && ' (custom)'} +
    • + ))} +
    + ) +} +``` + +An app hotkey registered by many components, such as one per list row, appears once. Its `registrations` array holds each live registration. Devtools also list declared app hotkeys that are not registered. + +## Customize and persist hotkeys + +TanStack Hotkeys does not store anything. Pass the user's saved custom hotkeys to the provider and save them when they change: + +```tsx +import { HotkeysProvider } from '@tanstack/preact-hotkeys' +import type { CustomHotkeys } from '@tanstack/preact-hotkeys' + +const saved: CustomHotkeys = JSON.parse(localStorage.getItem('hotkeys') ?? '{}') + +function Root() { + return ( + + localStorage.setItem('hotkeys', JSON.stringify(custom)) + } + > + + + ) +} +``` + +- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them. +- The provider reads `customHotkeys` once and owns the state afterwards. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning. +- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile. + +Recorded hotkeys use `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others. + +### Change hotkeys + +`useCustomHotkeys` reads and changes the user's custom hotkeys: + +```tsx +import { + formatForDisplay, + useAppHotkeys, + useCustomHotkeys, + useHotkeyRecorder, +} from '@tanstack/preact-hotkeys' +import type { AppHotkeyId } from '@tanstack/preact-hotkeys' + +function ShortcutRow({ id }: { id: AppHotkeyId }) { + const appHotkey = useAppHotkeys()[id] + const { setCustomHotkey, resetCustomHotkey, findConflicts } = useCustomHotkeys() + + const recorder = useHotkeyRecorder({ + maxSteps: 3, + validate: (hotkey) => { + const conflicts = findConflicts(id, hotkey) + return conflicts.length === 0 || `Used by ${conflicts[0]?.meta?.name}` + }, + onRecord: (hotkey) => setCustomHotkey(id, hotkey), + onClear: () => setCustomHotkey(id, null), + }) + + return ( +
    + {appHotkey.meta?.name}:{' '} + {appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned'} + + +
    + ) +} +``` + +| Member | Description | +| --- | --- | +| `customHotkeys` | The hotkeys that differ from the defaults | +| `setCustomHotkey(id, hotkey)` | Sets a hotkey, or unbinds with `null`. Returns conflicting app hotkeys; when there are any, nothing changes. | +| `findConflicts(id, hotkey)` | App hotkeys that would collide if `id` used `hotkey` | +| `resetCustomHotkey(id)` | Restores one default | +| `resetCustomHotkeys()` | Restores every default | + +Two app hotkeys conflict when their contexts match or either has no `context`, and their hotkeys collide: equal chords, or one sequence starts with the other, including a sequence whose first step equals a chord. Conflicts are checked across every declaration, whether or not it is registered. + +Each app hotkey's `defaultHotkey` holds its declared default, and `isCustom` in `useAppHotkeyList` reports whether the effective hotkey differs from it. + +## Without Preact + +The provider creates an `AppHotkeysStore` from `@tanstack/hotkeys`. Outside Preact, create the store yourself and publish it for devtools with `publishAppHotkeysStore`, or use `provideAppHotkeys` to do both. diff --git a/docs/framework/preact/guides/formatting-display.md b/docs/framework/preact/guides/formatting-display.md index fe272752..186e8d86 100644 --- a/docs/framework/preact/guides/formatting-display.md +++ b/docs/framework/preact/guides/formatting-display.md @@ -3,7 +3,7 @@ title: Formatting & Display Guide id: formatting-display --- -Use `formatForDisplay` whenever a binding appears in a menu, button, hint, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, and parsed bindings. Store the original binding and format it at render time: the display label is not a registration string. +Use `formatForDisplay` whenever a hotkey appears in a menu, button, hint, cheat sheet, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, parsed bindings, and sequences. Store the original binding and format it at render time: the display label is not a registration string. ## Format a binding @@ -32,16 +32,18 @@ const parts = formatForDisplay(binding, { platform: 'mac', parts: true }) With `parts` omitted or false, the result is a string. A runtime boolean returns `string | string[]`. Parts preserve literal plus keys and ignore `separatorToken`. -Sequences are arrays of bindings. Format their steps individually: +## Format sequences -```ts -import type { HotkeySequence } from '@tanstack/preact-hotkeys' +Pass a sequence to format each step and join the steps with `sequenceSeparator`, a space by default: -const sequence: HotkeySequence = ['Mod+[KeyK]', 'C'] -const label = sequence.map((step) => formatForDisplay(step)).join(' → ') +```ts +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac' }) // '⌘ K C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' → ' }) // 'G → G' +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac', parts: true }) +// [['⌘', 'K'], ['C']] — one array of keycaps per step ``` -`formatHotkeySequence` only joins stored strings with spaces. It intentionally retains brackets and code names, so use the code above for user-facing labels. +With `parts: true`, a sequence returns one array per step, so you can render each step as a group of keycaps. ## Choose symbols and separators @@ -63,7 +65,7 @@ formatForDisplay('Control++', { }) // 'Ctrl · +' ``` -An empty separator joins labels directly. `undefined` or `null` uses the platform default. `formatWithLabels(binding, options)` is the shorthand for `formatForDisplay` with `useSymbols: false`. +An empty separator joins labels directly. `undefined` or `null` uses the platform default. `separatorToken` applies within a chord; `sequenceSeparator` applies between steps. ## Supply layout labels @@ -88,6 +90,10 @@ formatForDisplay('Mod+[KeyQ]', { The precedence is `keyLabels`, then a layout entry, then the fallback label. Layout entries receive normal letter casing and key symbols; missing or empty entries fall back. Explicit labels are final. All of these options affect display only. +## Compare hotkeys + +`areHotkeysEqual(a, b)` compares chords or sequences after resolving modifiers, so `Mod+S` equals `Meta+S` on macOS. Sequences compare step by step. `isHotkeySequence` narrows a `Hotkey` to a sequence, and `simplifyHotkey` turns a one-step sequence into its chord. + ## Parse and store bindings `parseHotkey` returns either a logical `key` or a physical `code`, plus resolved modifier flags. Narrow the union before inspecting the identity: @@ -105,6 +111,6 @@ formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S' Parsed modifiers are already resolved. To display a portable `Mod` binding on another platform, serialize with the original platform first, as above. `normalizeRegisterableHotkey` accepts strings or raw objects and preserves the logical/physical distinction. Do not store display labels or put a bracketed code in a logical `key` field. -Use `validateHotkey` when accepting strings from an external source. It returns `valid`, `errors`, and `warnings`; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). +Use `validateHotkey` when accepting a chord or sequence from an external source. It returns `valid`, `errors`, and `warnings`, and names the failing step of a sequence; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). Try these options together in the [vanilla formatter playground](../../../framework/vanilla/examples/formatForDisplay). diff --git a/docs/framework/preact/guides/hotkey-recording.md b/docs/framework/preact/guides/hotkey-recording.md index 56fdcfba..cd0535bf 100644 --- a/docs/framework/preact/guides/hotkey-recording.md +++ b/docs/framework/preact/guides/hotkey-recording.md @@ -3,11 +3,11 @@ title: Hotkey Recording Guide id: hotkey-recording --- -TanStack Hotkeys includes the `useHotkeyRecorder` hook for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the desired key combination, the same way system preferences or IDE shortcut editors work. +The `useHotkeyRecorder` hook is for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the key combination they want, the same way system preferences or IDE shortcut editors work. By default it records one chord; set `maxSteps` to let users record a [sequence](./sequences) too. Recorders default to physical codes: recording a shortcut stores a string such as `Mod+[KeyS]`. Pass it directly to your hotkey registration and use `formatForDisplay` for the label. Set `recordBy: 'key'` when you intentionally want the produced character instead. -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. +TanStack Hotkeys automatically suppresses registered hotkey callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. ## Basic usage @@ -46,10 +46,12 @@ The `useHotkeyRecorder` hook returns an object with: | Property | Type | Description | |----------|------|-------------| | `isRecording` | `boolean` | Whether the recorder is currently listening for key presses | -| `recordedHotkey` | `Hotkey \| null` | The last recorded hotkey string, or `null` if nothing recorded | +| `steps` | `HotkeySequence` | Chords captured so far in a multi-step recording | +| `recordedHotkey` | `Hotkey \| null` | The last recorded hotkey, or `null` if nothing recorded | | `startRecording` | `() => void` | Start listening for key presses | -| `stopRecording` | `() => void` | Stop listening and reset recorder state without calling `onRecord` | -| `cancelRecording` | `() => void` | Stop listening and discard any recorded hotkey | +| `stopRecording` | `() => void` | Stop listening without calling `onRecord` or `onCancel` | +| `cancelRecording` | `() => void` | Stop listening and call `onCancel` | +| `commitRecording` | `() => void` | Commit the current `steps` now (multi-step recordings) | ## Options @@ -70,7 +72,7 @@ useHotkeyRecorder({ ### `onRecord` -Called when the user presses a valid key combination (a modifier + a non-modifier key, or a single non-modifier key). Receives the recorded `Hotkey` string. +Called when a recording commits. With the default `maxSteps: 1`, it receives a chord string such as `'Mod+[KeyS]'` as soon as the user presses a valid combination (a modifier + a non-modifier key, or a single non-modifier key). With a larger `maxSteps`, it receives a chord when one step was recorded, or a sequence otherwise. ### `onCancel` @@ -78,7 +80,7 @@ Called when recording is cancelled (either by pressing Escape or calling `cancel ### `onClear` -Called when the recorded hotkey is cleared (by pressing Backspace or Delete during recording). +Called when the recording is cleared: Backspace or Delete with no modifiers and no recorded steps. Clearing calls only `onClear`; your app decides whether that removes a hotkey or restores a default. ### Global defaults via provider @@ -91,6 +93,7 @@ import { HotkeysProvider } from '@tanstack/preact-hotkeys' defaultOptions={{ hotkeyRecorder: { onCancel: () => console.log('Recording cancelled'), + idleTimeoutMs: 2000, // applies to multi-step recorders }, }} > @@ -98,21 +101,61 @@ import { HotkeysProvider } from '@tanstack/preact-hotkeys' ``` +## Recording sequences + +Set `maxSteps` above 1 to record up to that many chords. Each chord adds a step, and the recording commits when the user presses Enter, when it reaches `maxSteps`, or after `idleTimeoutMs` without a new step: + +```tsx +import { useHotkeyRecorder, formatForDisplay } from '@tanstack/preact-hotkeys' + +function SequenceRecorder() { + const recorder = useHotkeyRecorder({ + maxSteps: 3, + onRecord: (hotkey) => { + console.log('Recorded:', hotkey) // 'Mod+[KeyS]' or ['[KeyG]', '[KeyG]'] + }, + }) + + return ( + + ) +} +``` + +A one-step recording commits as a chord, so the result can always be passed straight to `useHotkey`. Use `maxSteps: Infinity` for no limit. + +| Option | Description | +| --- | --- | +| `maxSteps` | Maximum chords to record. Reaching it commits. Default `1`. | +| `commitKeys` | `'enter'` (default) commits on plain Enter. With `'none'`, plain Enter can be recorded as a step, and only `commitRecording()`, `idleTimeoutMs`, or `maxSteps` commit. | +| `idleTimeoutMs` | Commit automatically after this many milliseconds without a new step. The timer does not run before the first chord. | + +`commitKeys` and `idleTimeoutMs` are only accepted when `maxSteps` is set. With `maxSteps` above 1, `onRecord` and `validate` receive `Hotkey` (a chord or a sequence) instead of a chord. + ## Recording behavior -The recorder has specific behavior for different keys: +Different keys mean different things to the recorder: | Key | Behavior | |-----|----------| | Modifier only (Shift, Ctrl, etc.) | Waits for a non-modifier key; modifier-only presses don't complete a recording | | Modifier + key (e.g., Ctrl+S) | Records the full combination | | Single key (e.g., Escape, F1) | Records the single key | +| Enter (multi-step, `commitKeys: 'enter'`) | Commits the recorded steps | | Escape | Cancels the recording | -| Backspace / Delete | Clears the currently recorded hotkey | +| Backspace / Delete | Removes the last step; with no steps, clears and calls `onClear` | + +Repeats and IME composition never add a step. ### `ignoreInputs` -The `HotkeyRecorderOptions` supports an `ignoreInputs` option (defaults to `true`). When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements. Keystrokes pass through to the input as usual. Pressing Escape still cancels recording even when focused on an input. Set `ignoreInputs: false` if you want the recorder to capture keys from within input elements. +`HotkeyRecorderOptions` supports an `ignoreInputs` option, which defaults to `true`. When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Escape still cancels recording even while an input is focused. Set `ignoreInputs: false` to let the recorder capture keys from within input elements. ```tsx useHotkeyRecorder({ @@ -123,11 +166,11 @@ useHotkeyRecorder({ ### Mod auto-conversion -Recorded hotkeys automatically use the portable `Mod` format. If a user on macOS presses Command+S, the recorded hotkey is `Mod+[KeyS]` rather than `Meta+[KeyS]`, so the shortcut keeps working when it syncs to a machine on another OS. +Recorded hotkeys use the portable `Mod` format. If a user on macOS presses Command+S, the recorded hotkey is `Mod+[KeyS]` rather than `Meta+[KeyS]`, so the primary modifier becomes Control if the binding is later used on Windows. The recorded physical position stays the same. ## Validation and conflicts -Both recorder APIs accept `detectConflicts`, `validate`, and `onReject`. For a single-hotkey recorder, these options can be supplied alongside `onRecord`: +The recorder accepts `detectConflicts`, `validate`, and `onReject` alongside `onRecord`: ```ts const options = { @@ -146,66 +189,69 @@ const options = { } ``` -`validate` returns true to accept, or false/a message to reject. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the candidate when available, and conflicting registration views for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. +`validate(hotkey, context)` returns true to accept, or false/a message to reject. The context holds the last chord's `event` and `parsedHotkey`, plus `events` and `parsedSequence` for every step. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the rejected `hotkey` when available, and conflicting registrations for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. A rejected multi-step recording keeps its steps so the user can edit them with Backspace. -`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a binding. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Both single bindings and sequence prefixes are checked. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can affect actual dispatch. +`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a binding. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Chords and sequence prefixes are checked: a recorded chord conflicts with a sequence that starts with it, and a recorded sequence conflicts with a registered chord equal to its first step. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can affect actual dispatch. -For checks outside recording, use `findHotkeyConflicts(bindingOrSequence, options)`. Without source `events`, it compares binding identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. +For checks outside recording, use `findHotkeyConflicts(hotkey, options)`. Each result's `registration` is a [registration view](./hotkeys#introspecting-registrations). Without source `events`, it compares binding identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. ## Building a shortcut settings UI -Here's a more complete example of a shortcut customization panel: +For an app-wide settings screen, declare the hotkeys with [app hotkeys](./app-hotkeys) and save the recorder's result with `setCustomHotkey`. That keeps cheat sheets, conflicts, and persistence in one place. + +For a single component, ordinary state is enough: ```tsx import { useState } from 'preact/hooks' import { - useHotkey, + useHotkeys, useHotkeyRecorder, formatForDisplay, } from '@tanstack/preact-hotkeys' import type { Hotkey } from '@tanstack/preact-hotkeys' +const actions = { save, undo, search } + function ShortcutSettings() { - const [shortcuts, setShortcuts] = useState>({ + const [shortcuts, setShortcuts] = useState>({ save: 'Mod+S', undo: 'Mod+Z', - search: 'Mod+K', + search: ['G', 'S'], }) - - const [editingAction, setEditingAction] = useState(null) + const [editing, setEditing] = useState(null) const recorder = useHotkeyRecorder({ + maxSteps: 2, onRecord: (hotkey) => { - if (editingAction) { - setShortcuts((prev) => ({ ...prev, [editingAction]: hotkey })) - setEditingAction(null) - } + if (editing) setShortcuts((prev) => ({ ...prev, [editing]: hotkey })) + setEditing(null) }, - onCancel: () => setEditingAction(null), + onCancel: () => setEditing(null), }) - // Register the actual hotkeys with their current bindings - useHotkey(shortcuts.save, () => save()) - useHotkey(shortcuts.undo, () => undo()) - useHotkey(shortcuts.search, () => openSearch()) + // Register the actual hotkeys with their current values + useHotkeys( + Object.entries(shortcuts).map(([action, hotkey]) => ({ + hotkey, + callback: actions[action as keyof typeof actions], + })), + ) return (
    -

    Keyboard Shortcuts

    {Object.entries(shortcuts).map(([action, hotkey]) => ( -
    - {action} - -
    + ))}
    ) @@ -214,4 +260,4 @@ function ShortcutSettings() { ## Under the hood -The `useHotkeyRecorder` hook creates a `HotkeyRecorder` class instance and subscribes to its reactive state via `@tanstack/preact-store`. The class manages its own keyboard event listeners and state, and the hook handles cleanup on unmount. +The `useHotkeyRecorder` hook creates a `HotkeyRecorder` class instance from `@tanstack/hotkeys` and subscribes to its reactive state via `@tanstack/preact-store`. The class manages its own keyboard event listeners and state, and the hook handles cleanup on unmount. diff --git a/docs/framework/preact/guides/hotkeys.md b/docs/framework/preact/guides/hotkeys.md index 3fa82c50..f323b033 100644 --- a/docs/framework/preact/guides/hotkeys.md +++ b/docs/framework/preact/guides/hotkeys.md @@ -5,6 +5,8 @@ id: hotkeys The `useHotkey` hook is the primary way to register keyboard shortcuts in Preact applications. It wraps the singleton `HotkeyManager` with automatic lifecycle management, stale-closure prevention, and Preact ref support. +A hotkey is either a **chord**, keys pressed together such as `'Mod+S'`, or a **sequence**, chords pressed one after another such as `['G', 'G']`. Every API in this guide accepts both. Sequence-specific behavior is covered in the [Sequences Guide](./sequences). To declare hotkeys up front and let users customize them, see the [App Hotkeys Guide](./app-hotkeys). + ## Logical keys and physical positions Use a logical binding when the shortcut should follow the character on the active layout. Use a physical binding when it should follow a keyboard position: @@ -45,6 +47,15 @@ useHotkey('Mod+S', (event, context) => { }) ``` +Pass an array of chords to register a sequence with the same hook: + +```tsx +useHotkey(['G', 'G'], () => scrollToTop()) +useHotkey(['Mod+K', 'Mod+C'], () => commentSelection()) +``` + +A one-step array such as `['Mod+S']` registers the chord `'Mod+S'`. + You can pass a hotkey as a string or as a `RawHotkey` object (modifier booleans optional). Use `mod` for cross-platform shortcuts (Command on Mac, Control elsewhere): ```tsx @@ -68,7 +79,7 @@ Property getters are read when the hook runs. They do not subscribe to external ## Default options -When you register a hotkey without passing options, or when you omit specific options, the following defaults apply: +When you register a hotkey without options, or omit specific ones, these defaults apply: ```tsx useHotkey('Mod+S', callback, { @@ -76,7 +87,8 @@ useHotkey('Mod+S', callback, { preventDefault: true, stopPropagation: true, eventType: 'keydown', - requireReset: false, + requireReset: false, // chords only + timeout: 1000, // sequences only ignoreInputs: undefined, // smart default: false for Mod+S, true for single keys target: document, platform: undefined, // auto-detected @@ -86,26 +98,38 @@ useHotkey('Mod+S', callback, { ### Why these defaults? -Most hotkey registrations are meant to override default browser behavior. `Mod+S` should save your document, not open the browser's "Save Page" dialog. So `preventDefault` and `stopPropagation` are `true` by default, and you opt out per hotkey when you actually want the browser behavior. +Most hotkey registrations exist to override the browser. When you bind `Mod+S` to save a document, you don't want the browser's "Save Page" dialog too. So `preventDefault` and `stopPropagation` are `true` by default, and you opt out per hotkey when you actually want the browser behavior. + +#### Smart input handling with `ignoreInputs` + +By default, `Ctrl`/`Meta` shortcuts (like `Mod+S`) and `Escape` fire even while focus is inside a text field or textarea, so save and close work wherever the user happens to be. Single keys and `Shift`/`Alt` combos are ignored inside non-button inputs, because those are just typing. Button-type inputs (`type="button"`, `"submit"`, `"reset"`) don't block any hotkeys. + +#### Hotkey conflicts and `conflictBehavior` -#### Smart input handling: `ignoreInputs` +If you register a hotkey that's already registered somewhere else in your app, the library logs a warning by default (`conflictBehavior: 'warn'`). That surfaces accidental duplicate bindings during development, before they reach production. -By default, hotkeys with `Ctrl`/`Meta` modifiers (like `Mod+S`) and the `Escape` key fire even when focus is inside input elements (such as text fields or text areas), and when focus is on button-type inputs (`type="button"`, `"submit"`, or `"reset"`). Shortcuts like save or close keep working wherever the user is focused. Single key shortcuts, and those using only `Shift`/`Alt`, are ignored within non-button inputs so they don't interfere with normal typing. +### Chord and sequence options -#### Hotkey conflicts: `conflictBehavior` +Most options apply to both kinds. `requireReset` applies only to chords and `timeout` only to sequences. The option types follow the hotkey you pass, so TypeScript rejects `timeout` on a chord and `requireReset` on a sequence: -When you attempt to register a hotkey that is already registered (possibly in another part of your app), the library logs a warning by default using the `conflictBehavior: 'warn'` setting. This helps you catch accidental duplicate bindings during development so they can be resolved before reaching production. +```tsx +useHotkey('Escape', close, { requireReset: true }) // OK +useHotkey(['G', 'G'], scrollToTop, { timeout: 500 }) // OK +useHotkey('Escape', close, { timeout: 500 }) // Type error +``` + +When the kind is not known statically, such as a `Hotkey` loaded from settings, only the shared options are accepted. ### Global defaults via provider -You can change the default options for all `useHotkey` calls in your app by wrapping your component tree with `HotkeysProvider`. Per-hook options override the provider defaults. +You can change the default options for all `useHotkey` and `useHotkeys` calls in your app by wrapping your component tree with `HotkeysProvider`. Per-hook options will override the provider defaults. Defaults apply only to the kinds that support them, so a default `timeout` affects sequences and a default `requireReset` affects chords. ```tsx import { HotkeysProvider } from '@tanstack/preact-hotkeys' @@ -118,7 +142,7 @@ import { HotkeysProvider } from '@tanstack/preact-hotkeys' Controls whether the hotkey is active. Defaults to `true`. -Disabled hotkeys remain registered in the manager and stay visible in devtools; only execution is suppressed. Hooks update `enabled` on the existing registration instead of unregistering and re-registering. +Disabled hotkeys stay registered in the manager and visible in devtools; only execution is suppressed. The hook updates `enabled` on the existing registration instead of unregistering and re-registering. ```tsx const [isEditing, setIsEditing] = useState(false) @@ -162,7 +186,7 @@ useHotkey('Shift', () => deactivateMode(), { eventType: 'keyup' }) ### `requireReset` -When `true`, the hotkey only fires once per key press. The key must be released and pressed again to fire again. Defaults to `false`. +When `true`, a chord fires only once per key press. The key must be released and pressed again to fire again. Defaults to `false`. Sequences ignore automatic repeats already, so this option applies only to chords. ```tsx // Only fires once per Escape press, not on key repeat @@ -171,7 +195,7 @@ useHotkey('Escape', () => closePanel(), { requireReset: true }) ### `ignoreInputs` -When `true`, the hotkey doesn't fire when the user is focused on a text input, textarea, select, or contentEditable element. Button-type inputs (`type="button"`, `"submit"`, `"reset"`) are not ignored, so shortcuts like Mod+S work when the user has tabbed to a form button. When unset, a smart default applies: `Ctrl`/`Meta` shortcuts and `Escape` fire in inputs; single keys and `Shift`/`Alt` combos are ignored. +When `true`, the hotkey will not fire when the user is focused on a text input, textarea, select, or contentEditable element. Button-type inputs (`type="button"`, `"submit"`, `"reset"`) are not ignored, so shortcuts like Mod+S work when the user has tabbed to a form button. When unset, a smart default applies: `Ctrl`/`Meta` shortcuts and `Escape` fire in inputs; single keys and `Shift`/`Alt` combos are ignored. ```tsx // Single key - ignored in inputs by default (smart default) @@ -195,7 +219,7 @@ The DOM element to attach the event listener to. Defaults to `document`. Can be import { useRef } from 'preact/hooks' function Panel() { - const panelRef = useRef(null) + const panelRef = useRef(null) // Only listens for events on this specific element useHotkey('Escape', () => closePanel(), { target: panelRef }) @@ -224,6 +248,8 @@ Controls what happens when you register a hotkey that's already registered. Opti useHotkey('Mod+S', () => save(), { conflictBehavior: 'replace' }) ``` +Duplicate detection compares resolved hotkeys on the same target, so `Mod+S` and `Meta+S` conflict on macOS. Two sequences conflict when their steps are equal; a shared prefix alone is not a duplicate. + ### `platform` Override the auto-detected platform. Useful for testing or for applications that need to force a specific platform behavior. @@ -264,7 +290,7 @@ function TemporaryPanel() { ## Registering multiple hotkeys -When you need to register several hotkeys at once, or a dynamic, variable-length list, use the `useHotkeys` (plural) hook instead of calling `useHotkey` multiple times. This matters when the number of shortcuts is not known at compile time, since calling hooks conditionally or in loops violates the rules of hooks. +To register several hotkeys at once, or a dynamic list whose length isn't known at compile time, use the `useHotkeys` (plural) hook instead of calling `useHotkey` multiple times. You can't call hooks conditionally or in loops, so a single hook that takes an array is the way to handle a variable number of shortcuts. ```tsx import { useHotkeys } from '@tanstack/preact-hotkeys' @@ -273,14 +299,17 @@ function Editor() { useHotkeys([ { hotkey: 'Mod+S', callback: () => save() }, { hotkey: 'Mod+Z', callback: () => undo() }, + { hotkey: ['G', 'G'], callback: () => scrollToTop(), options: { timeout: 500 } }, { hotkey: 'Escape', callback: () => close() }, ]) } ``` +Each definition's `options` are typed by its `hotkey`, just like `useHotkey`. + ### Common options with per-hotkey overrides -Pass shared options as the second argument. Per-definition options override the common ones: +Pass shared options as the second argument. Per-definition options override the common ones. Options merge in this order: `HotkeysProvider` defaults, then the common options, then each definition's options. A common `timeout` or `requireReset` applies only to the definitions of the matching kind. ```tsx useHotkeys( @@ -308,7 +337,7 @@ function MenuShortcuts({ items }) { } ``` -The hook diffs the array between renders by array index plus the normalized hotkey string, registering new hotkeys and unregistering removed ones automatically. Reordering the array changes that identity, so reordered entries are unregistered and re-registered even if their callback references stay the same. +The hook diffs the array between renders by array index plus the normalized hotkey, registering new hotkeys and unregistering removed ones automatically. Reordering the array changes that identity, so reordered entries are unregistered and re-registered even if their callback references stay the same. ## Metadata (name, description, and group) @@ -338,60 +367,56 @@ Group is descriptive metadata, not an execution scope. A shortcuts panel can gro ## Introspecting registrations -Use the `useHotkeyRegistrations` hook to get a live view of all hotkey and sequence registrations. It works well for shortcut palettes, help dialogs, or devtools. +Use the `useHotkeyRegistrations` hook to get a live view of every registration, chords and sequences together. It's the building block for shortcut palettes and help dialogs. ```tsx import { useHotkeyRegistrations, formatForDisplay } from '@tanstack/preact-hotkeys' function ShortcutPalette() { - const { hotkeys, sequences } = useHotkeyRegistrations() + const registrations = useHotkeyRegistrations() return ( -
    -

    Keyboard Shortcuts

    -
      - {hotkeys.map((reg) => ( -
    • - {formatForDisplay(reg.hotkey)} - {reg.options.meta?.name && — {reg.options.meta.name}} - {reg.options.meta?.description &&

      {reg.options.meta.description}

      } -
    • - ))} -
    - {sequences.length > 0 && ( - <> -

    Sequences

    -
      - {sequences.map((reg) => ( -
    • - {reg.sequence.map((step) => formatForDisplay(step)).join(' → ')} - {reg.options.meta?.name && — {reg.options.meta.name}} -
    • - ))} -
    - - )} -
    +
      + {registrations.map((reg) => ( +
    • + {formatForDisplay(reg.hotkey)} + {reg.options.meta?.name && — {reg.options.meta.name}} + {reg.kind === 'sequence' && ( + + {reg.matchedStepCount}/{reg.hotkey.length} steps + + )} +
    • + ))} +
    ) } ``` -The returned `hotkeys` array contains registration objects with the hotkey string, options (including `meta`), and enabled state. The `sequences` array contains sequence registrations with the same structure. +Each view has an `id`, the `hotkey`, its `options` (including `meta`), its `target`, and a `triggerCount`. Narrow with `kind`: chord views add `parsedHotkey`; sequence views add `parsedSequence` and `matchedStepCount`, the steps matched so far in the current attempt. + +Registrations only exist while their components are mounted. To list hotkeys from routes that have not rendered, declare them with [app hotkeys](./app-hotkeys). ## The hotkey manager -Under the hood, `useHotkey` uses the singleton `HotkeyManager`. You can also access the manager directly if needed: +Under the hood, `useHotkey` uses the singleton `HotkeyManager`, which handles chords and sequences alike. You can access the manager directly if needed: ```tsx import { getHotkeyManager } from '@tanstack/preact-hotkeys' const manager = getHotkeyManager() +// Register outside Preact; returns a handle with setOptions and unregister +const handle = manager.register(['G', 'G'], () => scrollToTop(), { timeout: 500 }) + // Check if a hotkey is registered manager.isRegistered('Mod+S') // Get total number of registrations manager.getRegistrationCount() + +// Reset in-progress sequences +manager.resetSequenceProgress() ``` -The manager attaches event listeners per target element, so only elements that have registered hotkeys receive listeners. That beats a single global listener that has to inspect every keystroke. +The manager attaches event listeners per target element, so only elements with registered hotkeys get listeners. That beats a single global listener that has to inspect every keystroke. diff --git a/docs/framework/preact/guides/key-state-tracking.md b/docs/framework/preact/guides/key-state-tracking.md index 4b4d5a84..7bef856f 100644 --- a/docs/framework/preact/guides/key-state-tracking.md +++ b/docs/framework/preact/guides/key-state-tracking.md @@ -3,7 +3,7 @@ title: Key State Tracking Guide id: key-state-tracking --- -TanStack Hotkeys includes three hooks for tracking the real-time state of keyboard keys. Use them to show modifier state in the UI or to build hold-to-activate features. +TanStack Hotkeys provides three hooks for tracking which keys are held down right now. Use them to show modifier state in the UI or to build hold-to-activate features. ## `useHeldKeys` @@ -29,7 +29,7 @@ The returned array contains key names like `'Shift'`, `'Control'`, `'Meta'`, `'A ## `useHeldKeyCodes` -Returns a reactive object mapping held key names to their physical key codes (`event.code` values). This is useful when you need to distinguish between left and right modifiers. +Returns a reactive object mapping held key names to their physical key codes (`event.code` values). Reach for this when you need to tell left and right modifiers apart. ```tsx import { useHeldKeyCodes } from '@tanstack/preact-hotkeys' @@ -52,7 +52,7 @@ function KeyCodeDisplay() { ## `useKeyHold` -Checks whether a specific key is currently held. This hook is optimized to only trigger re-renders when the specified key's held state changes, not when other keys are pressed or released. +Checks whether a specific key is currently held. The hook only re-renders when that key's held state changes, not when other keys are pressed or released. ```tsx import { useKeyHold } from '@tanstack/preact-hotkeys' @@ -204,15 +204,15 @@ function KeyDebugger() { ## Platform quirks -The underlying `KeyStateTracker` handles several platform-specific issues: +The underlying `KeyStateTracker` handles a few platform-specific issues: ### macOS modifier key behavior -On macOS, when a modifier key is held and a non-modifier key is pressed, the OS sometimes swallows the `keyup` event for the non-modifier key. TanStack Hotkeys detects and handles this automatically so held key state stays accurate. +On macOS, when a modifier key is held and a non-modifier key is pressed, the OS sometimes swallows the `keyup` event for the non-modifier key. TanStack Hotkeys detects and handles this so held key state stays accurate. ### Window blur -When the browser window loses focus, the tracker clears all held keys. This prevents "stuck" keys that would otherwise appear held even after the user tabs away and releases them. +When the browser window loses focus, the tracker clears all held keys. Otherwise a key released after tabbing away would appear "stuck" forever. ## Under the hood @@ -240,4 +240,10 @@ const visible = useHotkeyHint('Alt+Shift+[KeyK]') For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. +A sequence reveals its hint from its first step, and an app hotkey uses its effective hotkey (no hint while unbound): + +```ts +const visible = useHotkeyHint(['Mod+K', 'Mod+C']) // visible while Mod is held +``` + Combine the boolean with the action's enabled state. The helper does not register a shortcut or determine whether its target is focused. The core equivalent is `matchesHeldModifiers(binding, heldKeys, options)`. diff --git a/docs/framework/preact/guides/sequence-recording.md b/docs/framework/preact/guides/sequence-recording.md deleted file mode 100644 index 9f36a2ac..00000000 --- a/docs/framework/preact/guides/sequence-recording.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -title: Sequence Recording Guide -id: sequence-recording ---- - -TanStack Hotkeys includes the `useHotkeySequenceRecorder` hook for building UIs where users record multi-chord sequences (Vim-style shortcuts). Each step is captured like a single hotkey chord. Users finish with Enter by default, or you can use manual commit and optional idle timeout. - -Sequence recording uses `recordBy: 'code'` by default, preserving every step as a physical string such as `['[KeyG]', 'Alt+[KeyS]']`. Set `recordBy: 'key'` for logical characters. The shared rejection and conflict options follow the [hotkey recording guide](./hotkey-recording.md#validation-and-conflicts). - -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Basic usage - -```tsx -import { useHotkeySequenceRecorder, formatForDisplay } from '@tanstack/preact-hotkeys' -import type { HotkeySequence } from '@tanstack/preact-hotkeys' - -function HotkeySequenceRecorder() { - const recorder = useHotkeySequenceRecorder({ - onRecord: (sequence: HotkeySequence) => { - console.log('Recorded:', sequence) - }, - }) - - return ( -
    - - {recorder.isRecording && ( - - )} -
    - ) -} -``` - -## Return value - -| Property | Type | Description | -|----------|------|-------------| -| `isRecording` | `boolean` | Whether the recorder is listening | -| `steps` | `HotkeySequence` | Chords captured in the current session | -| `recordedSequence` | `HotkeySequence \| null` | Last committed sequence | -| `startRecording` | `() => void` | Start a new session | -| `stopRecording` | `() => void` | Stop without calling `onRecord` | -| `cancelRecording` | `() => void` | Stop and call `onCancel` | -| `commitRecording` | `() => void` | Commit current `steps` (no-op if empty) | - -## Options - -`recordBy` defaults to `'code'`; choose `'key'` for logical characters. Repeats and IME composition never append steps. Modifier-only presses wait for a complete chord. - -Core options live on `HotkeySequenceRecorderOptions` from `@tanstack/hotkeys`: - -- `onRecord(sequence)`: called when a nonempty sequence is committed. Clearing calls only `onClear`. -- `onCancel`, `onClear`: same intent as the hotkey recorder. -- `commitKeys`: `'enter'` (default) or `'none'`. With `'none'`, only `commitRecording()` (or `idleTimeoutMs`) finishes recording; plain Enter can be recorded as a chord. -- `commitOnEnter`: when `commitKeys` is `'enter'`, set to `false` to treat Enter as a normal chord (then use `commitRecording()` or idle timeout to finish). -- `idleTimeoutMs`: optional milliseconds of inactivity after the last completed chord to auto-commit. The timer does not run while waiting for the first chord. - -### Provider defaults - -```tsx - - - -``` - -### `ignoreInputs` - -The `HotkeySequenceRecorderOptions` supports an `ignoreInputs` option (defaults to `true`). When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements. Keystrokes pass through to the input as usual. Pressing Escape still cancels recording even when focused on an input. Set `ignoreInputs: false` if you want the recorder to capture keys from within input elements. - -## Validation and conflicts - -Sequence `validate(sequence, { events, parsedSequence })` runs when committing and returns true, false, or a rejection message. `detectConflicts` checks sequence prefixes as well as single bindings. Rejected commits keep recording active with the steps intact so the user can edit them with Backspace. Empty Backspace/Delete clears and emits `onClear` only; removing a nonempty step does not commit. Recorded steps, commit keys, and releases are isolated from application handlers. - -## Behavior - -| Input | Behavior | -|-------|----------| -| Valid chord | Appended to `steps`; listener stays active | -| Enter (no modifiers), `commitKeys: 'enter'`, `steps.length >= 1` | Commits and calls `onRecord` | -| Escape | Cancels; `onCancel` | -| Backspace / Delete (no modifiers) | Removes last step, or if empty runs only `onClear` and stops | - -Recorded chords use portable `Mod` format, same as `HotkeyRecorder`. - -## Under the hood - -`useHotkeySequenceRecorder` wraps the `HotkeySequenceRecorder` class and subscribes to its TanStack Store, same pattern as `useHotkeyRecorder`. diff --git a/docs/framework/preact/guides/sequences.md b/docs/framework/preact/guides/sequences.md index 209fee4e..830c2d5b 100644 --- a/docs/framework/preact/guides/sequences.md +++ b/docs/framework/preact/guides/sequences.md @@ -3,141 +3,106 @@ title: Sequences Guide id: sequences --- -TanStack Hotkeys supports multi-key sequences: shortcuts where you press keys one after another rather than simultaneously. Vim-style navigation, cheat codes, and multi-step commands all work this way. +A sequence is a hotkey made of chords pressed one after another rather than together. Think Vim-style navigation, cheat codes, or multi-step commands. Sequences are ordinary hotkeys: register them with `useHotkey` and `useHotkeys`, list them with `useHotkeyRegistrations`, record them with `useHotkeyRecorder`, and format them with `formatForDisplay`. -Sequence steps use the same string syntax as single hotkeys. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. Display steps with `sequence.map((step) => formatForDisplay(step)).join(' → ')`. +Sequence steps use the same syntax as chords. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. ## Basic usage -Use the `useHotkeySequence` hook to register a key sequence: +Pass an array of chords to `useHotkey`: ```tsx -import { useHotkeySequence } from '@tanstack/preact-hotkeys' +import { useHotkey } from '@tanstack/preact-hotkeys' function App() { // Vim-style: press g then g to scroll to top - useHotkeySequence(['G', 'G'], () => { + useHotkey(['G', 'G'], () => { window.scrollTo({ top: 0, behavior: 'smooth' }) }) } ``` -The first argument is an array of `Hotkey` strings representing each step in the sequence. The user must press them in order within the timeout window. +The user must press the steps in order, each within the timeout window. A one-step array such as `['Escape']` is the chord `'Escape'`. ## Many sequences at once -When you need several sequences, or a dynamic list whose length is not fixed at compile time, use `useHotkeySequences` instead of calling `useHotkeySequence` many times. One hook call keeps you within the rules of hooks while still registering every sequence. +`useHotkeys` registers chords and sequences together, including dynamic lists: ```tsx -import { useHotkeySequences } from '@tanstack/preact-hotkeys' +import { useHotkeys } from '@tanstack/preact-hotkeys' -useHotkeySequences([ - { sequence: ['G', 'G'], callback: () => scrollToTop() }, - { sequence: ['D', 'D'], callback: () => deleteLine(), options: { timeout: 500 } }, +useHotkeys([ + { hotkey: ['G', 'G'], callback: () => scrollToTop() }, + { hotkey: ['D', 'D'], callback: () => deleteLine(), options: { timeout: 500 } }, + { hotkey: 'Mod+S', callback: () => save() }, ]) ``` -Options merge in the same order as `useHotkeys`: `HotkeysProvider` defaults, then the second-argument `commonOptions`, then each definition's `options`. - ## Sequence options -The third argument is an options object: +Sequences accept the same options as chords, except `requireReset`, plus `timeout`: ```tsx -useHotkeySequence(['G', 'G'], callback, { - timeout: 1000, // Time allowed between keys (ms) - enabled: true, // Whether the sequence is active - target: document, // Or a Preact ref for scoped sequences +useHotkey(['G', 'G'], callback, { + timeout: 1000, // Time allowed between steps (ms) + enabled: true, // Whether the sequence is active }) ``` ### `timeout` -The maximum time (in milliseconds) allowed between consecutive key presses. If the user takes longer than this between any two keys, the sequence resets. Defaults to `1000` (1 second). +The maximum time (in milliseconds) allowed between consecutive steps. If the user takes longer than this between any two steps, the sequence resets. Defaults to `1000` (1 second). ```tsx // Fast sequence - user must type quickly -useHotkeySequence(['D', 'D'], () => deleteLine(), { timeout: 500 }) +useHotkey(['D', 'D'], () => deleteLine(), { timeout: 500 }) // Slow sequence - user has more time between keys -useHotkeySequence(['Shift+Z', 'Shift+Z'], () => forceQuit(), { timeout: 2000 }) -``` - -### `enabled` - -Controls whether the sequence is active. Defaults to `true`. - -Disabled sequences remain registered and stay visible in devtools; only execution is suppressed. - -```tsx -const [isVimMode, setIsVimMode] = useState(true) - -useHotkeySequence(['G', 'G'], () => scrollToTop(), { enabled: isVimMode }) +useHotkey(['Shift+Z', 'Shift+Z'], () => forceQuit(), { timeout: 2000 }) ``` -### `target` - -The DOM element to attach the sequence listener to. Defaults to `document`. Can be a Preact ref, DOM element, `document`, or `window` for scoped sequences. - -### Global defaults via provider - -You can set default options for all `useHotkeySequence` calls by wrapping your component tree with `HotkeysProvider`. Per-hook options override the provider defaults. +`timeout` is only accepted when the hotkey is a sequence. Set a default for every sequence with `HotkeysProvider`: ```tsx import { HotkeysProvider } from '@tanstack/preact-hotkeys' - + ``` -### `meta` +### `enabled` and `meta` -Sequences support the same `meta` option as hotkeys. Attach a `name` and `description` for use in shortcut palettes and devtools. +These work exactly as they do for chords. Disabled sequences stay registered and visible in devtools, and `meta` flows to registrations, devtools, and cheat sheets. See the [Hotkeys Guide](./hotkeys#metadata-name-description-and-group). ```tsx -useHotkeySequence(['G', 'G'], () => scrollToTop(), { - meta: { name: 'Go to Top', description: 'Scroll to the top of the page' }, +useHotkey(['G', 'G'], () => scrollToTop(), { + enabled: isVimMode, + meta: { name: 'Go to top', description: 'Scroll to the top of the page' }, }) ``` -See the [Hotkeys Guide](./hotkeys.md#metadata-name--description) for details on declaration merging and introspecting registrations. - ## Sequences with modifiers Each step in a sequence can include modifiers: ```tsx // Ctrl+K followed by Ctrl+C (VS Code-style comment) -useHotkeySequence(['Mod+K', 'Mod+C'], () => { - commentSelection() -}) +useHotkey(['Mod+K', 'Mod+C'], () => commentSelection()) // g then Shift+G (go to bottom, Vim-style) -useHotkeySequence(['G', 'Shift+G'], () => { - scrollToBottom() -}) +useHotkey(['G', 'Shift+G'], () => scrollToBottom()) ``` -## Chained modifier chords - -This example follows physical R and T positions. Brackets retain those positions even when the keys produce different letters. Other sequences can continue using logical characters. +### Chained modifier chords -You can repeat the same modifier across consecutive steps, for example `Shift+R` then `Shift+T`: +You can repeat the same modifier across consecutive steps. This example follows physical R and T positions; brackets retain those positions even when the keys produce different letters: ```tsx -useHotkeySequence(['Shift+[KeyR]', 'Shift+[KeyT]'], () => { - doNextAction() -}) +useHotkey(['Shift+[KeyR]', 'Shift+[KeyT]'], () => doNextAction()) ``` -### Modifier-only keys between steps - -While a sequence is in progress, the manager ignores modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone, with no letter or other key). They do not advance the sequence and they do not reset progress. A user can tap Shift (or hold it) between chords such as `Shift+R` and `Shift+T` without breaking the sequence, similar to Vim-style flows where a modifier may be pressed before the next chord. +While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored. They neither advance the sequence nor reset progress, so a user can tap or hold Shift between `Shift+R` and `Shift+T`. ## Common sequence patterns @@ -145,18 +110,20 @@ While a sequence is in progress, the manager ignores modifier-only keydown event ```tsx function VimNavigation() { - useHotkeySequence(['G', 'G'], () => scrollToTop()) - useHotkeySequence(['G', 'Shift+G'], () => scrollToBottom()) - useHotkeySequence(['D', 'D'], () => deleteLine()) - useHotkeySequence(['D', 'W'], () => deleteWord()) - useHotkeySequence(['C', 'I', 'W'], () => changeInnerWord()) + useHotkeys([ + { hotkey: ['G', 'G'], callback: () => scrollToTop() }, + { hotkey: ['G', 'Shift+G'], callback: () => scrollToBottom() }, + { hotkey: ['D', 'D'], callback: () => deleteLine() }, + { hotkey: ['D', 'W'], callback: () => deleteWord() }, + { hotkey: ['C', 'I', 'W'], callback: () => changeInnerWord() }, + ]) } ``` ### Konami Code ```tsx -useHotkeySequence( +useHotkey( [ 'ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', @@ -169,57 +136,59 @@ useHotkeySequence( ) ``` -### Multi-step commands +## Displaying sequences + +`formatForDisplay` formats each step and joins them with `sequenceSeparator`, a space by default: ```tsx -// Press "h", "e", "l", "p" to open help -useHotkeySequence(['H', 'E', 'L', 'P'], () => openHelp()) +formatForDisplay(['Mod+K', 'Mod+C'], { platform: 'mac' }) // '⌘ K ⌘ C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' then ' }) // 'G then G' ``` -## How sequences work +Registration views report progress for each sequence in `matchedStepCount`, so a help panel can show how far the user has typed. See [Introspecting registrations](./hotkeys#introspecting-registrations). -Both `SequenceManager` and `createSequenceMatcher` ignore modifier-only events, IME composition, and automatic keydown repeats. These events neither advance the sequence nor refresh its timeout: holding G does not complete a two-press G sequence. The manager prefers exact matches over weaker logical-key fallbacks while preserving equally strong matches. +## How sequences work -The `SequenceManager` (singleton) handles all sequence registrations. When a key is pressed: +The `HotkeyManager` tracks progress for every sequence registration. When a key is pressed: -1. It checks if the key matches the next expected step in any registered sequence -2. If it matches, the sequence advances to the next step +1. It checks whether the key matches the next expected step of each sequence +2. If it matches, that sequence advances 3. If the timeout expires between steps, the sequence resets 4. When all steps are completed, the callback fires -5. Modifier-only keydowns are ignored (they neither advance nor reset the sequence) + +Modifier-only events, IME composition, and automatic keydown repeats neither advance a sequence nor refresh its timeout: holding G does not complete a two-press G sequence. Exact matches take priority over weaker logical-key fallbacks. ### Overlapping sequences -Multiple sequences can share the same prefix. The manager tracks progress for each sequence independently: +Multiple sequences can share a prefix. Progress is tracked for each sequence independently: ```tsx -// Both share the 'D' prefix -useHotkeySequence(['D', 'D'], () => deleteLine()) // dd -useHotkeySequence(['D', 'W'], () => deleteWord()) // dw -useHotkeySequence(['D', 'I', 'W'], () => deleteInnerWord()) // diw +useHotkeys([ + { hotkey: ['D', 'D'], callback: () => deleteLine() }, // dd + { hotkey: ['D', 'W'], callback: () => deleteWord() }, // dw + { hotkey: ['D', 'I', 'W'], callback: () => deleteInnerWord() }, // diw +]) ``` -After pressing `D`, the manager waits for the next key to determine which sequence to complete. +After pressing `D`, the manager waits for the next key to determine which sequence completes. ### Conflicting registrations -For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate bindings. `conflictBehavior` applies to equivalent sequences without rewriting their stored strings. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection also checks prefixes and observed physical/logical overlap. +For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate hotkeys. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection and app hotkey conflicts are stricter: there, a sequence also conflicts with a chord equal to its first step and with any sequence it starts. -## The sequence manager +## Standalone matching -Under the hood, `useHotkeySequence` uses the singleton `SequenceManager`. You can also use the core `createSequenceMatcher` function for standalone sequence matching without the singleton: +For sequence matching without the singleton manager, use `createSequenceMatcher`: ```tsx import { createSequenceMatcher } from '@tanstack/preact-hotkeys' -const matcher = createSequenceMatcher(['G', 'G'], { - timeout: 1000, -}) +const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 }) document.addEventListener('keydown', (e) => { if (matcher.match(e)) { console.log('Sequence completed!') } - console.log('Progress:', matcher.getProgress()) // e.g., 1/2 + console.log('Progress:', matcher.getProgress()) // e.g., 1 }) ``` diff --git a/docs/framework/preact/quick-start.md b/docs/framework/preact/quick-start.md new file mode 100644 index 00000000..f553c81e --- /dev/null +++ b/docs/framework/preact/quick-start.md @@ -0,0 +1,220 @@ +--- +title: Quick Start +id: quick-start +--- + +## Installation + +Don't have TanStack Hotkeys installed yet? See the [Installation](../../installation) page for instructions. + +## Your first hotkey + +The `useHotkey` hook is the primary way to register keyboard shortcuts in Preact: + +```tsx +import { useHotkey } from '@tanstack/preact-hotkeys' + +function App() { + useHotkey('Mod+S', () => { + saveDocument() + }) + + return
    Press Cmd+S (Mac) or Ctrl+S (Windows) to save
    +} +``` + +The `Mod` modifier automatically resolves to `Meta` (Command) on macOS and `Control` on Windows/Linux, so your shortcuts work across platforms without extra logic. + +## Common patterns + +### Multiple hotkeys + +Register as many hotkeys as you need. Each `useHotkey` call is independent: + +```tsx +function Editor() { + useHotkey('Mod+S', () => save()) + useHotkey('Mod+Z', () => undo()) + useHotkey('Mod+Shift+Z', () => redo()) + useHotkey('Mod+F', () => openSearch()) + useHotkey('Escape', () => closeDialog()) + + return
    Editor with keyboard shortcuts
    +} +``` + +### Scoped hotkeys with refs + +Attach hotkeys to specific elements instead of the entire document: + +```tsx +import { useRef } from 'preact/hooks' +import { useHotkey } from '@tanstack/preact-hotkeys' + +function Panel() { + const panelRef = useRef(null) + + // This hotkey only fires when the panel (or its children) has focus + useHotkey('Escape', () => closePanel(), { target: panelRef }) + + return ( +
    +

    Press Escape while focused here to close

    +
    + ) +} +``` + +### Conditional hotkeys + +Enable or disable hotkeys based on application state: + +```tsx +function Modal({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) { + useHotkey('Escape', () => onClose(), { enabled: isOpen }) + + if (!isOpen) return null + + return ( +
    +

    Press Escape to close

    +
    + ) +} +``` + +### Multi-key sequences + +Pass an array of chords to register a Vim-style sequence: + +```tsx +import { useHotkey } from '@tanstack/preact-hotkeys' + +function VimStyleApp() { + useHotkey(['G', 'G'], () => scrollToTop()) + useHotkey(['G', 'Shift+G'], () => scrollToBottom()) + + return
    Use gg to go to top, gG to go to bottom
    +} +``` + +### Tracking held keys + +Display modifier key state for power-user UIs: + +```tsx +import { useKeyHold, useHeldKeys } from '@tanstack/preact-hotkeys' + +function StatusBar() { + const isShiftHeld = useKeyHold('Shift') + const heldKeys = useHeldKeys() + + return ( +
    + {isShiftHeld && Shift mode active} + {heldKeys.length > 0 && Keys: {heldKeys.join('+')}} +
    + ) +} +``` + +### Displaying hotkeys in the UI + +Format hotkeys for platform-aware display: + +```tsx +import { useHotkey, formatForDisplay } from '@tanstack/preact-hotkeys' + +function SaveButton() { + useHotkey('Mod+S', () => save()) + + return ( + + ) +} +``` + +## Setting up devtools + +Add the TanStack Devtools to your app to inspect registered hotkeys, view held keys, and test shortcuts: + +```tsx +import { TanStackDevtools } from '@tanstack/preact-devtools' +import { hotkeysDevtoolsPlugin } from '@tanstack/preact-hotkeys-devtools' + +function App() { + return ( +
    + {/* Your app */} + +
    + ) +} +``` + +## Declaring app hotkeys + +To list every hotkey in a cheat sheet, including those on routes that have not rendered, and to let users customize them, declare them once and pass them to `HotkeysProvider`: + +```tsx +import { + HotkeysProvider, + defineAppHotkeys, + useAppHotkeys, + useHotkey, +} from '@tanstack/preact-hotkeys' + +const appHotkeys = defineAppHotkeys({ + 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save' } }, + 'nav.home': { hotkey: ['G', 'H'], meta: { name: 'Go home' } }, +}) + +function Root() { + return ( + + + + ) +} + +function Editor() { + const hotkeys = useAppHotkeys() + useHotkey(hotkeys['doc.save'], () => save()) + return
    Editor
    +} +``` + +See the [App Hotkeys Guide](./guides/app-hotkeys) for cheat sheets and saving custom hotkeys. + +## Default options provider + +`HotkeysProvider` also sets default options for every hotkey hook. Options passed directly to a hook override the provider defaults. + +```tsx +import { HotkeysProvider } from '@tanstack/preact-hotkeys' + +function Root() { + return ( + console.log('Recording cancelled') }, + }} + > + + + ) +} +``` + +## Next steps + +- [Hotkeys Guide](./guides/hotkeys) - Deep dive into `useHotkey` options and patterns +- [Sequences Guide](./guides/sequences) - Multi-key sequence handling +- [App Hotkeys Guide](./guides/app-hotkeys) - Declaring, listing, and customizing every hotkey +- [Hotkey Recording Guide](./guides/hotkey-recording) - Recording chords and sequences in settings UIs +- [Key State Tracking Guide](./guides/key-state-tracking) - Real-time key state monitoring +- [Formatting & Display Guide](./guides/formatting-display) - Platform-aware hotkey formatting diff --git a/docs/framework/preact/reference/functions/HotkeysProvider.md b/docs/framework/preact/reference/functions/HotkeysProvider.md index 2fe89161..f30408cf 100644 --- a/docs/framework/preact/reference/functions/HotkeysProvider.md +++ b/docs/framework/preact/reference/functions/HotkeysProvider.md @@ -4,17 +4,39 @@ title: HotkeysProvider --- ```ts -function HotkeysProvider(__namedParameters): Element; +function HotkeysProvider(__namedParameters): Element; ``` -Defined in: [HotkeysProvider.tsx:31](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L31) +Defined in: [HotkeysProvider.tsx:70](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L70) + +Provides default options, app hotkeys, and the user's custom hotkeys. + +## Type Parameters + +### T + +`T` *extends* `Readonly`\<`Record`\<`string`, `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>\> = `Readonly`\<`Record`\<`string`, `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>\> ## Parameters ### \_\_namedParameters -[`HotkeysProviderProps`](../interfaces/HotkeysProviderProps.md) +[`HotkeysProviderProps`](../interfaces/HotkeysProviderProps.md)\<`T`\> ## Returns `Element` + +## Example + +```tsx + + localStorage.setItem('hotkeys', JSON.stringify(custom)) + } +> + + +``` diff --git a/docs/framework/preact/reference/functions/useAppHotkeyList.md b/docs/framework/preact/reference/functions/useAppHotkeyList.md new file mode 100644 index 00000000..1ced3d75 --- /dev/null +++ b/docs/framework/preact/reference/functions/useAppHotkeyList.md @@ -0,0 +1,36 @@ +--- +id: useAppHotkeyList +title: useAppHotkeyList +--- + +```ts +function useAppHotkeyList(): AppHotkeyListItem>[]; +``` + +Defined in: useAppHotkeys.ts:61 + +Lists every app hotkey with its live status, in declaration order, for +cheat sheets and settings screens. App hotkeys whose components have not +mounted are `'inactive'`; those without a hotkey are `'unbound'`. + +## Returns + +`AppHotkeyListItem`\<`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>[] + +## Example + +```tsx +function CheatSheet() { + const items = useAppHotkeyList() + return ( +
      + {items.map(({ appHotkey, status }) => ( +
    • + {appHotkey.meta?.name}:{' '} + {appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned'} +
    • + ))} +
    + ) +} +``` diff --git a/docs/framework/preact/reference/functions/useAppHotkeys.md b/docs/framework/preact/reference/functions/useAppHotkeys.md new file mode 100644 index 00000000..ec0e0e5e --- /dev/null +++ b/docs/framework/preact/reference/functions/useAppHotkeys.md @@ -0,0 +1,25 @@ +--- +id: useAppHotkeys +title: useAppHotkeys +--- + +```ts +function useAppHotkeys(): RegisteredAppHotkeys; +``` + +Defined in: useAppHotkeys.ts:35 + +Returns the effective app hotkeys from `HotkeysProvider`, typed by +`Register`: each declaration with the user's custom hotkey applied. +Pass an entry to `useHotkey` or `useHotkeys` to register it. + +## Returns + +`RegisteredAppHotkeys` + +## Example + +```tsx +const hotkeys = useAppHotkeys() +useHotkey(hotkeys['doc.save'], () => save()) +``` diff --git a/docs/framework/preact/reference/functions/useCustomHotkeys.md b/docs/framework/preact/reference/functions/useCustomHotkeys.md new file mode 100644 index 00000000..52eefd2e --- /dev/null +++ b/docs/framework/preact/reference/functions/useCustomHotkeys.md @@ -0,0 +1,30 @@ +--- +id: useCustomHotkeys +title: useCustomHotkeys +--- + +```ts +function useCustomHotkeys(): UseCustomHotkeysResult; +``` + +Defined in: useAppHotkeys.ts:108 + +Reads and changes the user's custom hotkeys. Changes apply to every +registered app hotkey immediately and are passed to the provider's +`onCustomHotkeysChange`, ready to save. + +## Returns + +[`UseCustomHotkeysResult`](../interfaces/UseCustomHotkeysResult.md) + +## Example + +```tsx +const { setCustomHotkey, findConflicts } = useCustomHotkeys() +const recorder = useHotkeyRecorder({ + maxSteps: 3, + validate: (hotkey) => + findConflicts('doc.save', hotkey).length === 0 || 'Already in use', + onRecord: (hotkey) => setCustomHotkey('doc.save', hotkey), +}) +``` diff --git a/docs/framework/preact/reference/functions/useDefaultHotkeysOptions.md b/docs/framework/preact/reference/functions/useDefaultHotkeysOptions.md index a0f351bb..c90203ff 100644 --- a/docs/framework/preact/reference/functions/useDefaultHotkeysOptions.md +++ b/docs/framework/preact/reference/functions/useDefaultHotkeysOptions.md @@ -7,7 +7,7 @@ title: useDefaultHotkeysOptions function useDefaultHotkeysOptions(): HotkeysProviderOptions; ``` -Defined in: [HotkeysProvider.tsx:53](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L53) +Defined in: [HotkeysProvider.tsx:119](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L119) ## Returns diff --git a/docs/framework/preact/reference/functions/useHotkey.md b/docs/framework/preact/reference/functions/useHotkey.md index 94e69a2b..af5d5903 100644 --- a/docs/framework/preact/reference/functions/useHotkey.md +++ b/docs/framework/preact/reference/functions/useHotkey.md @@ -3,88 +3,154 @@ id: useHotkey title: useHotkey --- +## Call Signature + ```ts -function useHotkey( +function useHotkey( hotkey, callback, options?): void; ``` -Defined in: [useHotkey.ts:86](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkey.ts#L86) +Defined in: [useHotkey.ts:87](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkey.ts#L87) + +Preact hook for registering a hotkey: a chord such as `'Mod+S'`, a sequence +such as `['G', 'G']`, or an app hotkey from `defineAppHotkeys`. + +Uses the singleton HotkeyManager for efficient event handling. The callback +and options sync on every render, so callbacks that reference Preact state +always see the latest values. `enabled: false` keeps the registration +(visible in devtools) and only suppresses firing. -Preact hook for registering a keyboard hotkey. +Chords accept `requireReset`; sequences accept `timeout`. A one-step +sequence registers as its chord. An app hotkey uses its effective hotkey +from `HotkeysProvider` (including the user's custom hotkey) and its declared +meta and options, and registers nothing while unbound. -Uses the singleton HotkeyManager for efficient event handling. -The callback receives both the keyboard event and a context object -containing the hotkey string and parsed hotkey. +Options merge in this order: provider defaults, app hotkey declaration, +call-site options. -This hook syncs the callback and options on every render to avoid -stale closures. This means -callbacks that reference Preact state will always have access to -the latest values. +### Type Parameters -## Parameters +#### T -### hotkey +`T` *extends* `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\> -`RegisterableHotkey` +### Parameters -The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object (supports `mod` for cross-platform) +#### hotkey -### callback +`T` + +A chord string, RawHotkey object, sequence, or app hotkey + +#### callback `HotkeyCallback` The function to call when the hotkey is pressed -### options? +#### options? -[`UseHotkeyOptions`](../interfaces/UseHotkeyOptions.md) = `{}` +[`UseAppHotkeyOptions`](../type-aliases/UseAppHotkeyOptions.md)\<`T`\> -Options for the hotkey behavior. `enabled: false` keeps the registration (visible in devtools) - and only suppresses firing; the hook updates the existing handle instead of unregistering. +Options for the hotkey behavior -## Returns +### Returns `void` -## Examples +### Examples ```tsx -function SaveButton() { +function Editor() { const [count, setCount] = useState(0) - // Callback always has access to latest count value - useHotkey('Mod+S', (event, { hotkey }) => { - console.log(`Save triggered, count is ${count}`) - handleSave() - }) - - return + useHotkey('Mod+S', () => save(count)) + useHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 }) + useHotkey('Escape', () => close(), { enabled: isOpen }) } ``` ```tsx -function Modal({ isOpen, onClose }) { - // enabled option is synced on every render - useHotkey('Escape', () => { - onClose() - }, { enabled: isOpen }) - - if (!isOpen) return null - return
    ...
    +function Toolbar() { + const hotkeys = useAppHotkeys() + useHotkey(hotkeys['doc.save'], () => save()) } ``` +## Call Signature + +```ts +function useHotkey( + hotkey, + callback, + options?): void; +``` + +Defined in: [useHotkey.ts:92](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkey.ts#L92) + +Preact hook for registering a hotkey: a chord such as `'Mod+S'`, a sequence +such as `['G', 'G']`, or an app hotkey from `defineAppHotkeys`. + +Uses the singleton HotkeyManager for efficient event handling. The callback +and options sync on every render, so callbacks that reference Preact state +always see the latest values. `enabled: false` keeps the registration +(visible in devtools) and only suppresses firing. + +Chords accept `requireReset`; sequences accept `timeout`. A one-step +sequence registers as its chord. An app hotkey uses its effective hotkey +from `HotkeysProvider` (including the user's custom hotkey) and its declared +meta and options, and registers nothing while unbound. + +Options merge in this order: provider defaults, app hotkey declaration, +call-site options. + +### Type Parameters + +#### T + +`T` *extends* `RegisterableHotkey` + +### Parameters + +#### hotkey + +`T` + +A chord string, RawHotkey object, sequence, or app hotkey + +#### callback + +`HotkeyCallback` + +The function to call when the hotkey is pressed + +#### options? + +`AdapterOptionsFor`\<`T`, [`UseHotkeyTarget`](../type-aliases/UseHotkeyTarget.md)\> + +Options for the hotkey behavior + +### Returns + +`void` + +### Examples + ```tsx function Editor() { - const editorRef = useRef(null) + const [count, setCount] = useState(0) - // Scoped to a specific element - useHotkey('Mod+S', () => { - save() - }, { target: editorRef }) + useHotkey('Mod+S', () => save(count)) + useHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 }) + useHotkey('Escape', () => close(), { enabled: isOpen }) +} +``` - return
    ...
    +```tsx +function Toolbar() { + const hotkeys = useAppHotkeys() + useHotkey(hotkeys['doc.save'], () => save()) } ``` diff --git a/docs/framework/preact/reference/functions/useHotkeyHint.md b/docs/framework/preact/reference/functions/useHotkeyHint.md index 94323f33..f08c4b58 100644 --- a/docs/framework/preact/reference/functions/useHotkeyHint.md +++ b/docs/framework/preact/reference/functions/useHotkeyHint.md @@ -7,19 +7,22 @@ title: useHotkeyHint function useHotkeyHint(hotkey, options?): boolean; ``` -Defined in: [useHotkeyHint.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyHint.ts#L12) +Defined in: [useHotkeyHint.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyHint.ts#L24) Returns a boolean indicating whether held modifiers reveal this shortcut. Only visibility changes from held-key updates cause a rerender. Uses the nonempty-subset/AltGr rules of `matchesHeldModifiers`; `exact` requires -every modifier. Combine with the action's enabled state before showing a badge. -This helper does not register a shortcut or check whether its target is focused. +every modifier. A sequence reveals its hint from its first step, and an app +hotkey uses its effective hotkey (false while unbound). Combine with the +action's enabled state before showing a badge. This helper does not register +a shortcut or check whether its target is focused. ## Parameters ### hotkey -`RegisterableHotkey` + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\> ### options? diff --git a/docs/framework/preact/reference/functions/useHotkeyRecorder.md b/docs/framework/preact/reference/functions/useHotkeyRecorder.md index 55773770..5f109fbe 100644 --- a/docs/framework/preact/reference/functions/useHotkeyRecorder.md +++ b/docs/framework/preact/reference/functions/useHotkeyRecorder.md @@ -3,57 +3,112 @@ id: useHotkeyRecorder title: useHotkeyRecorder --- +## Call Signature + +```ts +function useHotkeyRecorder(options): PreactHotkeyRecorder; +``` + +Defined in: [useHotkeyRecorder.ts:68](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L68) + +Preact hook for recording keyboard shortcuts. + +With the default `maxSteps: 1`, the first valid chord is recorded and +`onRecord` receives a chord such as `'Mod+S'`. With a larger `maxSteps`, each +chord adds a step; the recording commits on Enter (unless +`commitKeys: 'none'`), after `idleTimeoutMs`, or when it reaches `maxSteps`, +and `onRecord` receives a chord (one step) or a sequence. + +Escape cancels. Backspace/Delete removes the last step, or calls `onClear` +when nothing is recorded. + +### Parameters + +#### options + +`HotkeyChordRecorderOptions` + +Configuration options for the recorder + +### Returns + +[`PreactHotkeyRecorder`](../interfaces/PreactHotkeyRecorder.md)\<`HotkeyChord`\> + +The recorder state and controls + +### Examples + +```tsx +function ShortcutInput() { + const [shortcut, setShortcut] = useState('Mod+S') + const recorder = useHotkeyRecorder({ onRecord: setShortcut }) + + return ( + + ) +} +``` + +```tsx +const recorder = useHotkeyRecorder({ + maxSteps: 3, + onRecord: (hotkey) => setShortcut(hotkey), // 'Mod+S' or ['G', 'H'] +}) +``` + +## Call Signature + ```ts function useHotkeyRecorder(options): PreactHotkeyRecorder; ``` -Defined in: [useHotkeyRecorder.ts:58](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L58) +Defined in: [useHotkeyRecorder.ts:71](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L71) Preact hook for recording keyboard shortcuts. -This hook 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. +With the default `maxSteps: 1`, the first valid chord is recorded and +`onRecord` receives a chord such as `'Mod+S'`. With a larger `maxSteps`, each +chord adds a step; the recording commits on Enter (unless +`commitKeys: 'none'`), after `idleTimeoutMs`, or when it reaches `maxSteps`, +and `onRecord` receives a chord (one step) or a sequence. + +Escape cancels. Backspace/Delete removes the last step, or calls `onClear` +when nothing is recorded. -## Parameters +### Parameters -### options +#### options -`HotkeyRecorderOptions` +`HotkeySequenceRecorderOptions` Configuration options for the recorder -## Returns +### Returns [`PreactHotkeyRecorder`](../interfaces/PreactHotkeyRecorder.md) -An object with recording state and control functions +The recorder state and controls -## Example +### Examples ```tsx -function ShortcutSettings() { +function ShortcutInput() { const [shortcut, setShortcut] = useState('Mod+S') - - const recorder = useHotkeyRecorder({ - onRecord: (hotkey) => { - setShortcut(hotkey) - }, - onCancel: () => { - console.log('Recording cancelled') - }, - }) + const recorder = useHotkeyRecorder({ onRecord: setShortcut }) return ( -
    - - {recorder.recordedHotkey && ( -
    Recording: {recorder.recordedHotkey}
    - )} -
    + ) } ``` + +```tsx +const recorder = useHotkeyRecorder({ + maxSteps: 3, + onRecord: (hotkey) => setShortcut(hotkey), // 'Mod+S' or ['G', 'H'] +}) +``` diff --git a/docs/framework/preact/reference/functions/useHotkeyRegistrations.md b/docs/framework/preact/reference/functions/useHotkeyRegistrations.md index 6a8c4ab7..2ea5f43f 100644 --- a/docs/framework/preact/reference/functions/useHotkeyRegistrations.md +++ b/docs/framework/preact/reference/functions/useHotkeyRegistrations.md @@ -4,20 +4,37 @@ title: useHotkeyRegistrations --- ```ts -function useHotkeyRegistrations(): HotkeyRegistrationsResult; +function useHotkeyRegistrations(): HotkeyRegistrationView[]; ``` -Defined in: [useHotkeyRegistrations.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRegistrations.ts#L33) +Defined in: [useHotkeyRegistrations.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRegistrations.ts#L31) -Preact hook that reactively reads all hotkey and sequence registrations -from the singleton managers. +Preact hook that reactively reads every hotkey registration, chords and +sequences, from the singleton HotkeyManager. Narrow each view with `kind`. -This is a standalone hook that does NOT require the HotkeysProvider. -It subscribes to both HotkeyManager and SequenceManager stores and -re-renders when registrations change. +This hook does NOT require the HotkeysProvider. It re-renders when +registrations change, including trigger counts and sequence progress. ## Returns -[`HotkeyRegistrationsResult`](../interfaces/HotkeyRegistrationsResult.md) +`HotkeyRegistrationView`[] -Object with `hotkeys` and `sequences` arrays +All registrations, without callbacks + +## Example + +```tsx +function ShortcutPalette() { + const registrations = useHotkeyRegistrations() + + return ( +
      + {registrations.map((reg) => ( +
    • + {reg.options.meta?.name}: {formatForDisplay(reg.hotkey)} +
    • + ))} +
    + ) +} +``` diff --git a/docs/framework/preact/reference/functions/useHotkeySequence.md b/docs/framework/preact/reference/functions/useHotkeySequence.md deleted file mode 100644 index 511d6353..00000000 --- a/docs/framework/preact/reference/functions/useHotkeySequence.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -id: useHotkeySequence -title: useHotkeySequence ---- - -```ts -function useHotkeySequence( - sequence, - callback, - options?): void; -``` - -Defined in: [useHotkeySequence.ts:70](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequence.ts#L70) - -Preact hook for registering a keyboard shortcut sequence (Vim-style). - -This hook allows you to register multi-key sequences like 'g g' or 'd d' -that trigger when the full sequence is pressed within a timeout. - -Each step may include modifiers. You can chain the same modifier across -steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift, -Control, Alt, or Meta pressed alone) are ignored while matching—they do not -advance the sequence or reset progress. - -## Parameters - -### sequence - -`HotkeySequence` - -Array of hotkey strings that form the sequence - -### callback - -`HotkeyCallback` - -Function to call when the sequence is completed - -### options? - -[`UseHotkeySequenceOptions`](../interfaces/UseHotkeySequenceOptions.md) = `{}` - -Options for the sequence behavior. `enabled: false` keeps the registration (visible in devtools) - and only suppresses firing; the hook updates the existing handle instead of unregistering. - -## Returns - -`void` - -## Example - -```tsx -function VimEditor() { - // 'g g' to go to top - useHotkeySequence(['G', 'G'], () => { - scrollToTop() - }) - - // 'd d' to delete line - useHotkeySequence(['D', 'D'], () => { - deleteLine() - }) - - // 'd i w' to delete inner word - useHotkeySequence(['D', 'I', 'W'], () => { - deleteInnerWord() - }, { timeout: 500 }) - - // Same modifier on consecutive steps (bare Shift between chords is ignored) - useHotkeySequence(['Shift+R', 'Shift+T'], () => { - nextAction() - }) - - return
    ...
    -} -``` diff --git a/docs/framework/preact/reference/functions/useHotkeySequenceRecorder.md b/docs/framework/preact/reference/functions/useHotkeySequenceRecorder.md deleted file mode 100644 index 650713a5..00000000 --- a/docs/framework/preact/reference/functions/useHotkeySequenceRecorder.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: useHotkeySequenceRecorder -title: useHotkeySequenceRecorder ---- - -```ts -function useHotkeySequenceRecorder(options): PreactHotkeySequenceRecorder; -``` - -Defined in: [useHotkeySequenceRecorder.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequenceRecorder.ts#L23) - -Preact hook for recording multi-chord sequences (Vim-style shortcuts). - -## Parameters - -### options - -`HotkeySequenceRecorderOptions` - -## Returns - -[`PreactHotkeySequenceRecorder`](../interfaces/PreactHotkeySequenceRecorder.md) diff --git a/docs/framework/preact/reference/functions/useHotkeySequences.md b/docs/framework/preact/reference/functions/useHotkeySequences.md deleted file mode 100644 index 3db3249f..00000000 --- a/docs/framework/preact/reference/functions/useHotkeySequences.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -id: useHotkeySequences -title: useHotkeySequences ---- - -```ts -function useHotkeySequences(definitions, commonOptions?): void; -``` - -Defined in: [useHotkeySequences.ts:68](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequences.ts#L68) - -Preact hook for registering multiple keyboard shortcut sequences at once (Vim-style). - -Uses the singleton SequenceManager. Accepts a dynamic array of definitions so you can -register variable-length lists without violating the rules of hooks. - -Options are merged in this order: -HotkeysProvider defaults < commonOptions < per-definition options - -Callbacks and options are synced on every render to avoid stale closures. - -Definitions with an empty `sequence` are skipped (no registration). - -## Parameters - -### definitions - -[`UseHotkeySequenceDefinition`](../interfaces/UseHotkeySequenceDefinition.md)[] - -Array of sequence definitions to register - -### commonOptions? - -[`UseHotkeySequenceOptions`](../interfaces/UseHotkeySequenceOptions.md) = `{}` - -Shared options applied to all sequences (overridden by per-definition options). - Per-row `enabled: false` still registers that sequence: `SequenceManager` suppresses execution only (the row - stays in the store and appears in TanStack Hotkeys devtools). Toggling `enabled` updates the existing handle - via `setOptions` (no unregister/re-register churn). - -## Returns - -`void` - -## Examples - -```tsx -function VimPalette() { - useHotkeySequences([ - { sequence: ['G', 'G'], callback: () => scrollToTop() }, - { sequence: ['D', 'D'], callback: () => deleteLine() }, - { sequence: ['C', 'I', 'W'], callback: () => changeInnerWord(), options: { timeout: 500 } }, - ]) -} -``` - -```tsx -function DynamicSequences({ items }) { - useHotkeySequences( - items.map((item) => ({ - sequence: item.chords, - callback: item.action, - options: { enabled: item.enabled }, - })), - { preventDefault: true }, - ) -} -``` diff --git a/docs/framework/preact/reference/functions/useHotkeys.md b/docs/framework/preact/reference/functions/useHotkeys.md index f7da223a..b8accdd7 100644 --- a/docs/framework/preact/reference/functions/useHotkeys.md +++ b/docs/framework/preact/reference/functions/useHotkeys.md @@ -7,35 +7,32 @@ title: useHotkeys function useHotkeys(hotkeys, commonOptions?): void; ``` -Defined in: [useHotkeys.ts:73](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeys.ts#L73) +Defined in: [useHotkeys.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeys.ts#L59) -Preact hook for registering multiple keyboard hotkeys at once. +Preact hook for registering several hotkeys at once: chords, sequences, and +app hotkeys. Accepts a dynamic array, so it is safe to use with +variable-length lists without violating the rules of hooks. -Uses the singleton HotkeyManager for efficient event handling. -Accepts a dynamic array of hotkey definitions, making it safe to use -with variable-length lists without violating the rules of hooks. +Options merge in this order: provider defaults, app hotkey declaration, +commonOptions, per-hotkey options. Each option applies only to the kinds +that support it. Callbacks and options sync on every render. -Options are merged in this order: -HotkeysProvider defaults < commonOptions < per-definition options - -Callbacks and options are synced on every render to avoid stale closures. +Per-row `enabled: false` keeps that registration (visible in devtools) and +only suppresses firing. Unbound app hotkeys and empty sequences register nothing. ## Parameters ### hotkeys -[`UseHotkeyDefinition`](../interfaces/UseHotkeyDefinition.md)[] +readonly [`UseHotkeyDefinition`](../type-aliases/UseHotkeyDefinition.md)[] -Array of hotkey definitions to register +Hotkeys to register ### commonOptions? -[`UseHotkeyOptions`](../interfaces/UseHotkeyOptions.md) = `{}` +[`UseHotkeyCommonOptions`](../type-aliases/UseHotkeyCommonOptions.md) = `{}` -Shared options applied to all hotkeys (overridden by per-definition options). - Per-row `enabled: false` still registers that hotkey: `HotkeyManager` suppresses execution only (the row - stays in the store and appears in TanStack Hotkeys devtools). Toggling `enabled` updates the existing handle - via `setOptions` (no unregister/re-register churn). +Options applied to every hotkey (overridden by per-hotkey options) ## Returns @@ -47,7 +44,7 @@ Shared options applied to all hotkeys (overridden by per-definition options). function Editor() { useHotkeys([ { hotkey: 'Mod+S', callback: () => save() }, - { hotkey: 'Mod+Z', callback: () => undo() }, + { hotkey: ['G', 'G'], callback: () => scrollToTop(), options: { timeout: 500 } }, { hotkey: 'Escape', callback: () => close() }, ]) } diff --git a/docs/framework/preact/reference/functions/useHotkeysContext.md b/docs/framework/preact/reference/functions/useHotkeysContext.md index 8a084f1f..07ba0ea3 100644 --- a/docs/framework/preact/reference/functions/useHotkeysContext.md +++ b/docs/framework/preact/reference/functions/useHotkeysContext.md @@ -7,7 +7,7 @@ title: useHotkeysContext function useHotkeysContext(): HotkeysContextValue | null; ``` -Defined in: [HotkeysProvider.tsx:49](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L49) +Defined in: [HotkeysProvider.tsx:115](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L115) ## Returns diff --git a/docs/framework/preact/reference/index.md b/docs/framework/preact/reference/index.md index 4c63b52a..d577c2b4 100644 --- a/docs/framework/preact/reference/index.md +++ b/docs/framework/preact/reference/index.md @@ -5,19 +5,28 @@ title: "@tanstack/preact-hotkeys" ## Interfaces -- [HotkeyRegistrationsResult](interfaces/HotkeyRegistrationsResult.md) - [HotkeysProviderOptions](interfaces/HotkeysProviderOptions.md) - [HotkeysProviderProps](interfaces/HotkeysProviderProps.md) - [PreactHotkeyRecorder](interfaces/PreactHotkeyRecorder.md) -- [PreactHotkeySequenceRecorder](interfaces/PreactHotkeySequenceRecorder.md) -- [UseHotkeyDefinition](interfaces/UseHotkeyDefinition.md) -- [UseHotkeyOptions](interfaces/UseHotkeyOptions.md) -- [UseHotkeySequenceDefinition](interfaces/UseHotkeySequenceDefinition.md) -- [UseHotkeySequenceOptions](interfaces/UseHotkeySequenceOptions.md) +- [UseCustomHotkeysResult](interfaces/UseCustomHotkeysResult.md) + +## Type Aliases + +- [UseAppHotkeyOptions](type-aliases/UseAppHotkeyOptions.md) +- [UseHotkeyChordOptions](type-aliases/UseHotkeyChordOptions.md) +- [UseHotkeyCommonOptions](type-aliases/UseHotkeyCommonOptions.md) +- [UseHotkeyDefinition](type-aliases/UseHotkeyDefinition.md) +- [UseHotkeyOptions](type-aliases/UseHotkeyOptions.md) +- [UseHotkeyOptionsFor](type-aliases/UseHotkeyOptionsFor.md) +- [UseHotkeySequenceOptions](type-aliases/UseHotkeySequenceOptions.md) +- [UseHotkeyTarget](type-aliases/UseHotkeyTarget.md) ## Functions - [HotkeysProvider](functions/HotkeysProvider.md) +- [useAppHotkeyList](functions/useAppHotkeyList.md) +- [useAppHotkeys](functions/useAppHotkeys.md) +- [useCustomHotkeys](functions/useCustomHotkeys.md) - [useDefaultHotkeysOptions](functions/useDefaultHotkeysOptions.md) - [useHeldKeyCodes](functions/useHeldKeyCodes.md) - [useHeldKeys](functions/useHeldKeys.md) @@ -27,7 +36,4 @@ title: "@tanstack/preact-hotkeys" - [useHotkeyRegistrations](functions/useHotkeyRegistrations.md) - [useHotkeys](functions/useHotkeys.md) - [useHotkeysContext](functions/useHotkeysContext.md) -- [useHotkeySequence](functions/useHotkeySequence.md) -- [useHotkeySequenceRecorder](functions/useHotkeySequenceRecorder.md) -- [useHotkeySequences](functions/useHotkeySequences.md) - [useKeyHold](functions/useKeyHold.md) diff --git a/docs/framework/preact/reference/interfaces/HotkeyRegistrationsResult.md b/docs/framework/preact/reference/interfaces/HotkeyRegistrationsResult.md deleted file mode 100644 index 13d3760e..00000000 --- a/docs/framework/preact/reference/interfaces/HotkeyRegistrationsResult.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -id: HotkeyRegistrationsResult -title: HotkeyRegistrationsResult ---- - -Defined in: [useHotkeyRegistrations.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRegistrations.ts#L16) - -Return type for useHotkeyRegistrations. - -## Properties - -### hotkeys - -```ts -hotkeys: HotkeyRegistrationView[]; -``` - -Defined in: [useHotkeyRegistrations.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRegistrations.ts#L18) - -All registered hotkeys (public view, no callbacks) - -*** - -### sequences - -```ts -sequences: SequenceRegistrationView[]; -``` - -Defined in: [useHotkeyRegistrations.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRegistrations.ts#L20) - -All registered sequences diff --git a/docs/framework/preact/reference/interfaces/HotkeysProviderOptions.md b/docs/framework/preact/reference/interfaces/HotkeysProviderOptions.md index 26b9256f..ce5a9e9d 100644 --- a/docs/framework/preact/reference/interfaces/HotkeysProviderOptions.md +++ b/docs/framework/preact/reference/interfaces/HotkeysProviderOptions.md @@ -3,44 +3,29 @@ id: HotkeysProviderOptions title: HotkeysProviderOptions --- -Defined in: [HotkeysProvider.tsx:11](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L11) +Defined in: [HotkeysProvider.tsx:13](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L13) ## Properties ### hotkey? ```ts -optional hotkey?: Partial; +optional hotkey?: UseHotkeyCommonOptions; ``` -Defined in: [HotkeysProvider.tsx:12](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L12) - -*** - -### hotkeyRecorder? - -```ts -optional hotkeyRecorder?: Partial; -``` +Defined in: [HotkeysProvider.tsx:18](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L18) -Defined in: [HotkeysProvider.tsx:13](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L13) +Defaults for every hotkey registered by `useHotkey` and `useHotkeys`. +`requireReset` applies to chords and `timeout` applies to sequences. *** -### hotkeySequence? +### hotkeyRecorder? ```ts -optional hotkeySequence?: Partial; +optional hotkeyRecorder?: Partial>; ``` -Defined in: [HotkeysProvider.tsx:15](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L15) - -*** - -### hotkeySequenceRecorder? - -```ts -optional hotkeySequenceRecorder?: Partial; -``` +Defined in: [HotkeysProvider.tsx:20](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L20) -Defined in: [HotkeysProvider.tsx:14](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L14) +Defaults for every `useHotkeyRecorder`. diff --git a/docs/framework/preact/reference/interfaces/HotkeysProviderProps.md b/docs/framework/preact/reference/interfaces/HotkeysProviderProps.md index a7d183c9..7d98fcd1 100644 --- a/docs/framework/preact/reference/interfaces/HotkeysProviderProps.md +++ b/docs/framework/preact/reference/interfaces/HotkeysProviderProps.md @@ -3,17 +3,49 @@ id: HotkeysProviderProps title: HotkeysProviderProps --- -Defined in: [HotkeysProvider.tsx:24](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L24) +Defined in: [HotkeysProvider.tsx:35](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L35) + +## Type Parameters + +### T + +`T` *extends* `AppHotkeys` = `AppHotkeys` ## Properties +### appHotkeys? + +```ts +optional appHotkeys?: T; +``` + +Defined in: [HotkeysProvider.tsx:41](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L41) + +Every app hotkey, from `defineAppHotkeys`. Listed by `useAppHotkeyList` +whether or not their components have mounted. + +*** + ### children ```ts children: ComponentChildren; ``` -Defined in: [HotkeysProvider.tsx:25](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L25) +Defined in: [HotkeysProvider.tsx:36](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L36) + +*** + +### customHotkeys? + +```ts +optional customHotkeys?: CustomHotkeys | null; +``` + +Defined in: [HotkeysProvider.tsx:46](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L46) + +Starting custom hotkeys, such as saved data. Read once; the provider owns +the state afterwards. Invalid entries are ignored with a warning. *** @@ -23,4 +55,26 @@ Defined in: [HotkeysProvider.tsx:25](https://github.com/TanStack/hotkeys/blob/ma optional defaultOptions?: HotkeysProviderOptions; ``` -Defined in: [HotkeysProvider.tsx:26](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L26) +Defined in: [HotkeysProvider.tsx:49](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L49) + +*** + +### onCustomHotkeysChange? + +```ts +optional onCustomHotkeysChange?: (customHotkeys) => void; +``` + +Defined in: [HotkeysProvider.tsx:48](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/HotkeysProvider.tsx#L48) + +Called with the custom hotkeys after every change, ready to save. + +#### Parameters + +##### customHotkeys + +`CustomHotkeys`\<`T`\> + +#### Returns + +`void` diff --git a/docs/framework/preact/reference/interfaces/PreactHotkeyRecorder.md b/docs/framework/preact/reference/interfaces/PreactHotkeyRecorder.md index 14bb23eb..98598090 100644 --- a/docs/framework/preact/reference/interfaces/PreactHotkeyRecorder.md +++ b/docs/framework/preact/reference/interfaces/PreactHotkeyRecorder.md @@ -3,7 +3,13 @@ id: PreactHotkeyRecorder title: PreactHotkeyRecorder --- -Defined in: [useHotkeyRecorder.ts:7](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L7) +Defined in: [useHotkeyRecorder.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L14) + +## Type Parameters + +### THotkey + +`THotkey` *extends* `Hotkey` = `Hotkey` ## Properties @@ -13,9 +19,25 @@ Defined in: [useHotkeyRecorder.ts:7](https://github.com/TanStack/hotkeys/blob/ma cancelRecording: () => void; ``` -Defined in: [useHotkeyRecorder.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L17) +Defined in: [useHotkeyRecorder.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L26) -Cancel recording without saving +Cancel recording and call onCancel + +#### Returns + +`void` + +*** + +### commitRecording + +```ts +commitRecording: () => void; +``` + +Defined in: [useHotkeyRecorder.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L28) + +Commit the recorded steps now (multi-step recordings) #### Returns @@ -29,7 +51,7 @@ Cancel recording without saving isRecording: boolean; ``` -Defined in: [useHotkeyRecorder.ts:9](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L9) +Defined in: [useHotkeyRecorder.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L16) Whether recording is currently active @@ -38,12 +60,12 @@ Whether recording is currently active ### recordedHotkey ```ts -recordedHotkey: Hotkey | null; +recordedHotkey: THotkey | null; ``` -Defined in: [useHotkeyRecorder.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L11) +Defined in: [useHotkeyRecorder.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L20) -The currently recorded hotkey (for live preview) +The last committed hotkey *** @@ -53,7 +75,7 @@ The currently recorded hotkey (for live preview) startRecording: () => void; ``` -Defined in: [useHotkeyRecorder.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L13) +Defined in: [useHotkeyRecorder.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L22) Start recording a new hotkey @@ -63,15 +85,27 @@ Start recording a new hotkey *** +### steps + +```ts +steps: HotkeySequence; +``` + +Defined in: [useHotkeyRecorder.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L18) + +Chords recorded so far in a multi-step recording (for live preview) + +*** + ### stopRecording ```ts stopRecording: () => void; ``` -Defined in: [useHotkeyRecorder.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L15) +Defined in: [useHotkeyRecorder.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeyRecorder.ts#L24) -Stop recording (same as cancel) +Stop recording without calling onCancel #### Returns diff --git a/docs/framework/preact/reference/interfaces/PreactHotkeySequenceRecorder.md b/docs/framework/preact/reference/interfaces/PreactHotkeySequenceRecorder.md deleted file mode 100644 index 277242ab..00000000 --- a/docs/framework/preact/reference/interfaces/PreactHotkeySequenceRecorder.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -id: PreactHotkeySequenceRecorder -title: PreactHotkeySequenceRecorder ---- - -Defined in: [useHotkeySequenceRecorder.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequenceRecorder.ts#L10) - -## Properties - -### cancelRecording - -```ts -cancelRecording: () => void; -``` - -Defined in: [useHotkeySequenceRecorder.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequenceRecorder.ts#L16) - -#### Returns - -`void` - -*** - -### commitRecording - -```ts -commitRecording: () => void; -``` - -Defined in: [useHotkeySequenceRecorder.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequenceRecorder.ts#L17) - -#### Returns - -`void` - -*** - -### isRecording - -```ts -isRecording: boolean; -``` - -Defined in: [useHotkeySequenceRecorder.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequenceRecorder.ts#L11) - -*** - -### recordedSequence - -```ts -recordedSequence: HotkeySequence | null; -``` - -Defined in: [useHotkeySequenceRecorder.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequenceRecorder.ts#L13) - -*** - -### startRecording - -```ts -startRecording: () => void; -``` - -Defined in: [useHotkeySequenceRecorder.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequenceRecorder.ts#L14) - -#### Returns - -`void` - -*** - -### steps - -```ts -steps: HotkeySequence; -``` - -Defined in: [useHotkeySequenceRecorder.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequenceRecorder.ts#L12) - -*** - -### stopRecording - -```ts -stopRecording: () => void; -``` - -Defined in: [useHotkeySequenceRecorder.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequenceRecorder.ts#L15) - -#### Returns - -`void` diff --git a/docs/framework/preact/reference/interfaces/UseCustomHotkeysResult.md b/docs/framework/preact/reference/interfaces/UseCustomHotkeysResult.md new file mode 100644 index 00000000..59cc3db7 --- /dev/null +++ b/docs/framework/preact/reference/interfaces/UseCustomHotkeysResult.md @@ -0,0 +1,111 @@ +--- +id: UseCustomHotkeysResult +title: UseCustomHotkeysResult +--- + +Defined in: useAppHotkeys.ts:70 + +Custom hotkey state and actions returned by `useCustomHotkeys`. + +## Properties + +### customHotkeys + +```ts +customHotkeys: CustomHotkeys; +``` + +Defined in: useAppHotkeys.ts:72 + +The user's hotkeys that differ from the defaults. + +*** + +### findConflicts + +```ts +findConflicts: (id, hotkey) => AppHotkey[]; +``` + +Defined in: useAppHotkeys.ts:86 + +Other app hotkeys that would collide if `id` used `hotkey`. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] + +*** + +### resetCustomHotkey + +```ts +resetCustomHotkey: (id) => void; +``` + +Defined in: useAppHotkeys.ts:82 + +Restores the default hotkey for an app hotkey. + +#### Parameters + +##### id + +`string` + +#### Returns + +`void` + +*** + +### resetCustomHotkeys + +```ts +resetCustomHotkeys: () => void; +``` + +Defined in: useAppHotkeys.ts:84 + +Restores every default hotkey. + +#### Returns + +`void` + +*** + +### setCustomHotkey + +```ts +setCustomHotkey: (id, hotkey) => AppHotkey[]; +``` + +Defined in: useAppHotkeys.ts:77 + +Sets the user's hotkey for an app hotkey. `null` unbinds it. +Returns conflicting app hotkeys; when any exist, nothing changes. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` \| `null` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] diff --git a/docs/framework/preact/reference/interfaces/UseHotkeyDefinition.md b/docs/framework/preact/reference/interfaces/UseHotkeyDefinition.md deleted file mode 100644 index 02f513c0..00000000 --- a/docs/framework/preact/reference/interfaces/UseHotkeyDefinition.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: UseHotkeyDefinition -title: UseHotkeyDefinition ---- - -Defined in: [useHotkeys.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeys.ts#L20) - -A single hotkey definition for use with `useHotkeys`. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [useHotkeys.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeys.ts#L24) - -The function to call when the hotkey is pressed - -*** - -### hotkey - -```ts -hotkey: RegisterableHotkey; -``` - -Defined in: [useHotkeys.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeys.ts#L22) - -The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object - -*** - -### options? - -```ts -optional options?: UseHotkeyOptions; -``` - -Defined in: [useHotkeys.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeys.ts#L26) - -Per-hotkey options (merged on top of commonOptions) diff --git a/docs/framework/preact/reference/interfaces/UseHotkeyOptions.md b/docs/framework/preact/reference/interfaces/UseHotkeyOptions.md deleted file mode 100644 index 903e7406..00000000 --- a/docs/framework/preact/reference/interfaces/UseHotkeyOptions.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -id: UseHotkeyOptions -title: UseHotkeyOptions ---- - -Defined in: [useHotkey.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkey.ts#L17) - -## Extends - -- `Omit`\<`HotkeyOptions`, `"target"`\> - -## Properties - -### target? - -```ts -optional target?: - | Document - | Window - | HTMLElement - | RefObject - | null; -``` - -Defined in: [useHotkey.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkey.ts#L23) - -The DOM element to attach the event listener to. -Can be a Preact ref, direct DOM element, or null. -Defaults to document. diff --git a/docs/framework/preact/reference/interfaces/UseHotkeySequenceDefinition.md b/docs/framework/preact/reference/interfaces/UseHotkeySequenceDefinition.md deleted file mode 100644 index 2ba704d0..00000000 --- a/docs/framework/preact/reference/interfaces/UseHotkeySequenceDefinition.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: UseHotkeySequenceDefinition -title: UseHotkeySequenceDefinition ---- - -Defined in: [useHotkeySequences.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequences.ts#L15) - -A single sequence definition for use with `useHotkeySequences`. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [useHotkeySequences.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequences.ts#L19) - -The function to call when the sequence is completed - -*** - -### options? - -```ts -optional options?: UseHotkeySequenceOptions; -``` - -Defined in: [useHotkeySequences.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequences.ts#L21) - -Per-sequence options (merged on top of commonOptions) - -*** - -### sequence - -```ts -sequence: HotkeySequence; -``` - -Defined in: [useHotkeySequences.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequences.ts#L17) - -Array of hotkey strings that form the sequence diff --git a/docs/framework/preact/reference/interfaces/UseHotkeySequenceOptions.md b/docs/framework/preact/reference/interfaces/UseHotkeySequenceOptions.md deleted file mode 100644 index 1e01cd9c..00000000 --- a/docs/framework/preact/reference/interfaces/UseHotkeySequenceOptions.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -id: UseHotkeySequenceOptions -title: UseHotkeySequenceOptions ---- - -Defined in: [useHotkeySequence.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequence.ts#L14) - -## Extends - -- `Omit`\<`SequenceOptions`, `"target"`\> - -## Properties - -### target? - -```ts -optional target?: - | Document - | Window - | HTMLElement - | RefObject - | null; -``` - -Defined in: [useHotkeySequence.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeySequence.ts#L23) - -The DOM element to attach the event listener to. -Can be a Preact ref, direct DOM element, or null. -Defaults to document. diff --git a/docs/framework/preact/reference/type-aliases/UseAppHotkeyOptions.md b/docs/framework/preact/reference/type-aliases/UseAppHotkeyOptions.md new file mode 100644 index 00000000..62743d58 --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/UseAppHotkeyOptions.md @@ -0,0 +1,21 @@ +--- +id: UseAppHotkeyOptions +title: UseAppHotkeyOptions +--- + +```ts +type UseAppHotkeyOptions = AdapterAppHotkeyOptions; +``` + +Defined in: [useHotkey.ts:44](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkey.ts#L44) + +Options for an app hotkey at the call site. `meta` comes from the declaration. +Kind-specific options follow the declared default: `requireReset` for a chord, +`timeout` for a sequence, both for an app hotkey without a default. If the +user rebinds it to the other kind, options for the old kind no longer apply. + +## Type Parameters + +### T + +`T` *extends* `AppHotkey` = `AppHotkey` diff --git a/docs/framework/preact/reference/type-aliases/UseHotkeyChordOptions.md b/docs/framework/preact/reference/type-aliases/UseHotkeyChordOptions.md new file mode 100644 index 00000000..9e280e94 --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/UseHotkeyChordOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeyChordOptions +title: UseHotkeyChordOptions +--- + +```ts +type UseHotkeyChordOptions = AdapterChordOptions; +``` + +Defined in: [useHotkey.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkey.ts#L26) + +Options for a chord such as `'Mod+S'`. diff --git a/docs/framework/preact/reference/type-aliases/UseHotkeyCommonOptions.md b/docs/framework/preact/reference/type-aliases/UseHotkeyCommonOptions.md new file mode 100644 index 00000000..73a0e6cc --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/UseHotkeyCommonOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeyCommonOptions +title: UseHotkeyCommonOptions +--- + +```ts +type UseHotkeyCommonOptions = AdapterCommonOptions; +``` + +Defined in: [useHotkey.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkey.ts#L32) + +Options applied to every hotkey, such as provider defaults. Each applies to the kinds that support it. diff --git a/docs/framework/preact/reference/type-aliases/UseHotkeyDefinition.md b/docs/framework/preact/reference/type-aliases/UseHotkeyDefinition.md new file mode 100644 index 00000000..b9ff6462 --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/UseHotkeyDefinition.md @@ -0,0 +1,13 @@ +--- +id: UseHotkeyDefinition +title: UseHotkeyDefinition +--- + +```ts +type UseHotkeyDefinition = AdapterHotkeyDefinition; +``` + +Defined in: [useHotkeys.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkeys.ts#L16) + +One hotkey for `useHotkeys`. Chords accept `requireReset`; sequences accept +`timeout`; hotkeys of unknown kind accept only shared options. diff --git a/docs/framework/preact/reference/type-aliases/UseHotkeyOptions.md b/docs/framework/preact/reference/type-aliases/UseHotkeyOptions.md new file mode 100644 index 00000000..5f6a17db --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/UseHotkeyOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeyOptions +title: UseHotkeyOptions +--- + +```ts +type UseHotkeyOptions = AdapterHotkeyOptions; +``` + +Defined in: [useHotkey.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkey.ts#L23) + +Options shared by chords and sequences. diff --git a/docs/framework/preact/reference/type-aliases/UseHotkeyOptionsFor.md b/docs/framework/preact/reference/type-aliases/UseHotkeyOptionsFor.md new file mode 100644 index 00000000..a2698763 --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/UseHotkeyOptionsFor.md @@ -0,0 +1,18 @@ +--- +id: UseHotkeyOptionsFor +title: UseHotkeyOptionsFor +--- + +```ts +type UseHotkeyOptionsFor = AdapterOptionsFor; +``` + +Defined in: [useHotkey.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkey.ts#L35) + +The options type for a hotkey: chord options for chords, sequence options for sequences. + +## Type Parameters + +### T + +`T` *extends* `RegisterableHotkey` diff --git a/docs/framework/preact/reference/type-aliases/UseHotkeySequenceOptions.md b/docs/framework/preact/reference/type-aliases/UseHotkeySequenceOptions.md new file mode 100644 index 00000000..5f2bc080 --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/UseHotkeySequenceOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeySequenceOptions +title: UseHotkeySequenceOptions +--- + +```ts +type UseHotkeySequenceOptions = AdapterSequenceOptions; +``` + +Defined in: [useHotkey.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkey.ts#L29) + +Options for a sequence such as `['G', 'G']`. diff --git a/docs/framework/preact/reference/type-aliases/UseHotkeyTarget.md b/docs/framework/preact/reference/type-aliases/UseHotkeyTarget.md new file mode 100644 index 00000000..6c7856d1 --- /dev/null +++ b/docs/framework/preact/reference/type-aliases/UseHotkeyTarget.md @@ -0,0 +1,17 @@ +--- +id: UseHotkeyTarget +title: UseHotkeyTarget +--- + +```ts +type UseHotkeyTarget = + | RefObject + | HTMLElement + | Document + | Window + | null; +``` + +Defined in: [useHotkey.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/preact-hotkeys/src/useHotkey.ts#L19) + +A Preact ref, element, document, or window to listen on. diff --git a/docs/framework/react/guides/app-hotkeys.md b/docs/framework/react/guides/app-hotkeys.md new file mode 100644 index 00000000..e3098f38 --- /dev/null +++ b/docs/framework/react/guides/app-hotkeys.md @@ -0,0 +1,210 @@ +--- +title: App Hotkeys Guide +id: app-hotkeys +--- + +`useHotkey` registers a hotkey when its component mounts. A cheat sheet built from live registrations therefore misses hotkeys on routes that have not rendered yet, and a settings screen has nowhere to change them. App hotkeys solve both: you declare every hotkey in one place, pass the declarations to `HotkeysProvider`, and attach callbacks where the work happens. Users can then customize any of them, and your app decides how to save their choices. + +A declaration holds a hotkey's id, default hotkey, metadata, and options. It never holds a callback. `useHotkey` and `useHotkeys` still attach the callbacks. + +## Declare app hotkeys + +Use `defineAppHotkeys` in a module that contains only declarations. Keep it free of components and handlers, because it loads with your app shell: + +```ts +// app-hotkeys.ts +import { defineAppHotkeys } from '@tanstack/react-hotkeys' + +export const appHotkeys = defineAppHotkeys({ + 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save', group: 'Document' } }, + 'doc.find': { + hotkey: 'Mod+F', + meta: { name: 'Find', group: 'Document' }, + context: 'editor', + }, + 'nav.home': { + hotkey: ['G', 'H'], + meta: { name: 'Go home', group: 'Navigation' }, + options: { timeout: 800 }, + }, + 'palette.open': { hotkey: null, meta: { name: 'Command palette' } }, +}) + +declare module '@tanstack/react-hotkeys' { + interface Register { + appHotkeys: typeof appHotkeys + } +} +``` + +- The object key is the app hotkey's id. Treat ids as a stable contract, because saved custom hotkeys use them as keys. +- `hotkey` is the default: a chord, a sequence, or `null` for an action without a default. An unbound app hotkey registers nothing until the user assigns one. +- `options` describe the hotkey itself: `conflictBehavior`, `eventType`, `ignoreInputs`, `preventDefault`, `stopPropagation`, plus `requireReset` for a chord default or `timeout` for a sequence default. Options that don't match the default's kind are type errors. Pass `target` and `enabled` where you register the hotkey. +- `context` labels where a hotkey applies, such as `'editor'`. App hotkeys with different contexts may share a hotkey. It is used only for conflict checks and does not affect matching. + +The `Register` augmentation types `useAppHotkeys()`, the ids accepted by `useCustomHotkeys()`, and the `CustomHotkeys` type. + +## Provide app hotkeys + +Pass the declarations to `HotkeysProvider`: + +```tsx +import { HotkeysProvider } from '@tanstack/react-hotkeys' +import { appHotkeys } from './app-hotkeys' + +function Root() { + return ( + + + + ) +} +``` + +## Register app hotkeys + +Pass an app hotkey wherever you pass a hotkey. Read it from `useAppHotkeys()`, or import it directly: + +```tsx +import { useAppHotkeys, useHotkey } from '@tanstack/react-hotkeys' + +function Editor() { + const hotkeys = useAppHotkeys() + + useHotkey(hotkeys['doc.save'], () => save(), { enabled: isDirty }) + useHotkey(hotkeys['nav.home'], () => navigate({ to: '/' })) +} +``` + +Both forms use the provider's effective hotkey for that id, so a user's custom hotkey applies even to a direct import. Without a provider, the declared default is used. + +The registration takes `meta` and `options` from the declaration. Options merge in this order: provider `defaultOptions`, then declared options, then call-site options. `meta` comes only from the declaration, so the cheat sheet and the registration cannot disagree. Call-site options are typed by the declared default's kind, so `timeout` is accepted for `nav.home` but not for `doc.save`. + +`useHotkeys` accepts app hotkeys in the same field: + +```tsx +useHotkeys([ + { hotkey: hotkeys['doc.save'], callback: save }, + { hotkey: 'Escape', callback: close }, +]) +``` + +When the user changes a hotkey, registered app hotkeys move to the new hotkey immediately, even if it changes from a chord to a sequence. + +## Build a cheat sheet + +`useAppHotkeyList` returns every app hotkey in declaration order, with its live status: + +| Status | Meaning | +| --- | --- | +| `'active'` | At least one enabled registration | +| `'disabled'` | Registered, but every registration has `enabled: false` | +| `'inactive'` | Has a hotkey, but nothing has registered it | +| `'unbound'` | No hotkey assigned | + +```tsx +import { formatForDisplay, useAppHotkeyList } from '@tanstack/react-hotkeys' + +function CheatSheet() { + const items = useAppHotkeyList() + + return ( +
      + {items.map(({ appHotkey, status, isCustom }) => ( +
    • + {appHotkey.meta?.name}:{' '} + {appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned'} + {isCustom && ' (custom)'} +
    • + ))} +
    + ) +} +``` + +An app hotkey registered by many components, such as one per list row, appears once. Its `registrations` array holds each live registration. Devtools also list declared app hotkeys that are not registered. + +## Customize and persist hotkeys + +TanStack Hotkeys does not store anything. Pass the user's saved custom hotkeys to the provider and save them when they change: + +```tsx +import { HotkeysProvider } from '@tanstack/react-hotkeys' +import type { CustomHotkeys } from '@tanstack/react-hotkeys' + +const saved: CustomHotkeys = JSON.parse(localStorage.getItem('hotkeys') ?? '{}') + +function Root() { + return ( + + localStorage.setItem('hotkeys', JSON.stringify(custom)) + } + > + + + ) +} +``` + +- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them. +- The provider reads `customHotkeys` once and owns the state afterwards. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning. +- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile. + +Recorded hotkeys use `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others. + +### Change hotkeys + +`useCustomHotkeys` reads and changes the user's custom hotkeys: + +```tsx +import { + formatForDisplay, + useAppHotkeys, + useCustomHotkeys, + useHotkeyRecorder, +} from '@tanstack/react-hotkeys' +import type { AppHotkeyId } from '@tanstack/react-hotkeys' + +function ShortcutRow({ id }: { id: AppHotkeyId }) { + const appHotkey = useAppHotkeys()[id] + const { setCustomHotkey, resetCustomHotkey, findConflicts } = useCustomHotkeys() + + const recorder = useHotkeyRecorder({ + maxSteps: 3, + validate: (hotkey) => { + const conflicts = findConflicts(id, hotkey) + return conflicts.length === 0 || `Used by ${conflicts[0]?.meta?.name}` + }, + onRecord: (hotkey) => setCustomHotkey(id, hotkey), + onClear: () => setCustomHotkey(id, null), + }) + + return ( +
    + {appHotkey.meta?.name}:{' '} + {appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned'} + + +
    + ) +} +``` + +| Member | Description | +| --- | --- | +| `customHotkeys` | The hotkeys that differ from the defaults | +| `setCustomHotkey(id, hotkey)` | Sets a hotkey, or unbinds with `null`. Returns conflicting app hotkeys; when there are any, nothing changes. | +| `findConflicts(id, hotkey)` | App hotkeys that would collide if `id` used `hotkey` | +| `resetCustomHotkey(id)` | Restores one default | +| `resetCustomHotkeys()` | Restores every default | + +Two app hotkeys conflict when their contexts match or either has no `context`, and their hotkeys collide: equal chords, or one sequence starts with the other, including a sequence whose first step equals a chord. Conflicts are checked across every declaration, whether or not it is registered. + +Each app hotkey's `defaultHotkey` holds its declared default, and `isCustom` in `useAppHotkeyList` reports whether the effective hotkey differs from it. + +## Without React + +The provider creates an `AppHotkeysStore` from `@tanstack/hotkeys`. Outside React, create the store yourself and publish it for devtools with `publishAppHotkeysStore`, or use `provideAppHotkeys` to do both. diff --git a/docs/framework/react/guides/formatting-display.md b/docs/framework/react/guides/formatting-display.md index b10aa57a..ec25b8bf 100644 --- a/docs/framework/react/guides/formatting-display.md +++ b/docs/framework/react/guides/formatting-display.md @@ -3,7 +3,7 @@ title: Formatting & Display Guide id: formatting-display --- -Use `formatForDisplay` whenever a binding appears in a menu, button, hint, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, and parsed bindings. Store the original binding and format it at render time: the display label is not a registration string. +Use `formatForDisplay` whenever a hotkey appears in a menu, button, hint, cheat sheet, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, parsed bindings, and sequences. Store the original binding and format it at render time: the display label is not a registration string. ## Format a binding @@ -32,16 +32,18 @@ const parts = formatForDisplay(binding, { platform: 'mac', parts: true }) With `parts` omitted or false, the result is a string. A runtime boolean returns `string | string[]`. Parts preserve literal plus keys and ignore `separatorToken`. -Sequences are arrays of bindings. Format their steps individually: +## Format sequences -```ts -import type { HotkeySequence } from '@tanstack/react-hotkeys' +Pass a sequence to format each step and join the steps with `sequenceSeparator`, a space by default: -const sequence: HotkeySequence = ['Mod+[KeyK]', 'C'] -const label = sequence.map((step) => formatForDisplay(step)).join(' → ') +```ts +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac' }) // '⌘ K C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' → ' }) // 'G → G' +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac', parts: true }) +// [['⌘', 'K'], ['C']] — one array of keycaps per step ``` -`formatHotkeySequence` only joins stored strings with spaces. It intentionally retains brackets and code names, so use the code above for user-facing labels. +With `parts: true`, a sequence returns one array per step, so you can render each step as a group of keycaps. ## Choose symbols and separators @@ -63,7 +65,7 @@ formatForDisplay('Control++', { }) // 'Ctrl · +' ``` -An empty separator joins labels directly. `undefined` or `null` uses the platform default. `formatWithLabels(binding, options)` is the shorthand for `formatForDisplay` with `useSymbols: false`. +An empty separator joins labels directly. `undefined` or `null` uses the platform default. `separatorToken` applies within a chord; `sequenceSeparator` applies between steps. ## Supply layout labels @@ -88,6 +90,10 @@ formatForDisplay('Mod+[KeyQ]', { The precedence is `keyLabels`, then a layout entry, then the fallback label. Layout entries receive normal letter casing and key symbols; missing or empty entries fall back. Explicit labels are final. All of these options affect display only. +## Compare hotkeys + +`areHotkeysEqual(a, b)` compares chords or sequences after resolving modifiers, so `Mod+S` equals `Meta+S` on macOS. Sequences compare step by step. `isHotkeySequence` narrows a `Hotkey` to a sequence, and `simplifyHotkey` turns a one-step sequence into its chord. + ## Parse and store bindings `parseHotkey` returns either a logical `key` or a physical `code`, plus resolved modifier flags. Narrow the union before inspecting the identity: @@ -105,6 +111,6 @@ formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S' Parsed modifiers are already resolved. To display a portable `Mod` binding on another platform, serialize with the original platform first, as above. `normalizeRegisterableHotkey` accepts strings or raw objects and preserves the logical/physical distinction. Do not store display labels or put a bracketed code in a logical `key` field. -Use `validateHotkey` when accepting strings from an external source. It returns `valid`, `errors`, and `warnings`; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). +Use `validateHotkey` when accepting a chord or sequence from an external source. It returns `valid`, `errors`, and `warnings`, and names the failing step of a sequence; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). Try these options together in the [vanilla formatter playground](../../../framework/vanilla/examples/formatForDisplay). diff --git a/docs/framework/react/guides/hotkey-recording.md b/docs/framework/react/guides/hotkey-recording.md index b914dfd4..45a65e67 100644 --- a/docs/framework/react/guides/hotkey-recording.md +++ b/docs/framework/react/guides/hotkey-recording.md @@ -3,11 +3,11 @@ title: Hotkey Recording Guide id: hotkey-recording --- -The `useHotkeyRecorder` hook is for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the key combination they want, the same way system preferences or IDE shortcut editors work. +The `useHotkeyRecorder` hook is for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the key combination they want, the same way system preferences or IDE shortcut editors work. By default it records one chord; set `maxSteps` to let users record a [sequence](./sequences) too. Recorders default to physical codes: recording a shortcut stores a string such as `Mod+[KeyS]`. Pass it directly to your hotkey registration and use `formatForDisplay` for the label. Set `recordBy: 'key'` when you intentionally want the produced character instead. -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. +TanStack Hotkeys automatically suppresses registered hotkey callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. ## Basic usage @@ -46,10 +46,12 @@ The `useHotkeyRecorder` hook returns an object with: | Property | Type | Description | |----------|------|-------------| | `isRecording` | `boolean` | Whether the recorder is currently listening for key presses | -| `recordedHotkey` | `Hotkey \| null` | The last recorded hotkey string, or `null` if nothing recorded | +| `steps` | `HotkeySequence` | Chords captured so far in a multi-step recording | +| `recordedHotkey` | `Hotkey \| null` | The last recorded hotkey, or `null` if nothing recorded | | `startRecording` | `() => void` | Start listening for key presses | -| `stopRecording` | `() => void` | Stop listening and reset recorder state without calling `onRecord` | -| `cancelRecording` | `() => void` | Stop listening and discard any recorded hotkey | +| `stopRecording` | `() => void` | Stop listening without calling `onRecord` or `onCancel` | +| `cancelRecording` | `() => void` | Stop listening and call `onCancel` | +| `commitRecording` | `() => void` | Commit the current `steps` now (multi-step recordings) | ## Options @@ -70,7 +72,7 @@ useHotkeyRecorder({ ### `onRecord` -Called when the user presses a valid key combination (a modifier + a non-modifier key, or a single non-modifier key). Receives the recorded `Hotkey` string. +Called when a recording commits. With the default `maxSteps: 1`, it receives a chord string such as `'Mod+[KeyS]'` as soon as the user presses a valid combination (a modifier + a non-modifier key, or a single non-modifier key). With a larger `maxSteps`, it receives a chord when one step was recorded, or a sequence otherwise. ### `onCancel` @@ -78,7 +80,7 @@ Called when recording is cancelled (either by pressing Escape or calling `cancel ### `onClear` -Called when the recorded hotkey is cleared (by pressing Backspace or Delete during recording). +Called when the recording is cleared: Backspace or Delete with no modifiers and no recorded steps. Clearing calls only `onClear`; your app decides whether that removes a hotkey or restores a default. ### Global defaults via provider @@ -91,6 +93,7 @@ import { HotkeysProvider } from '@tanstack/react-hotkeys' defaultOptions={{ hotkeyRecorder: { onCancel: () => console.log('Recording cancelled'), + idleTimeoutMs: 2000, // applies to multi-step recorders }, }} > @@ -98,6 +101,43 @@ import { HotkeysProvider } from '@tanstack/react-hotkeys'
    ``` +## Recording sequences + +Set `maxSteps` above 1 to record up to that many chords. Each chord adds a step, and the recording commits when the user presses Enter, when it reaches `maxSteps`, or after `idleTimeoutMs` without a new step: + +```tsx +import { useHotkeyRecorder, formatForDisplay } from '@tanstack/react-hotkeys' + +function SequenceRecorder() { + const recorder = useHotkeyRecorder({ + maxSteps: 3, + onRecord: (hotkey) => { + console.log('Recorded:', hotkey) // 'Mod+[KeyS]' or ['[KeyG]', '[KeyG]'] + }, + }) + + return ( + + ) +} +``` + +A one-step recording commits as a chord, so the result can always be passed straight to `useHotkey`. Use `maxSteps: Infinity` for no limit. + +| Option | Description | +| --- | --- | +| `maxSteps` | Maximum chords to record. Reaching it commits. Default `1`. | +| `commitKeys` | `'enter'` (default) commits on plain Enter. With `'none'`, plain Enter can be recorded as a step, and only `commitRecording()`, `idleTimeoutMs`, or `maxSteps` commit. | +| `idleTimeoutMs` | Commit automatically after this many milliseconds without a new step. The timer does not run before the first chord. | + +`commitKeys` and `idleTimeoutMs` are only accepted when `maxSteps` is set. With `maxSteps` above 1, `onRecord` and `validate` receive `Hotkey` (a chord or a sequence) instead of a chord. + ## Recording behavior Different keys mean different things to the recorder: @@ -107,8 +147,11 @@ Different keys mean different things to the recorder: | Modifier only (Shift, Ctrl, etc.) | Waits for a non-modifier key; modifier-only presses don't complete a recording | | Modifier + key (e.g., Ctrl+S) | Records the full combination | | Single key (e.g., Escape, F1) | Records the single key | +| Enter (multi-step, `commitKeys: 'enter'`) | Commits the recorded steps | | Escape | Cancels the recording | -| Backspace / Delete | Clears the currently recorded hotkey | +| Backspace / Delete | Removes the last step; with no steps, clears and calls `onClear` | + +Repeats and IME composition never add a step. ### `ignoreInputs` @@ -127,7 +170,7 @@ Recorded hotkeys use the portable `Mod` format. If a user on macOS presses Comma ## Validation and conflicts -Both recorder APIs accept `detectConflicts`, `validate`, and `onReject`. For a single-hotkey recorder, these options can be supplied alongside `onRecord`: +The recorder accepts `detectConflicts`, `validate`, and `onReject` alongside `onRecord`: ```ts const options = { @@ -146,66 +189,69 @@ const options = { } ``` -`validate` returns true to accept, or false/a message to reject. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the candidate when available, and conflicting registration views for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. +`validate(hotkey, context)` returns true to accept, or false/a message to reject. The context holds the last chord's `event` and `parsedHotkey`, plus `events` and `parsedSequence` for every step. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the rejected `hotkey` when available, and conflicting registrations for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. A rejected multi-step recording keeps its steps so the user can edit them with Backspace. -`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a binding. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Both single bindings and sequence prefixes are checked. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can affect actual dispatch. +`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a binding. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Chords and sequence prefixes are checked: a recorded chord conflicts with a sequence that starts with it, and a recorded sequence conflicts with a registered chord equal to its first step. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can affect actual dispatch. -For checks outside recording, use `findHotkeyConflicts(bindingOrSequence, options)`. Without source `events`, it compares binding identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. +For checks outside recording, use `findHotkeyConflicts(hotkey, options)`. Each result's `registration` is a [registration view](./hotkeys#introspecting-registrations). Without source `events`, it compares binding identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. ## Building a shortcut settings UI -A more complete shortcut customization panel: +For an app-wide settings screen, declare the hotkeys with [app hotkeys](./app-hotkeys) and save the recorder's result with `setCustomHotkey`. That keeps cheat sheets, conflicts, and persistence in one place. + +For a single component, ordinary state is enough: ```tsx import { useState } from 'react' import { - useHotkey, + useHotkeys, useHotkeyRecorder, formatForDisplay, } from '@tanstack/react-hotkeys' import type { Hotkey } from '@tanstack/react-hotkeys' +const actions = { save, undo, search } + function ShortcutSettings() { - const [shortcuts, setShortcuts] = useState>({ + const [shortcuts, setShortcuts] = useState>({ save: 'Mod+S', undo: 'Mod+Z', - search: 'Mod+K', + search: ['G', 'S'], }) - - const [editingAction, setEditingAction] = useState(null) + const [editing, setEditing] = useState(null) const recorder = useHotkeyRecorder({ + maxSteps: 2, onRecord: (hotkey) => { - if (editingAction) { - setShortcuts((prev) => ({ ...prev, [editingAction]: hotkey })) - setEditingAction(null) - } + if (editing) setShortcuts((prev) => ({ ...prev, [editing]: hotkey })) + setEditing(null) }, - onCancel: () => setEditingAction(null), + onCancel: () => setEditing(null), }) - // Register the actual hotkeys with their current bindings - useHotkey(shortcuts.save, () => save()) - useHotkey(shortcuts.undo, () => undo()) - useHotkey(shortcuts.search, () => openSearch()) + // Register the actual hotkeys with their current values + useHotkeys( + Object.entries(shortcuts).map(([action, hotkey]) => ({ + hotkey, + callback: actions[action as keyof typeof actions], + })), + ) return (
    -

    Keyboard Shortcuts

    {Object.entries(shortcuts).map(([action, hotkey]) => ( -
    - {action} - -
    + ))}
    ) @@ -214,4 +260,4 @@ function ShortcutSettings() { ## Under the hood -The `useHotkeyRecorder` hook creates a `HotkeyRecorder` class instance and subscribes to its reactive state via `@tanstack/react-store`. The class manages its own keyboard event listeners and state, and the hook handles cleanup on unmount. +The `useHotkeyRecorder` hook creates a `HotkeyRecorder` class instance from `@tanstack/hotkeys` and subscribes to its reactive state via `@tanstack/react-store`. The class manages its own keyboard event listeners and state, and the hook handles cleanup on unmount. diff --git a/docs/framework/react/guides/hotkeys.md b/docs/framework/react/guides/hotkeys.md index 9d10521a..64eecbfa 100644 --- a/docs/framework/react/guides/hotkeys.md +++ b/docs/framework/react/guides/hotkeys.md @@ -5,6 +5,8 @@ id: hotkeys The `useHotkey` hook is the primary way to register keyboard shortcuts in React applications. It wraps the singleton `HotkeyManager` with automatic lifecycle management, stale-closure prevention, and React ref support. +A hotkey is either a **chord**, keys pressed together such as `'Mod+S'`, or a **sequence**, chords pressed one after another such as `['G', 'G']`. Every API in this guide accepts both. Sequence-specific behavior is covered in the [Sequences Guide](./sequences). To declare hotkeys up front and let users customize them, see the [App Hotkeys Guide](./app-hotkeys). + ## Logical keys and physical positions Use a logical binding when the shortcut should follow the character on the active layout. Use a physical binding when it should follow a keyboard position: @@ -45,6 +47,15 @@ useHotkey('Mod+S', (event, context) => { }) ``` +Pass an array of chords to register a sequence with the same hook: + +```tsx +useHotkey(['G', 'G'], () => scrollToTop()) +useHotkey(['Mod+K', 'Mod+C'], () => commentSelection()) +``` + +A one-step array such as `['Mod+S']` registers the chord `'Mod+S'`. + You can pass a hotkey as a string or as a `RawHotkey` object (modifier booleans optional). Use `mod` for cross-platform shortcuts (Command on Mac, Control elsewhere): ```tsx @@ -76,7 +87,8 @@ useHotkey('Mod+S', callback, { preventDefault: true, stopPropagation: true, eventType: 'keydown', - requireReset: false, + requireReset: false, // chords only + timeout: 1000, // sequences only ignoreInputs: undefined, // smart default: false for Mod+S, true for single keys target: document, platform: undefined, // auto-detected @@ -96,16 +108,28 @@ By default, `Ctrl`/`Meta` shortcuts (like `Mod+S`) and `Escape` fire even while If you register a hotkey that's already registered somewhere else in your app, the library logs a warning by default (`conflictBehavior: 'warn'`). That surfaces accidental duplicate bindings during development, before they reach production. +### Chord and sequence options + +Most options apply to both kinds. `requireReset` applies only to chords and `timeout` only to sequences. The option types follow the hotkey you pass, so TypeScript rejects `timeout` on a chord and `requireReset` on a sequence: + +```tsx +useHotkey('Escape', close, { requireReset: true }) // OK +useHotkey(['G', 'G'], scrollToTop, { timeout: 500 }) // OK +useHotkey('Escape', close, { timeout: 500 }) // Type error +``` + +When the kind is not known statically, such as a `Hotkey` loaded from settings, only the shared options are accepted. + ### Global defaults via provider -You can change the default options for all `useHotkey` calls in your app by wrapping your component tree with `HotkeysProvider`. Per-hook options will override the provider defaults. +You can change the default options for all `useHotkey` and `useHotkeys` calls in your app by wrapping your component tree with `HotkeysProvider`. Per-hook options will override the provider defaults. Defaults apply only to the kinds that support them, so a default `timeout` affects sequences and a default `requireReset` affects chords. ```tsx import { HotkeysProvider } from '@tanstack/react-hotkeys' @@ -162,7 +186,7 @@ useHotkey('Shift', () => deactivateMode(), { eventType: 'keyup' }) ### `requireReset` -When `true`, the hotkey will only fire once per key press. The key must be released and pressed again to fire again. Defaults to `false`. +When `true`, a chord fires only once per key press. The key must be released and pressed again to fire again. Defaults to `false`. Sequences ignore automatic repeats already, so this option applies only to chords. ```tsx // Only fires once per Escape press, not on key repeat @@ -224,6 +248,8 @@ Controls what happens when you register a hotkey that's already registered. Opti useHotkey('Mod+S', () => save(), { conflictBehavior: 'replace' }) ``` +Duplicate detection compares resolved hotkeys on the same target, so `Mod+S` and `Meta+S` conflict on macOS. Two sequences conflict when their steps are equal; a shared prefix alone is not a duplicate. + ### `platform` Override the auto-detected platform. Useful for testing or for applications that need to force a specific platform behavior. @@ -273,14 +299,17 @@ function Editor() { useHotkeys([ { hotkey: 'Mod+S', callback: () => save() }, { hotkey: 'Mod+Z', callback: () => undo() }, + { hotkey: ['G', 'G'], callback: () => scrollToTop(), options: { timeout: 500 } }, { hotkey: 'Escape', callback: () => close() }, ]) } ``` +Each definition's `options` are typed by its `hotkey`, just like `useHotkey`. + ### Common options with per-hotkey overrides -Pass shared options as the second argument. Per-definition options override the common ones: +Pass shared options as the second argument. Per-definition options override the common ones. Options merge in this order: `HotkeysProvider` defaults, then the common options, then each definition's options. A common `timeout` or `requireReset` applies only to the definitions of the matching kind. ```tsx useHotkeys( @@ -308,7 +337,7 @@ function MenuShortcuts({ items }) { } ``` -The hook diffs the array between renders by array index plus the normalized hotkey string, registering new hotkeys and unregistering removed ones automatically. Reordering the array changes that identity, so reordered entries are unregistered and re-registered even if their callback references stay the same. +The hook diffs the array between renders by array index plus the normalized hotkey, registering new hotkeys and unregistering removed ones automatically. Reordering the array changes that identity, so reordered entries are unregistered and re-registered even if their callback references stay the same. ## Metadata (name, description, and group) @@ -338,60 +367,56 @@ Group is descriptive metadata, not an execution scope. A shortcuts panel can gro ## Introspecting registrations -Use the `useHotkeyRegistrations` hook to get a live view of all hotkey and sequence registrations. It's the building block for shortcut palettes and help dialogs. +Use the `useHotkeyRegistrations` hook to get a live view of every registration, chords and sequences together. It's the building block for shortcut palettes and help dialogs. ```tsx import { useHotkeyRegistrations, formatForDisplay } from '@tanstack/react-hotkeys' function ShortcutPalette() { - const { hotkeys, sequences } = useHotkeyRegistrations() + const registrations = useHotkeyRegistrations() return ( -
    -

    Keyboard Shortcuts

    -
      - {hotkeys.map((reg) => ( -
    • - {formatForDisplay(reg.hotkey)} - {reg.options.meta?.name && — {reg.options.meta.name}} - {reg.options.meta?.description &&

      {reg.options.meta.description}

      } -
    • - ))} -
    - {sequences.length > 0 && ( - <> -

    Sequences

    -
      - {sequences.map((reg) => ( -
    • - {reg.sequence.map((step) => formatForDisplay(step)).join(' → ')} - {reg.options.meta?.name && — {reg.options.meta.name}} -
    • - ))} -
    - - )} -
    +
      + {registrations.map((reg) => ( +
    • + {formatForDisplay(reg.hotkey)} + {reg.options.meta?.name && — {reg.options.meta.name}} + {reg.kind === 'sequence' && ( + + {reg.matchedStepCount}/{reg.hotkey.length} steps + + )} +
    • + ))} +
    ) } ``` -The returned `hotkeys` array contains registration objects with the hotkey string, options (including `meta`), and enabled state. The `sequences` array contains sequence registrations with the same structure. +Each view has an `id`, the `hotkey`, its `options` (including `meta`), its `target`, and a `triggerCount`. Narrow with `kind`: chord views add `parsedHotkey`; sequence views add `parsedSequence` and `matchedStepCount`, the steps matched so far in the current attempt. + +Registrations only exist while their components are mounted. To list hotkeys from routes that have not rendered, declare them with [app hotkeys](./app-hotkeys). ## The hotkey manager -Under the hood, `useHotkey` uses the singleton `HotkeyManager`. You can access the manager directly if needed: +Under the hood, `useHotkey` uses the singleton `HotkeyManager`, which handles chords and sequences alike. You can access the manager directly if needed: ```tsx import { getHotkeyManager } from '@tanstack/react-hotkeys' const manager = getHotkeyManager() +// Register outside React; returns a handle with setOptions and unregister +const handle = manager.register(['G', 'G'], () => scrollToTop(), { timeout: 500 }) + // Check if a hotkey is registered manager.isRegistered('Mod+S') // Get total number of registrations manager.getRegistrationCount() + +// Reset in-progress sequences +manager.resetSequenceProgress() ``` The manager attaches event listeners per target element, so only elements with registered hotkeys get listeners. That beats a single global listener that has to inspect every keystroke. diff --git a/docs/framework/react/guides/key-state-tracking.md b/docs/framework/react/guides/key-state-tracking.md index d0b9027d..4f37bfc8 100644 --- a/docs/framework/react/guides/key-state-tracking.md +++ b/docs/framework/react/guides/key-state-tracking.md @@ -240,4 +240,10 @@ const visible = useHotkeyHint('Alt+Shift+[KeyK]') For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. +A sequence reveals its hint from its first step, and an app hotkey uses its effective hotkey (no hint while unbound): + +```ts +const visible = useHotkeyHint(['Mod+K', 'Mod+C']) // visible while Mod is held +``` + Combine the boolean with the action's enabled state. The helper does not register a shortcut or determine whether its target is focused. The core equivalent is `matchesHeldModifiers(binding, heldKeys, options)`. diff --git a/docs/framework/react/guides/sequence-recording.md b/docs/framework/react/guides/sequence-recording.md deleted file mode 100644 index 7bb6c42e..00000000 --- a/docs/framework/react/guides/sequence-recording.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -title: Sequence Recording Guide -id: sequence-recording ---- - -The `useHotkeySequenceRecorder` hook is for building UIs where users record multi-chord sequences (Vim-style shortcuts). Each step is captured like a single hotkey chord. Users finish with Enter by default, or you can use manual commit and an optional idle timeout. - -Sequence recording uses `recordBy: 'code'` by default, preserving every step as a physical string such as `['[KeyG]', 'Alt+[KeyS]']`. Set `recordBy: 'key'` for logical characters. The shared rejection and conflict options follow the [hotkey recording guide](./hotkey-recording.md#validation-and-conflicts). - -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Basic usage - -```tsx -import { useHotkeySequenceRecorder, formatForDisplay } from '@tanstack/react-hotkeys' -import type { HotkeySequence } from '@tanstack/react-hotkeys' - -function HotkeySequenceRecorder() { - const recorder = useHotkeySequenceRecorder({ - onRecord: (sequence: HotkeySequence) => { - console.log('Recorded:', sequence) - }, - }) - - return ( -
    - - {recorder.isRecording && ( - - )} -
    - ) -} -``` - -## Return value - -| Property | Type | Description | -|----------|------|-------------| -| `isRecording` | `boolean` | Whether the recorder is listening | -| `steps` | `HotkeySequence` | Chords captured in the current session | -| `recordedSequence` | `HotkeySequence \| null` | Last committed sequence | -| `startRecording` | `() => void` | Start a new session | -| `stopRecording` | `() => void` | Stop without calling `onRecord` | -| `cancelRecording` | `() => void` | Stop and call `onCancel` | -| `commitRecording` | `() => void` | Commit current `steps` (no-op if empty) | - -## Options - -`recordBy` defaults to `'code'`; choose `'key'` for logical characters. Repeats and IME composition never append steps. Modifier-only presses wait for a complete chord. - -Core options live on `HotkeySequenceRecorderOptions` from `@tanstack/hotkeys`: - -- `onRecord(sequence)`: called when a nonempty sequence is committed. Clearing calls only `onClear`. -- `onCancel`, `onClear`: same intent as the hotkey recorder. -- `commitKeys`: `'enter'` (default) or `'none'`. With `'none'`, only `commitRecording()` (or `idleTimeoutMs`) finishes recording; plain Enter can be recorded as a chord. -- `commitOnEnter`: when `commitKeys` is `'enter'`, set to `false` to treat Enter as a normal chord (then use `commitRecording()` or idle timeout to finish). -- `idleTimeoutMs`: optional milliseconds of inactivity after the last completed chord to auto-commit. The timer does not run while waiting for the first chord. - -### Provider defaults - -```tsx - - - -``` - -### `ignoreInputs` - -`HotkeySequenceRecorderOptions` supports an `ignoreInputs` option, which defaults to `true`. When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Escape still cancels recording even while an input is focused. Set `ignoreInputs: false` to let the recorder capture keys from within input elements. - -## Validation and conflicts - -Sequence `validate(sequence, { events, parsedSequence })` runs when committing and returns true, false, or a rejection message. `detectConflicts` checks sequence prefixes as well as single bindings. Rejected commits keep recording active with the steps intact so the user can edit them with Backspace. Empty Backspace/Delete clears and emits `onClear` only; removing a nonempty step does not commit. Recorded steps, commit keys, and releases are isolated from application handlers. - -## Behavior - -| Input | Behavior | -|-------|----------| -| Valid chord | Appended to `steps`; listener stays active | -| Enter (no modifiers), `commitKeys: 'enter'`, `steps.length >= 1` | Commits and calls `onRecord` | -| Escape | Cancels; `onCancel` | -| Backspace / Delete (no modifiers) | Removes last step, or if empty runs only `onClear` and stops | - -Recorded chords use portable `Mod` format, same as `HotkeyRecorder`. - -## Under the hood - -`useHotkeySequenceRecorder` wraps the `HotkeySequenceRecorder` class and subscribes to its TanStack Store, same pattern as `useHotkeyRecorder`. diff --git a/docs/framework/react/guides/sequences.md b/docs/framework/react/guides/sequences.md index 153e9b2b..939792f9 100644 --- a/docs/framework/react/guides/sequences.md +++ b/docs/framework/react/guides/sequences.md @@ -3,136 +3,106 @@ title: Sequences Guide id: sequences --- -TanStack Hotkeys supports multi-key sequences, shortcuts where you press keys one after another rather than together. Think Vim-style navigation, cheat codes, or multi-step commands. +A sequence is a hotkey made of chords pressed one after another rather than together. Think Vim-style navigation, cheat codes, or multi-step commands. Sequences are ordinary hotkeys: register them with `useHotkey` and `useHotkeys`, list them with `useHotkeyRegistrations`, record them with `useHotkeyRecorder`, and format them with `formatForDisplay`. -Sequence steps use the same string syntax as single hotkeys. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. Display steps with `sequence.map((step) => formatForDisplay(step)).join(' → ')`. +Sequence steps use the same syntax as chords. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. ## Basic usage -Use the `useHotkeySequence` hook to register a key sequence: +Pass an array of chords to `useHotkey`: ```tsx -import { useHotkeySequence } from '@tanstack/react-hotkeys' +import { useHotkey } from '@tanstack/react-hotkeys' function App() { // Vim-style: press g then g to scroll to top - useHotkeySequence(['G', 'G'], () => { + useHotkey(['G', 'G'], () => { window.scrollTo({ top: 0, behavior: 'smooth' }) }) } ``` -The first argument is an array of `Hotkey` strings representing each step in the sequence. The user must press them in order within the timeout window. +The user must press the steps in order, each within the timeout window. A one-step array such as `['Escape']` is the chord `'Escape'`. ## Many sequences at once -When you need several sequences, or a dynamic list whose length isn't fixed at compile time, use `useHotkeySequences` instead of calling `useHotkeySequence` many times. One hook call keeps you within the rules of hooks while still registering every sequence. +`useHotkeys` registers chords and sequences together, including dynamic lists: ```tsx -import { useHotkeySequences } from '@tanstack/react-hotkeys' +import { useHotkeys } from '@tanstack/react-hotkeys' -useHotkeySequences([ - { sequence: ['G', 'G'], callback: () => scrollToTop() }, - { sequence: ['D', 'D'], callback: () => deleteLine(), options: { timeout: 500 } }, +useHotkeys([ + { hotkey: ['G', 'G'], callback: () => scrollToTop() }, + { hotkey: ['D', 'D'], callback: () => deleteLine(), options: { timeout: 500 } }, + { hotkey: 'Mod+S', callback: () => save() }, ]) ``` -Options merge in the same order as `useHotkeys`: `HotkeysProvider` defaults, then the second-argument `commonOptions`, then each definition's `options`. - ## Sequence options -The third argument is an options object: +Sequences accept the same options as chords, except `requireReset`, plus `timeout`: ```tsx -useHotkeySequence(['G', 'G'], callback, { - timeout: 1000, // Time allowed between keys (ms) - enabled: true, // Whether the sequence is active +useHotkey(['G', 'G'], callback, { + timeout: 1000, // Time allowed between steps (ms) + enabled: true, // Whether the sequence is active }) ``` ### `timeout` -The maximum time (in milliseconds) allowed between consecutive key presses. If the user takes longer than this between any two keys, the sequence resets. Defaults to `1000` (1 second). +The maximum time (in milliseconds) allowed between consecutive steps. If the user takes longer than this between any two steps, the sequence resets. Defaults to `1000` (1 second). ```tsx // Fast sequence - user must type quickly -useHotkeySequence(['D', 'D'], () => deleteLine(), { timeout: 500 }) +useHotkey(['D', 'D'], () => deleteLine(), { timeout: 500 }) // Slow sequence - user has more time between keys -useHotkeySequence(['Shift+Z', 'Shift+Z'], () => forceQuit(), { timeout: 2000 }) +useHotkey(['Shift+Z', 'Shift+Z'], () => forceQuit(), { timeout: 2000 }) ``` -### `enabled` - -Controls whether the sequence is active. Defaults to `true`. - -Disabled sequences stay registered and visible in devtools; only execution is suppressed. - -```tsx -const [isVimMode, setIsVimMode] = useState(true) - -useHotkeySequence(['G', 'G'], () => scrollToTop(), { enabled: isVimMode }) -``` - -### Global defaults via provider - -You can set default options for all `useHotkeySequence` calls by wrapping your component tree with `HotkeysProvider`. Per-hook options override the provider defaults. +`timeout` is only accepted when the hotkey is a sequence. Set a default for every sequence with `HotkeysProvider`: ```tsx import { HotkeysProvider } from '@tanstack/react-hotkeys' - + ``` -### `meta` +### `enabled` and `meta` -Sequences support the same `meta` option as hotkeys. Attach a `name` and `description` for use in shortcut palettes and devtools. +These work exactly as they do for chords. Disabled sequences stay registered and visible in devtools, and `meta` flows to registrations, devtools, and cheat sheets. See the [Hotkeys Guide](./hotkeys#metadata-name-description-and-group). ```tsx -useHotkeySequence(['G', 'G'], () => scrollToTop(), { - meta: { name: 'Go to Top', description: 'Scroll to the top of the page' }, +useHotkey(['G', 'G'], () => scrollToTop(), { + enabled: isVimMode, + meta: { name: 'Go to top', description: 'Scroll to the top of the page' }, }) ``` -See the [Hotkeys Guide](./hotkeys.md#metadata-name--description) for details on declaration merging and introspecting registrations. - ## Sequences with modifiers Each step in a sequence can include modifiers: ```tsx // Ctrl+K followed by Ctrl+C (VS Code-style comment) -useHotkeySequence(['Mod+K', 'Mod+C'], () => { - commentSelection() -}) +useHotkey(['Mod+K', 'Mod+C'], () => commentSelection()) // g then Shift+G (go to bottom, Vim-style) -useHotkeySequence(['G', 'Shift+G'], () => { - scrollToBottom() -}) +useHotkey(['G', 'Shift+G'], () => scrollToBottom()) ``` -## Chained modifier chords - -This example follows physical R and T positions. Brackets retain those positions even when the keys produce different letters. Other sequences can continue using logical characters. +### Chained modifier chords -You can repeat the same modifier across consecutive steps, for example `Shift+R` then `Shift+T`: +You can repeat the same modifier across consecutive steps. This example follows physical R and T positions; brackets retain those positions even when the keys produce different letters: ```tsx -useHotkeySequence(['Shift+[KeyR]', 'Shift+[KeyT]'], () => { - doNextAction() -}) +useHotkey(['Shift+[KeyR]', 'Shift+[KeyT]'], () => doNextAction()) ``` -### Modifier-only keys between steps - -While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone, with no other key) are ignored. They neither advance the sequence nor reset progress. A user can tap Shift, or hold it, between chords like `Shift+R` and `Shift+T` without breaking the sequence, matching Vim-style flows where a modifier goes down before the next chord. +While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored. They neither advance the sequence nor reset progress, so a user can tap or hold Shift between `Shift+R` and `Shift+T`. ## Common sequence patterns @@ -140,18 +110,20 @@ While a sequence is in progress, modifier-only keydown events (Shift, Control, A ```tsx function VimNavigation() { - useHotkeySequence(['G', 'G'], () => scrollToTop()) - useHotkeySequence(['G', 'Shift+G'], () => scrollToBottom()) - useHotkeySequence(['D', 'D'], () => deleteLine()) - useHotkeySequence(['D', 'W'], () => deleteWord()) - useHotkeySequence(['C', 'I', 'W'], () => changeInnerWord()) + useHotkeys([ + { hotkey: ['G', 'G'], callback: () => scrollToTop() }, + { hotkey: ['G', 'Shift+G'], callback: () => scrollToBottom() }, + { hotkey: ['D', 'D'], callback: () => deleteLine() }, + { hotkey: ['D', 'W'], callback: () => deleteWord() }, + { hotkey: ['C', 'I', 'W'], callback: () => changeInnerWord() }, + ]) } ``` ### Konami Code ```tsx -useHotkeySequence( +useHotkey( [ 'ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', @@ -164,57 +136,59 @@ useHotkeySequence( ) ``` -### Multi-step commands +## Displaying sequences + +`formatForDisplay` formats each step and joins them with `sequenceSeparator`, a space by default: ```tsx -// Press "h", "e", "l", "p" to open help -useHotkeySequence(['H', 'E', 'L', 'P'], () => openHelp()) +formatForDisplay(['Mod+K', 'Mod+C'], { platform: 'mac' }) // '⌘ K ⌘ C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' then ' }) // 'G then G' ``` -## How sequences work +Registration views report progress for each sequence in `matchedStepCount`, so a help panel can show how far the user has typed. See [Introspecting registrations](./hotkeys#introspecting-registrations). -Both `SequenceManager` and `createSequenceMatcher` ignore modifier-only events, IME composition, and automatic keydown repeats. These events neither advance the sequence nor refresh its timeout: holding G does not complete a two-press G sequence. The manager prefers exact matches over weaker logical-key fallbacks while preserving equally strong matches. +## How sequences work -The `SequenceManager` (singleton) handles all sequence registrations. When a key is pressed: +The `HotkeyManager` tracks progress for every sequence registration. When a key is pressed: -1. It checks if the key matches the next expected step in any registered sequence -2. If it matches, the sequence advances to the next step +1. It checks whether the key matches the next expected step of each sequence +2. If it matches, that sequence advances 3. If the timeout expires between steps, the sequence resets 4. When all steps are completed, the callback fires -5. Modifier-only keydowns are ignored (they neither advance nor reset the sequence) + +Modifier-only events, IME composition, and automatic keydown repeats neither advance a sequence nor refresh its timeout: holding G does not complete a two-press G sequence. Exact matches take priority over weaker logical-key fallbacks. ### Overlapping sequences -Multiple sequences can share the same prefix. The manager tracks progress for each sequence independently: +Multiple sequences can share a prefix. Progress is tracked for each sequence independently: ```tsx -// Both share the 'D' prefix -useHotkeySequence(['D', 'D'], () => deleteLine()) // dd -useHotkeySequence(['D', 'W'], () => deleteWord()) // dw -useHotkeySequence(['D', 'I', 'W'], () => deleteInnerWord()) // diw +useHotkeys([ + { hotkey: ['D', 'D'], callback: () => deleteLine() }, // dd + { hotkey: ['D', 'W'], callback: () => deleteWord() }, // dw + { hotkey: ['D', 'I', 'W'], callback: () => deleteInnerWord() }, // diw +]) ``` -After pressing `D`, the manager waits for the next key to determine which sequence to complete. +After pressing `D`, the manager waits for the next key to determine which sequence completes. ### Conflicting registrations -For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate bindings. `conflictBehavior` applies to equivalent sequences without rewriting their stored strings. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection also checks prefixes and observed physical/logical overlap. +For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate hotkeys. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection and app hotkey conflicts are stricter: there, a sequence also conflicts with a chord equal to its first step and with any sequence it starts. -## The sequence manager +## Standalone matching -Under the hood, `useHotkeySequence` uses the singleton `SequenceManager`. For standalone sequence matching without the singleton, use the core `createSequenceMatcher` function: +For sequence matching without the singleton manager, use `createSequenceMatcher`: ```tsx import { createSequenceMatcher } from '@tanstack/react-hotkeys' -const matcher = createSequenceMatcher(['G', 'G'], { - timeout: 1000, -}) +const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 }) document.addEventListener('keydown', (e) => { if (matcher.match(e)) { console.log('Sequence completed!') } - console.log('Progress:', matcher.getProgress()) // e.g., 1/2 + console.log('Progress:', matcher.getProgress()) // e.g., 1 }) ``` diff --git a/docs/framework/react/quick-start.md b/docs/framework/react/quick-start.md index 768a408f..031e2aa4 100644 --- a/docs/framework/react/quick-start.md +++ b/docs/framework/react/quick-start.md @@ -85,14 +85,14 @@ function Modal({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) { ### Multi-key sequences -Register Vim-style key sequences with `useHotkeySequence`: +Pass an array of chords to register a Vim-style sequence: ```tsx -import { useHotkeySequence } from '@tanstack/react-hotkeys' +import { useHotkey } from '@tanstack/react-hotkeys' function VimStyleApp() { - useHotkeySequence(['G', 'G'], () => scrollToTop()) - useHotkeySequence(['G', 'Shift+G'], () => scrollToBottom()) + useHotkey(['G', 'G'], () => scrollToTop()) + useHotkey(['G', 'Shift+G'], () => scrollToBottom()) return
    Use gg to go to top, gG to go to bottom
    } @@ -131,7 +131,7 @@ function SaveButton() { return ( ) } @@ -155,9 +155,43 @@ function App() { } ``` +## Declaring app hotkeys + +To list every hotkey in a cheat sheet, including those on routes that have not rendered, and to let users customize them, declare them once and pass them to `HotkeysProvider`: + +```tsx +import { + HotkeysProvider, + defineAppHotkeys, + useAppHotkeys, + useHotkey, +} from '@tanstack/react-hotkeys' + +const appHotkeys = defineAppHotkeys({ + 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save' } }, + 'nav.home': { hotkey: ['G', 'H'], meta: { name: 'Go home' } }, +}) + +function Root() { + return ( + + + + ) +} + +function Editor() { + const hotkeys = useAppHotkeys() + useHotkey(hotkeys['doc.save'], () => save()) + return
    Editor
    +} +``` + +See the [App Hotkeys Guide](./guides/app-hotkeys) for cheat sheets and saving custom hotkeys. + ## Default options provider -Wrap your app with `HotkeysProvider` to set default options for all hotkey hooks globally. Options passed directly to a hook override the provider defaults. +`HotkeysProvider` also sets default options for every hotkey hook. Options passed directly to a hook override the provider defaults. ```tsx import { HotkeysProvider } from '@tanstack/react-hotkeys' @@ -166,8 +200,7 @@ function Root() { return ( console.log('Recording cancelled') }, }} > @@ -181,7 +214,7 @@ function Root() { - [Hotkeys Guide](./guides/hotkeys) - Deep dive into `useHotkey` options and patterns - [Sequences Guide](./guides/sequences) - Multi-key sequence handling -- [Hotkey Recording Guide](./guides/hotkey-recording) - Building shortcut customization UIs -- [Sequence Recording Guide](./guides/sequence-recording) +- [App Hotkeys Guide](./guides/app-hotkeys) - Declaring, listing, and customizing every hotkey +- [Hotkey Recording Guide](./guides/hotkey-recording) - Recording chords and sequences in settings UIs - [Key State Tracking Guide](./guides/key-state-tracking) - Real-time key state monitoring - [Formatting & Display Guide](./guides/formatting-display) - Platform-aware hotkey formatting diff --git a/docs/framework/react/reference/functions/HotkeysProvider.md b/docs/framework/react/reference/functions/HotkeysProvider.md index e0b17212..1ec7ea7c 100644 --- a/docs/framework/react/reference/functions/HotkeysProvider.md +++ b/docs/framework/react/reference/functions/HotkeysProvider.md @@ -4,17 +4,39 @@ title: HotkeysProvider --- ```ts -function HotkeysProvider(__namedParameters): Element; +function HotkeysProvider(__namedParameters): Element; ``` -Defined in: [HotkeysProvider.tsx:30](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L30) +Defined in: [HotkeysProvider.tsx:75](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L75) + +Provides default options, app hotkeys, and the user's custom hotkeys. + +## Type Parameters + +### T + +`T` *extends* `Readonly`\<`Record`\<`string`, `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>\> = `Readonly`\<`Record`\<`string`, `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>\> ## Parameters ### \_\_namedParameters -[`HotkeysProviderProps`](../interfaces/HotkeysProviderProps.md) +[`HotkeysProviderProps`](../interfaces/HotkeysProviderProps.md)\<`T`\> ## Returns `Element` + +## Example + +```tsx + + localStorage.setItem('hotkeys', JSON.stringify(custom)) + } +> + + +``` diff --git a/docs/framework/react/reference/functions/useAppHotkeyList.md b/docs/framework/react/reference/functions/useAppHotkeyList.md new file mode 100644 index 00000000..1ced3d75 --- /dev/null +++ b/docs/framework/react/reference/functions/useAppHotkeyList.md @@ -0,0 +1,36 @@ +--- +id: useAppHotkeyList +title: useAppHotkeyList +--- + +```ts +function useAppHotkeyList(): AppHotkeyListItem>[]; +``` + +Defined in: useAppHotkeys.ts:61 + +Lists every app hotkey with its live status, in declaration order, for +cheat sheets and settings screens. App hotkeys whose components have not +mounted are `'inactive'`; those without a hotkey are `'unbound'`. + +## Returns + +`AppHotkeyListItem`\<`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>[] + +## Example + +```tsx +function CheatSheet() { + const items = useAppHotkeyList() + return ( +
      + {items.map(({ appHotkey, status }) => ( +
    • + {appHotkey.meta?.name}:{' '} + {appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned'} +
    • + ))} +
    + ) +} +``` diff --git a/docs/framework/react/reference/functions/useAppHotkeys.md b/docs/framework/react/reference/functions/useAppHotkeys.md new file mode 100644 index 00000000..ec0e0e5e --- /dev/null +++ b/docs/framework/react/reference/functions/useAppHotkeys.md @@ -0,0 +1,25 @@ +--- +id: useAppHotkeys +title: useAppHotkeys +--- + +```ts +function useAppHotkeys(): RegisteredAppHotkeys; +``` + +Defined in: useAppHotkeys.ts:35 + +Returns the effective app hotkeys from `HotkeysProvider`, typed by +`Register`: each declaration with the user's custom hotkey applied. +Pass an entry to `useHotkey` or `useHotkeys` to register it. + +## Returns + +`RegisteredAppHotkeys` + +## Example + +```tsx +const hotkeys = useAppHotkeys() +useHotkey(hotkeys['doc.save'], () => save()) +``` diff --git a/docs/framework/react/reference/functions/useCustomHotkeys.md b/docs/framework/react/reference/functions/useCustomHotkeys.md new file mode 100644 index 00000000..52eefd2e --- /dev/null +++ b/docs/framework/react/reference/functions/useCustomHotkeys.md @@ -0,0 +1,30 @@ +--- +id: useCustomHotkeys +title: useCustomHotkeys +--- + +```ts +function useCustomHotkeys(): UseCustomHotkeysResult; +``` + +Defined in: useAppHotkeys.ts:108 + +Reads and changes the user's custom hotkeys. Changes apply to every +registered app hotkey immediately and are passed to the provider's +`onCustomHotkeysChange`, ready to save. + +## Returns + +[`UseCustomHotkeysResult`](../interfaces/UseCustomHotkeysResult.md) + +## Example + +```tsx +const { setCustomHotkey, findConflicts } = useCustomHotkeys() +const recorder = useHotkeyRecorder({ + maxSteps: 3, + validate: (hotkey) => + findConflicts('doc.save', hotkey).length === 0 || 'Already in use', + onRecord: (hotkey) => setCustomHotkey('doc.save', hotkey), +}) +``` diff --git a/docs/framework/react/reference/functions/useDefaultHotkeysOptions.md b/docs/framework/react/reference/functions/useDefaultHotkeysOptions.md index 2ccb7409..32f1b9cf 100644 --- a/docs/framework/react/reference/functions/useDefaultHotkeysOptions.md +++ b/docs/framework/react/reference/functions/useDefaultHotkeysOptions.md @@ -7,7 +7,7 @@ title: useDefaultHotkeysOptions function useDefaultHotkeysOptions(): HotkeysProviderOptions; ``` -Defined in: [HotkeysProvider.tsx:54](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L54) +Defined in: [HotkeysProvider.tsx:126](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L126) ## Returns diff --git a/docs/framework/react/reference/functions/useHotkey.md b/docs/framework/react/reference/functions/useHotkey.md index 2ea9f2d1..6a00337e 100644 --- a/docs/framework/react/reference/functions/useHotkey.md +++ b/docs/framework/react/reference/functions/useHotkey.md @@ -3,8 +3,10 @@ id: useHotkey title: useHotkey --- +## Call Signature + ```ts -function useHotkey( +function useHotkey( hotkey, callback, options?): void; @@ -12,79 +14,143 @@ function useHotkey( Defined in: [useHotkey.ts:85](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkey.ts#L85) -React hook for registering a keyboard hotkey. +React hook for registering a hotkey: a chord such as `'Mod+S'`, a sequence +such as `['G', 'G']`, or an app hotkey from `defineAppHotkeys`. + +Uses the singleton HotkeyManager for efficient event handling. The callback +and options sync on every render, so callbacks that reference React state +always see the latest values. `enabled: false` keeps the registration +(visible in devtools) and only suppresses firing. + +Chords accept `requireReset`; sequences accept `timeout`. A one-step +sequence registers as its chord. An app hotkey uses its effective hotkey +from `HotkeysProvider` (including the user's custom hotkey) and its declared +meta and options, and registers nothing while unbound. -Uses the singleton HotkeyManager for efficient event handling. -The callback receives both the keyboard event and a context object -containing the hotkey string and parsed hotkey. +Options merge in this order: provider defaults, app hotkey declaration, +call-site options. -This hook syncs the callback and options on every render to avoid -stale closures. This means -callbacks that reference React state will always have access to -the latest values. +### Type Parameters -## Parameters +#### T -### hotkey +`T` *extends* `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\> -`RegisterableHotkey` +### Parameters -The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object (supports `mod` for cross-platform) +#### hotkey -### callback +`T` + +A chord string, RawHotkey object, sequence, or app hotkey + +#### callback `HotkeyCallback` The function to call when the hotkey is pressed -### options? +#### options? -[`UseHotkeyOptions`](../interfaces/UseHotkeyOptions.md) = `{}` +[`UseAppHotkeyOptions`](../type-aliases/UseAppHotkeyOptions.md)\<`T`\> -Options for the hotkey behavior. `enabled: false` keeps the registration (visible in devtools) - and only suppresses firing; the hook updates the existing handle instead of unregistering. +Options for the hotkey behavior -## Returns +### Returns `void` -## Examples +### Examples ```tsx -function SaveButton() { +function Editor() { const [count, setCount] = useState(0) - // Callback always has access to latest count value - useHotkey('Mod+S', (event, { hotkey }) => { - console.log(`Save triggered, count is ${count}`) - handleSave() - }) - - return + useHotkey('Mod+S', () => save(count)) + useHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 }) + useHotkey('Escape', () => close(), { enabled: isOpen }) } ``` ```tsx -function Modal({ isOpen, onClose }) { - // enabled option is synced on every render - useHotkey('Escape', () => { - onClose() - }, { enabled: isOpen }) - - if (!isOpen) return null - return
    ...
    +function Toolbar() { + const hotkeys = useAppHotkeys() + useHotkey(hotkeys['doc.save'], () => save()) } ``` +## Call Signature + +```ts +function useHotkey( + hotkey, + callback, + options?): void; +``` + +Defined in: [useHotkey.ts:90](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkey.ts#L90) + +React hook for registering a hotkey: a chord such as `'Mod+S'`, a sequence +such as `['G', 'G']`, or an app hotkey from `defineAppHotkeys`. + +Uses the singleton HotkeyManager for efficient event handling. The callback +and options sync on every render, so callbacks that reference React state +always see the latest values. `enabled: false` keeps the registration +(visible in devtools) and only suppresses firing. + +Chords accept `requireReset`; sequences accept `timeout`. A one-step +sequence registers as its chord. An app hotkey uses its effective hotkey +from `HotkeysProvider` (including the user's custom hotkey) and its declared +meta and options, and registers nothing while unbound. + +Options merge in this order: provider defaults, app hotkey declaration, +call-site options. + +### Type Parameters + +#### T + +`T` *extends* `RegisterableHotkey` + +### Parameters + +#### hotkey + +`T` + +A chord string, RawHotkey object, sequence, or app hotkey + +#### callback + +`HotkeyCallback` + +The function to call when the hotkey is pressed + +#### options? + +`AdapterOptionsFor`\<`T`, [`UseHotkeyTarget`](../type-aliases/UseHotkeyTarget.md)\> + +Options for the hotkey behavior + +### Returns + +`void` + +### Examples + ```tsx function Editor() { - const editorRef = useRef(null) + const [count, setCount] = useState(0) - // Scoped to a specific element - useHotkey('Mod+S', () => { - save() - }, { target: editorRef }) + useHotkey('Mod+S', () => save(count)) + useHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 }) + useHotkey('Escape', () => close(), { enabled: isOpen }) +} +``` - return
    ...
    +```tsx +function Toolbar() { + const hotkeys = useAppHotkeys() + useHotkey(hotkeys['doc.save'], () => save()) } ``` diff --git a/docs/framework/react/reference/functions/useHotkeyHint.md b/docs/framework/react/reference/functions/useHotkeyHint.md index 73da8319..ee3e8cc6 100644 --- a/docs/framework/react/reference/functions/useHotkeyHint.md +++ b/docs/framework/react/reference/functions/useHotkeyHint.md @@ -7,19 +7,22 @@ title: useHotkeyHint function useHotkeyHint(hotkey, options?): boolean; ``` -Defined in: [useHotkeyHint.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyHint.ts#L12) +Defined in: [useHotkeyHint.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyHint.ts#L24) Returns a boolean indicating whether held modifiers reveal this shortcut. Only visibility changes from held-key updates cause a rerender. Uses the nonempty-subset/AltGr rules of `matchesHeldModifiers`; `exact` requires -every modifier. Combine with the action's enabled state before showing a badge. -This helper does not register a shortcut or check whether its target is focused. +every modifier. A sequence reveals its hint from its first step, and an app +hotkey uses its effective hotkey (false while unbound). Combine with the +action's enabled state before showing a badge. This helper does not register +a shortcut or check whether its target is focused. ## Parameters ### hotkey -`RegisterableHotkey` + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\> ### options? diff --git a/docs/framework/react/reference/functions/useHotkeyRecorder.md b/docs/framework/react/reference/functions/useHotkeyRecorder.md index 25cadc6a..dd72bf96 100644 --- a/docs/framework/react/reference/functions/useHotkeyRecorder.md +++ b/docs/framework/react/reference/functions/useHotkeyRecorder.md @@ -3,57 +3,112 @@ id: useHotkeyRecorder title: useHotkeyRecorder --- +## Call Signature + +```ts +function useHotkeyRecorder(options): ReactHotkeyRecorder; +``` + +Defined in: [useHotkeyRecorder.ts:68](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L68) + +React hook for recording keyboard shortcuts. + +With the default `maxSteps: 1`, the first valid chord is recorded and +`onRecord` receives a chord such as `'Mod+S'`. With a larger `maxSteps`, each +chord adds a step; the recording commits on Enter (unless +`commitKeys: 'none'`), after `idleTimeoutMs`, or when it reaches `maxSteps`, +and `onRecord` receives a chord (one step) or a sequence. + +Escape cancels. Backspace/Delete removes the last step, or calls `onClear` +when nothing is recorded. + +### Parameters + +#### options + +`HotkeyChordRecorderOptions` + +Configuration options for the recorder + +### Returns + +[`ReactHotkeyRecorder`](../interfaces/ReactHotkeyRecorder.md)\<`HotkeyChord`\> + +The recorder state and controls + +### Examples + +```tsx +function ShortcutInput() { + const [shortcut, setShortcut] = useState('Mod+S') + const recorder = useHotkeyRecorder({ onRecord: setShortcut }) + + return ( + + ) +} +``` + +```tsx +const recorder = useHotkeyRecorder({ + maxSteps: 3, + onRecord: (hotkey) => setShortcut(hotkey), // 'Mod+S' or ['G', 'H'] +}) +``` + +## Call Signature + ```ts function useHotkeyRecorder(options): ReactHotkeyRecorder; ``` -Defined in: [useHotkeyRecorder.ts:58](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L58) +Defined in: [useHotkeyRecorder.ts:71](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L71) React hook for recording keyboard shortcuts. -This hook 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. +With the default `maxSteps: 1`, the first valid chord is recorded and +`onRecord` receives a chord such as `'Mod+S'`. With a larger `maxSteps`, each +chord adds a step; the recording commits on Enter (unless +`commitKeys: 'none'`), after `idleTimeoutMs`, or when it reaches `maxSteps`, +and `onRecord` receives a chord (one step) or a sequence. + +Escape cancels. Backspace/Delete removes the last step, or calls `onClear` +when nothing is recorded. -## Parameters +### Parameters -### options +#### options -`HotkeyRecorderOptions` +`HotkeySequenceRecorderOptions` Configuration options for the recorder -## Returns +### Returns [`ReactHotkeyRecorder`](../interfaces/ReactHotkeyRecorder.md) -An object with recording state and control functions +The recorder state and controls -## Example +### Examples ```tsx -function ShortcutSettings() { +function ShortcutInput() { const [shortcut, setShortcut] = useState('Mod+S') - - const recorder = useHotkeyRecorder({ - onRecord: (hotkey) => { - setShortcut(hotkey) - }, - onCancel: () => { - console.log('Recording cancelled') - }, - }) + const recorder = useHotkeyRecorder({ onRecord: setShortcut }) return ( -
    - - {recorder.recordedHotkey && ( -
    Recording: {recorder.recordedHotkey}
    - )} -
    + ) } ``` + +```tsx +const recorder = useHotkeyRecorder({ + maxSteps: 3, + onRecord: (hotkey) => setShortcut(hotkey), // 'Mod+S' or ['G', 'H'] +}) +``` diff --git a/docs/framework/react/reference/functions/useHotkeyRegistrations.md b/docs/framework/react/reference/functions/useHotkeyRegistrations.md index 943bced8..3624bdff 100644 --- a/docs/framework/react/reference/functions/useHotkeyRegistrations.md +++ b/docs/framework/react/reference/functions/useHotkeyRegistrations.md @@ -4,40 +4,34 @@ title: useHotkeyRegistrations --- ```ts -function useHotkeyRegistrations(): HotkeyRegistrationsResult; +function useHotkeyRegistrations(): HotkeyRegistrationView[]; ``` -Defined in: [useHotkeyRegistrations.ts:54](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRegistrations.ts#L54) +Defined in: [useHotkeyRegistrations.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRegistrations.ts#L31) -React hook that reactively reads all hotkey and sequence registrations -from the singleton managers. +React hook that reactively reads every hotkey registration, chords and +sequences, from the singleton HotkeyManager. Narrow each view with `kind`. -This is a standalone hook that does NOT require the HotkeysProvider. -It subscribes to both HotkeyManager and SequenceManager stores and -re-renders when registrations change. +This hook does NOT require the HotkeysProvider. It re-renders when +registrations change, including trigger counts and sequence progress. ## Returns -[`HotkeyRegistrationsResult`](../interfaces/HotkeyRegistrationsResult.md) +`HotkeyRegistrationView`[] -Object with `hotkeys` and `sequences` arrays +All registrations, without callbacks ## Example ```tsx function ShortcutPalette() { - const { hotkeys, sequences } = useHotkeyRegistrations() + const registrations = useHotkeyRegistrations() return (
      - {hotkeys.map((reg) => ( + {registrations.map((reg) => (
    • - {reg.options.meta?.name ?? reg.hotkey} -
    • - ))} - {sequences.map((reg) => ( -
    • - {reg.options.meta?.name ?? reg.sequence.join(' ')} + {reg.options.meta?.name}: {formatForDisplay(reg.hotkey)}
    • ))}
    diff --git a/docs/framework/react/reference/functions/useHotkeySequence.md b/docs/framework/react/reference/functions/useHotkeySequence.md deleted file mode 100644 index ae1bf353..00000000 --- a/docs/framework/react/reference/functions/useHotkeySequence.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -id: useHotkeySequence -title: useHotkeySequence ---- - -```ts -function useHotkeySequence( - sequence, - callback, - options?): void; -``` - -Defined in: [useHotkeySequence.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequence.ts#L69) - -React hook for registering a keyboard shortcut sequence (Vim-style). - -This hook allows you to register multi-key sequences like 'g g' or 'd d' -that trigger when the full sequence is pressed within a timeout. - -Each step may include modifiers. You can chain the same modifier across -steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift, -Control, Alt, or Meta pressed alone) are ignored while matching—they do not -advance the sequence or reset progress. - -## Parameters - -### sequence - -`HotkeySequence` - -Array of hotkey strings that form the sequence - -### callback - -`HotkeyCallback` - -Function to call when the sequence is completed - -### options? - -[`UseHotkeySequenceOptions`](../interfaces/UseHotkeySequenceOptions.md) = `{}` - -Options for the sequence behavior. `enabled: false` keeps the registration (visible in devtools) - and only suppresses firing; the hook updates the existing handle instead of unregistering. - -## Returns - -`void` - -## Example - -```tsx -function VimEditor() { - // 'g g' to go to top - useHotkeySequence(['G', 'G'], () => { - scrollToTop() - }) - - // 'd d' to delete line - useHotkeySequence(['D', 'D'], () => { - deleteLine() - }) - - // 'd i w' to delete inner word - useHotkeySequence(['D', 'I', 'W'], () => { - deleteInnerWord() - }, { timeout: 500 }) - - // Same modifier on consecutive steps (bare Shift between chords is ignored) - useHotkeySequence(['Shift+R', 'Shift+T'], () => { - nextAction() - }) - - return
    ...
    -} -``` diff --git a/docs/framework/react/reference/functions/useHotkeySequenceRecorder.md b/docs/framework/react/reference/functions/useHotkeySequenceRecorder.md deleted file mode 100644 index 35380c35..00000000 --- a/docs/framework/react/reference/functions/useHotkeySequenceRecorder.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -id: useHotkeySequenceRecorder -title: useHotkeySequenceRecorder ---- - -```ts -function useHotkeySequenceRecorder(options): ReactHotkeySequenceRecorder; -``` - -Defined in: [useHotkeySequenceRecorder.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequenceRecorder.ts#L29) - -React hook for recording multi-chord sequences (Vim-style shortcuts). - -## Parameters - -### options - -`HotkeySequenceRecorderOptions` - -Configuration options for the hotkey sequence recorder - -## Returns - -[`ReactHotkeySequenceRecorder`](../interfaces/ReactHotkeySequenceRecorder.md) diff --git a/docs/framework/react/reference/functions/useHotkeySequences.md b/docs/framework/react/reference/functions/useHotkeySequences.md deleted file mode 100644 index 93c0a6cb..00000000 --- a/docs/framework/react/reference/functions/useHotkeySequences.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -id: useHotkeySequences -title: useHotkeySequences ---- - -```ts -function useHotkeySequences(definitions, commonOptions?): void; -``` - -Defined in: [useHotkeySequences.ts:68](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequences.ts#L68) - -React hook for registering multiple keyboard shortcut sequences at once (Vim-style). - -Uses the singleton SequenceManager. Accepts a dynamic array of definitions so you can -register variable-length lists without violating the rules of hooks. - -Options are merged in this order: -HotkeysProvider defaults < commonOptions < per-definition options - -Callbacks and options are synced on every render to avoid stale closures. - -Definitions with an empty `sequence` are skipped (no registration). - -## Parameters - -### definitions - -[`UseHotkeySequenceDefinition`](../interfaces/UseHotkeySequenceDefinition.md)[] - -Array of sequence definitions to register - -### commonOptions? - -[`UseHotkeySequenceOptions`](../interfaces/UseHotkeySequenceOptions.md) = `{}` - -Shared options applied to all sequences (overridden by per-definition options). - Per-row `enabled: false` still registers that sequence: `SequenceManager` suppresses execution only (the row - stays in the store and appears in TanStack Hotkeys devtools). Toggling `enabled` updates the existing handle - via `setOptions` (no unregister/re-register churn). - -## Returns - -`void` - -## Examples - -```tsx -function VimPalette() { - useHotkeySequences([ - { sequence: ['G', 'G'], callback: () => scrollToTop() }, - { sequence: ['D', 'D'], callback: () => deleteLine() }, - { sequence: ['C', 'I', 'W'], callback: () => changeInnerWord(), options: { timeout: 500 } }, - ]) -} -``` - -```tsx -function DynamicSequences({ items }) { - useHotkeySequences( - items.map((item) => ({ - sequence: item.chords, - callback: item.action, - options: { enabled: item.enabled }, - })), - { preventDefault: true }, - ) -} -``` diff --git a/docs/framework/react/reference/functions/useHotkeys.md b/docs/framework/react/reference/functions/useHotkeys.md index 5e311055..d9966dda 100644 --- a/docs/framework/react/reference/functions/useHotkeys.md +++ b/docs/framework/react/reference/functions/useHotkeys.md @@ -7,35 +7,32 @@ title: useHotkeys function useHotkeys(hotkeys, commonOptions?): void; ``` -Defined in: [useHotkeys.ts:73](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeys.ts#L73) +Defined in: [useHotkeys.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeys.ts#L59) -React hook for registering multiple keyboard hotkeys at once. +React hook for registering several hotkeys at once: chords, sequences, and +app hotkeys. Accepts a dynamic array, so it is safe to use with +variable-length lists without violating the rules of hooks. -Uses the singleton HotkeyManager for efficient event handling. -Accepts a dynamic array of hotkey definitions, making it safe to use -with variable-length lists without violating the rules of hooks. +Options merge in this order: provider defaults, app hotkey declaration, +commonOptions, per-hotkey options. Each option applies only to the kinds +that support it. Callbacks and options sync on every render. -Options are merged in this order: -HotkeysProvider defaults < commonOptions < per-definition options - -Callbacks and options are synced on every render to avoid stale closures. +Per-row `enabled: false` keeps that registration (visible in devtools) and +only suppresses firing. Unbound app hotkeys and empty sequences register nothing. ## Parameters ### hotkeys -[`UseHotkeyDefinition`](../interfaces/UseHotkeyDefinition.md)[] +readonly [`UseHotkeyDefinition`](../type-aliases/UseHotkeyDefinition.md)[] -Array of hotkey definitions to register +Hotkeys to register ### commonOptions? -[`UseHotkeyOptions`](../interfaces/UseHotkeyOptions.md) = `{}` +[`UseHotkeyCommonOptions`](../type-aliases/UseHotkeyCommonOptions.md) = `{}` -Shared options applied to all hotkeys (overridden by per-definition options). - Per-row `enabled: false` still registers that hotkey: `HotkeyManager` suppresses execution only (the row - stays in the store and appears in TanStack Hotkeys devtools). Toggling `enabled` updates the existing handle - via `setOptions` (no unregister/re-register churn). +Options applied to every hotkey (overridden by per-hotkey options) ## Returns @@ -47,7 +44,7 @@ Shared options applied to all hotkeys (overridden by per-definition options). function Editor() { useHotkeys([ { hotkey: 'Mod+S', callback: () => save() }, - { hotkey: 'Mod+Z', callback: () => undo() }, + { hotkey: ['G', 'G'], callback: () => scrollToTop(), options: { timeout: 500 } }, { hotkey: 'Escape', callback: () => close() }, ]) } diff --git a/docs/framework/react/reference/functions/useHotkeysContext.md b/docs/framework/react/reference/functions/useHotkeysContext.md index f84f7aa2..8700c2d6 100644 --- a/docs/framework/react/reference/functions/useHotkeysContext.md +++ b/docs/framework/react/reference/functions/useHotkeysContext.md @@ -7,7 +7,7 @@ title: useHotkeysContext function useHotkeysContext(): HotkeysContextValue | null; ``` -Defined in: [HotkeysProvider.tsx:49](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L49) +Defined in: [HotkeysProvider.tsx:121](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L121) ## Returns diff --git a/docs/framework/react/reference/index.md b/docs/framework/react/reference/index.md index 3f3e15be..c0f1be30 100644 --- a/docs/framework/react/reference/index.md +++ b/docs/framework/react/reference/index.md @@ -5,19 +5,28 @@ title: "@tanstack/react-hotkeys" ## Interfaces -- [HotkeyRegistrationsResult](interfaces/HotkeyRegistrationsResult.md) - [HotkeysProviderOptions](interfaces/HotkeysProviderOptions.md) - [HotkeysProviderProps](interfaces/HotkeysProviderProps.md) - [ReactHotkeyRecorder](interfaces/ReactHotkeyRecorder.md) -- [ReactHotkeySequenceRecorder](interfaces/ReactHotkeySequenceRecorder.md) -- [UseHotkeyDefinition](interfaces/UseHotkeyDefinition.md) -- [UseHotkeyOptions](interfaces/UseHotkeyOptions.md) -- [UseHotkeySequenceDefinition](interfaces/UseHotkeySequenceDefinition.md) -- [UseHotkeySequenceOptions](interfaces/UseHotkeySequenceOptions.md) +- [UseCustomHotkeysResult](interfaces/UseCustomHotkeysResult.md) + +## Type Aliases + +- [UseAppHotkeyOptions](type-aliases/UseAppHotkeyOptions.md) +- [UseHotkeyChordOptions](type-aliases/UseHotkeyChordOptions.md) +- [UseHotkeyCommonOptions](type-aliases/UseHotkeyCommonOptions.md) +- [UseHotkeyDefinition](type-aliases/UseHotkeyDefinition.md) +- [UseHotkeyOptions](type-aliases/UseHotkeyOptions.md) +- [UseHotkeyOptionsFor](type-aliases/UseHotkeyOptionsFor.md) +- [UseHotkeySequenceOptions](type-aliases/UseHotkeySequenceOptions.md) +- [UseHotkeyTarget](type-aliases/UseHotkeyTarget.md) ## Functions - [HotkeysProvider](functions/HotkeysProvider.md) +- [useAppHotkeyList](functions/useAppHotkeyList.md) +- [useAppHotkeys](functions/useAppHotkeys.md) +- [useCustomHotkeys](functions/useCustomHotkeys.md) - [useDefaultHotkeysOptions](functions/useDefaultHotkeysOptions.md) - [useHeldKeyCodes](functions/useHeldKeyCodes.md) - [useHeldKeys](functions/useHeldKeys.md) @@ -27,7 +36,4 @@ title: "@tanstack/react-hotkeys" - [useHotkeyRegistrations](functions/useHotkeyRegistrations.md) - [useHotkeys](functions/useHotkeys.md) - [useHotkeysContext](functions/useHotkeysContext.md) -- [useHotkeySequence](functions/useHotkeySequence.md) -- [useHotkeySequenceRecorder](functions/useHotkeySequenceRecorder.md) -- [useHotkeySequences](functions/useHotkeySequences.md) - [useKeyHold](functions/useKeyHold.md) diff --git a/docs/framework/react/reference/interfaces/HotkeyRegistrationsResult.md b/docs/framework/react/reference/interfaces/HotkeyRegistrationsResult.md deleted file mode 100644 index 05b0e482..00000000 --- a/docs/framework/react/reference/interfaces/HotkeyRegistrationsResult.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -id: HotkeyRegistrationsResult -title: HotkeyRegistrationsResult ---- - -Defined in: [useHotkeyRegistrations.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRegistrations.ts#L15) - -Return type for useHotkeyRegistrations. - -## Properties - -### hotkeys - -```ts -hotkeys: HotkeyRegistrationView[]; -``` - -Defined in: [useHotkeyRegistrations.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRegistrations.ts#L17) - -All registered hotkeys (public view, no callbacks) - -*** - -### sequences - -```ts -sequences: SequenceRegistrationView[]; -``` - -Defined in: [useHotkeyRegistrations.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRegistrations.ts#L19) - -All registered sequences diff --git a/docs/framework/react/reference/interfaces/HotkeysProviderOptions.md b/docs/framework/react/reference/interfaces/HotkeysProviderOptions.md index 690fed24..af290c6e 100644 --- a/docs/framework/react/reference/interfaces/HotkeysProviderOptions.md +++ b/docs/framework/react/reference/interfaces/HotkeysProviderOptions.md @@ -3,44 +3,29 @@ id: HotkeysProviderOptions title: HotkeysProviderOptions --- -Defined in: [HotkeysProvider.tsx:10](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L10) +Defined in: [HotkeysProvider.tsx:18](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L18) ## Properties ### hotkey? ```ts -optional hotkey?: Partial; +optional hotkey?: UseHotkeyCommonOptions; ``` -Defined in: [HotkeysProvider.tsx:11](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L11) +Defined in: [HotkeysProvider.tsx:23](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L23) -*** - -### hotkeyRecorder? - -```ts -optional hotkeyRecorder?: Partial; -``` - -Defined in: [HotkeysProvider.tsx:12](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L12) +Defaults for every hotkey registered by `useHotkey` and `useHotkeys`. +`requireReset` applies to chords and `timeout` applies to sequences. *** -### hotkeySequence? +### hotkeyRecorder? ```ts -optional hotkeySequence?: Partial; +optional hotkeyRecorder?: Partial>; ``` -Defined in: [HotkeysProvider.tsx:13](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L13) - -*** - -### hotkeySequenceRecorder? - -```ts -optional hotkeySequenceRecorder?: Partial; -``` +Defined in: [HotkeysProvider.tsx:25](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L25) -Defined in: [HotkeysProvider.tsx:14](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L14) +Defaults for every `useHotkeyRecorder`. diff --git a/docs/framework/react/reference/interfaces/HotkeysProviderProps.md b/docs/framework/react/reference/interfaces/HotkeysProviderProps.md index 5f04a9a9..40a22c83 100644 --- a/docs/framework/react/reference/interfaces/HotkeysProviderProps.md +++ b/docs/framework/react/reference/interfaces/HotkeysProviderProps.md @@ -3,17 +3,49 @@ id: HotkeysProviderProps title: HotkeysProviderProps --- -Defined in: [HotkeysProvider.tsx:23](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L23) +Defined in: [HotkeysProvider.tsx:40](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L40) + +## Type Parameters + +### T + +`T` *extends* `AppHotkeys` = `AppHotkeys` ## Properties +### appHotkeys? + +```ts +optional appHotkeys?: T; +``` + +Defined in: [HotkeysProvider.tsx:46](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L46) + +Every app hotkey, from `defineAppHotkeys`. Listed by `useAppHotkeyList` +whether or not their components have mounted. + +*** + ### children ```ts children: ReactNode; ``` -Defined in: [HotkeysProvider.tsx:24](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L24) +Defined in: [HotkeysProvider.tsx:41](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L41) + +*** + +### customHotkeys? + +```ts +optional customHotkeys?: CustomHotkeys | null; +``` + +Defined in: [HotkeysProvider.tsx:51](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L51) + +Starting custom hotkeys, such as saved data. Read once; the provider owns +the state afterwards. Invalid entries are ignored with a warning. *** @@ -23,4 +55,26 @@ Defined in: [HotkeysProvider.tsx:24](https://github.com/TanStack/hotkeys/blob/ma optional defaultOptions?: HotkeysProviderOptions; ``` -Defined in: [HotkeysProvider.tsx:25](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L25) +Defined in: [HotkeysProvider.tsx:54](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L54) + +*** + +### onCustomHotkeysChange? + +```ts +optional onCustomHotkeysChange?: (customHotkeys) => void; +``` + +Defined in: [HotkeysProvider.tsx:53](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/HotkeysProvider.tsx#L53) + +Called with the custom hotkeys after every change, ready to save. + +#### Parameters + +##### customHotkeys + +`CustomHotkeys`\<`T`\> + +#### Returns + +`void` diff --git a/docs/framework/react/reference/interfaces/ReactHotkeyRecorder.md b/docs/framework/react/reference/interfaces/ReactHotkeyRecorder.md index 9b9b1e4c..8b932f7c 100644 --- a/docs/framework/react/reference/interfaces/ReactHotkeyRecorder.md +++ b/docs/framework/react/reference/interfaces/ReactHotkeyRecorder.md @@ -3,7 +3,13 @@ id: ReactHotkeyRecorder title: ReactHotkeyRecorder --- -Defined in: [useHotkeyRecorder.ts:7](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L7) +Defined in: [useHotkeyRecorder.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L14) + +## Type Parameters + +### THotkey + +`THotkey` *extends* `Hotkey` = `Hotkey` ## Properties @@ -13,9 +19,25 @@ Defined in: [useHotkeyRecorder.ts:7](https://github.com/TanStack/hotkeys/blob/ma cancelRecording: () => void; ``` -Defined in: [useHotkeyRecorder.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L17) +Defined in: [useHotkeyRecorder.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L26) -Cancel recording without saving +Cancel recording and call onCancel + +#### Returns + +`void` + +*** + +### commitRecording + +```ts +commitRecording: () => void; +``` + +Defined in: [useHotkeyRecorder.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L28) + +Commit the recorded steps now (multi-step recordings) #### Returns @@ -29,7 +51,7 @@ Cancel recording without saving isRecording: boolean; ``` -Defined in: [useHotkeyRecorder.ts:9](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L9) +Defined in: [useHotkeyRecorder.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L16) Whether recording is currently active @@ -38,12 +60,12 @@ Whether recording is currently active ### recordedHotkey ```ts -recordedHotkey: Hotkey | null; +recordedHotkey: THotkey | null; ``` -Defined in: [useHotkeyRecorder.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L11) +Defined in: [useHotkeyRecorder.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L20) -The currently recorded hotkey (for live preview) +The last committed hotkey *** @@ -53,7 +75,7 @@ The currently recorded hotkey (for live preview) startRecording: () => void; ``` -Defined in: [useHotkeyRecorder.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L13) +Defined in: [useHotkeyRecorder.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L22) Start recording a new hotkey @@ -63,15 +85,27 @@ Start recording a new hotkey *** +### steps + +```ts +steps: HotkeySequence; +``` + +Defined in: [useHotkeyRecorder.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L18) + +Chords recorded so far in a multi-step recording (for live preview) + +*** + ### stopRecording ```ts stopRecording: () => void; ``` -Defined in: [useHotkeyRecorder.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L15) +Defined in: [useHotkeyRecorder.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeyRecorder.ts#L24) -Stop recording (same as cancel) +Stop recording without calling onCancel #### Returns diff --git a/docs/framework/react/reference/interfaces/ReactHotkeySequenceRecorder.md b/docs/framework/react/reference/interfaces/ReactHotkeySequenceRecorder.md deleted file mode 100644 index b2d0814e..00000000 --- a/docs/framework/react/reference/interfaces/ReactHotkeySequenceRecorder.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -id: ReactHotkeySequenceRecorder -title: ReactHotkeySequenceRecorder ---- - -Defined in: [useHotkeySequenceRecorder.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequenceRecorder.ts#L10) - -## Properties - -### cancelRecording - -```ts -cancelRecording: () => void; -``` - -Defined in: [useHotkeySequenceRecorder.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequenceRecorder.ts#L19) - -#### Returns - -`void` - -*** - -### commitRecording - -```ts -commitRecording: () => void; -``` - -Defined in: [useHotkeySequenceRecorder.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequenceRecorder.ts#L21) - -Commit current steps (no-op if empty) - -#### Returns - -`void` - -*** - -### isRecording - -```ts -isRecording: boolean; -``` - -Defined in: [useHotkeySequenceRecorder.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequenceRecorder.ts#L12) - -Whether recording is currently active - -*** - -### recordedSequence - -```ts -recordedSequence: HotkeySequence | null; -``` - -Defined in: [useHotkeySequenceRecorder.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequenceRecorder.ts#L16) - -Last committed sequence - -*** - -### startRecording - -```ts -startRecording: () => void; -``` - -Defined in: [useHotkeySequenceRecorder.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequenceRecorder.ts#L17) - -#### Returns - -`void` - -*** - -### steps - -```ts -steps: HotkeySequence; -``` - -Defined in: [useHotkeySequenceRecorder.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequenceRecorder.ts#L14) - -Chords captured in the current session - -*** - -### stopRecording - -```ts -stopRecording: () => void; -``` - -Defined in: [useHotkeySequenceRecorder.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequenceRecorder.ts#L18) - -#### Returns - -`void` diff --git a/docs/framework/react/reference/interfaces/UseCustomHotkeysResult.md b/docs/framework/react/reference/interfaces/UseCustomHotkeysResult.md new file mode 100644 index 00000000..59cc3db7 --- /dev/null +++ b/docs/framework/react/reference/interfaces/UseCustomHotkeysResult.md @@ -0,0 +1,111 @@ +--- +id: UseCustomHotkeysResult +title: UseCustomHotkeysResult +--- + +Defined in: useAppHotkeys.ts:70 + +Custom hotkey state and actions returned by `useCustomHotkeys`. + +## Properties + +### customHotkeys + +```ts +customHotkeys: CustomHotkeys; +``` + +Defined in: useAppHotkeys.ts:72 + +The user's hotkeys that differ from the defaults. + +*** + +### findConflicts + +```ts +findConflicts: (id, hotkey) => AppHotkey[]; +``` + +Defined in: useAppHotkeys.ts:86 + +Other app hotkeys that would collide if `id` used `hotkey`. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] + +*** + +### resetCustomHotkey + +```ts +resetCustomHotkey: (id) => void; +``` + +Defined in: useAppHotkeys.ts:82 + +Restores the default hotkey for an app hotkey. + +#### Parameters + +##### id + +`string` + +#### Returns + +`void` + +*** + +### resetCustomHotkeys + +```ts +resetCustomHotkeys: () => void; +``` + +Defined in: useAppHotkeys.ts:84 + +Restores every default hotkey. + +#### Returns + +`void` + +*** + +### setCustomHotkey + +```ts +setCustomHotkey: (id, hotkey) => AppHotkey[]; +``` + +Defined in: useAppHotkeys.ts:77 + +Sets the user's hotkey for an app hotkey. `null` unbinds it. +Returns conflicting app hotkeys; when any exist, nothing changes. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` \| `null` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] diff --git a/docs/framework/react/reference/interfaces/UseHotkeyDefinition.md b/docs/framework/react/reference/interfaces/UseHotkeyDefinition.md deleted file mode 100644 index de5da8fe..00000000 --- a/docs/framework/react/reference/interfaces/UseHotkeyDefinition.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: UseHotkeyDefinition -title: UseHotkeyDefinition ---- - -Defined in: [useHotkeys.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeys.ts#L20) - -A single hotkey definition for use with `useHotkeys`. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [useHotkeys.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeys.ts#L24) - -The function to call when the hotkey is pressed - -*** - -### hotkey - -```ts -hotkey: RegisterableHotkey; -``` - -Defined in: [useHotkeys.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeys.ts#L22) - -The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object - -*** - -### options? - -```ts -optional options?: UseHotkeyOptions; -``` - -Defined in: [useHotkeys.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeys.ts#L26) - -Per-hotkey options (merged on top of commonOptions) diff --git a/docs/framework/react/reference/interfaces/UseHotkeyOptions.md b/docs/framework/react/reference/interfaces/UseHotkeyOptions.md deleted file mode 100644 index 2ee3d99b..00000000 --- a/docs/framework/react/reference/interfaces/UseHotkeyOptions.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -id: UseHotkeyOptions -title: UseHotkeyOptions ---- - -Defined in: [useHotkey.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkey.ts#L16) - -## Extends - -- `Omit`\<`HotkeyOptions`, `"target"`\> - -## Properties - -### target? - -```ts -optional target?: - | Document - | Window - | HTMLElement - | RefObject - | null; -``` - -Defined in: [useHotkey.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkey.ts#L22) - -The DOM element to attach the event listener to. -Can be a React ref, direct DOM element, or null. -Defaults to document. diff --git a/docs/framework/react/reference/interfaces/UseHotkeySequenceDefinition.md b/docs/framework/react/reference/interfaces/UseHotkeySequenceDefinition.md deleted file mode 100644 index 2aebf836..00000000 --- a/docs/framework/react/reference/interfaces/UseHotkeySequenceDefinition.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: UseHotkeySequenceDefinition -title: UseHotkeySequenceDefinition ---- - -Defined in: [useHotkeySequences.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequences.ts#L15) - -A single sequence definition for use with `useHotkeySequences`. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [useHotkeySequences.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequences.ts#L19) - -The function to call when the sequence is completed - -*** - -### options? - -```ts -optional options?: UseHotkeySequenceOptions; -``` - -Defined in: [useHotkeySequences.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequences.ts#L21) - -Per-sequence options (merged on top of commonOptions) - -*** - -### sequence - -```ts -sequence: HotkeySequence; -``` - -Defined in: [useHotkeySequences.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequences.ts#L17) - -Array of hotkey strings that form the sequence diff --git a/docs/framework/react/reference/interfaces/UseHotkeySequenceOptions.md b/docs/framework/react/reference/interfaces/UseHotkeySequenceOptions.md deleted file mode 100644 index 0f8b7cae..00000000 --- a/docs/framework/react/reference/interfaces/UseHotkeySequenceOptions.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -id: UseHotkeySequenceOptions -title: UseHotkeySequenceOptions ---- - -Defined in: [useHotkeySequence.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequence.ts#L13) - -## Extends - -- `Omit`\<`SequenceOptions`, `"target"`\> - -## Properties - -### target? - -```ts -optional target?: - | Document - | Window - | HTMLElement - | RefObject - | null; -``` - -Defined in: [useHotkeySequence.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeySequence.ts#L22) - -The DOM element to attach the event listener to. -Can be a React ref, direct DOM element, or null. -Defaults to document. diff --git a/docs/framework/react/reference/type-aliases/UseAppHotkeyOptions.md b/docs/framework/react/reference/type-aliases/UseAppHotkeyOptions.md new file mode 100644 index 00000000..0834bf08 --- /dev/null +++ b/docs/framework/react/reference/type-aliases/UseAppHotkeyOptions.md @@ -0,0 +1,21 @@ +--- +id: UseAppHotkeyOptions +title: UseAppHotkeyOptions +--- + +```ts +type UseAppHotkeyOptions = AdapterAppHotkeyOptions; +``` + +Defined in: [useHotkey.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkey.ts#L42) + +Options for an app hotkey at the call site. `meta` comes from the declaration. +Kind-specific options follow the declared default: `requireReset` for a chord, +`timeout` for a sequence, both for an app hotkey without a default. If the +user rebinds it to the other kind, options for the old kind no longer apply. + +## Type Parameters + +### T + +`T` *extends* `AppHotkey` = `AppHotkey` diff --git a/docs/framework/react/reference/type-aliases/UseHotkeyChordOptions.md b/docs/framework/react/reference/type-aliases/UseHotkeyChordOptions.md new file mode 100644 index 00000000..79aefc36 --- /dev/null +++ b/docs/framework/react/reference/type-aliases/UseHotkeyChordOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeyChordOptions +title: UseHotkeyChordOptions +--- + +```ts +type UseHotkeyChordOptions = AdapterChordOptions; +``` + +Defined in: [useHotkey.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkey.ts#L24) + +Options for a chord such as `'Mod+S'`. diff --git a/docs/framework/react/reference/type-aliases/UseHotkeyCommonOptions.md b/docs/framework/react/reference/type-aliases/UseHotkeyCommonOptions.md new file mode 100644 index 00000000..e9b00284 --- /dev/null +++ b/docs/framework/react/reference/type-aliases/UseHotkeyCommonOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeyCommonOptions +title: UseHotkeyCommonOptions +--- + +```ts +type UseHotkeyCommonOptions = AdapterCommonOptions; +``` + +Defined in: [useHotkey.ts:30](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkey.ts#L30) + +Options applied to every hotkey, such as provider defaults. Each applies to the kinds that support it. diff --git a/docs/framework/react/reference/type-aliases/UseHotkeyDefinition.md b/docs/framework/react/reference/type-aliases/UseHotkeyDefinition.md new file mode 100644 index 00000000..a88d1d31 --- /dev/null +++ b/docs/framework/react/reference/type-aliases/UseHotkeyDefinition.md @@ -0,0 +1,13 @@ +--- +id: UseHotkeyDefinition +title: UseHotkeyDefinition +--- + +```ts +type UseHotkeyDefinition = AdapterHotkeyDefinition; +``` + +Defined in: [useHotkeys.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkeys.ts#L16) + +One hotkey for `useHotkeys`. Chords accept `requireReset`; sequences accept +`timeout`; hotkeys of unknown kind accept only shared options. diff --git a/docs/framework/react/reference/type-aliases/UseHotkeyOptions.md b/docs/framework/react/reference/type-aliases/UseHotkeyOptions.md new file mode 100644 index 00000000..985522f9 --- /dev/null +++ b/docs/framework/react/reference/type-aliases/UseHotkeyOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeyOptions +title: UseHotkeyOptions +--- + +```ts +type UseHotkeyOptions = AdapterHotkeyOptions; +``` + +Defined in: [useHotkey.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkey.ts#L21) + +Options shared by chords and sequences. diff --git a/docs/framework/react/reference/type-aliases/UseHotkeyOptionsFor.md b/docs/framework/react/reference/type-aliases/UseHotkeyOptionsFor.md new file mode 100644 index 00000000..6b20a04a --- /dev/null +++ b/docs/framework/react/reference/type-aliases/UseHotkeyOptionsFor.md @@ -0,0 +1,18 @@ +--- +id: UseHotkeyOptionsFor +title: UseHotkeyOptionsFor +--- + +```ts +type UseHotkeyOptionsFor = AdapterOptionsFor; +``` + +Defined in: [useHotkey.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkey.ts#L33) + +The options type for a hotkey: chord options for chords, sequence options for sequences. + +## Type Parameters + +### T + +`T` *extends* `RegisterableHotkey` diff --git a/docs/framework/react/reference/type-aliases/UseHotkeySequenceOptions.md b/docs/framework/react/reference/type-aliases/UseHotkeySequenceOptions.md new file mode 100644 index 00000000..8541ea49 --- /dev/null +++ b/docs/framework/react/reference/type-aliases/UseHotkeySequenceOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeySequenceOptions +title: UseHotkeySequenceOptions +--- + +```ts +type UseHotkeySequenceOptions = AdapterSequenceOptions; +``` + +Defined in: [useHotkey.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkey.ts#L27) + +Options for a sequence such as `['G', 'G']`. diff --git a/docs/framework/react/reference/type-aliases/UseHotkeyTarget.md b/docs/framework/react/reference/type-aliases/UseHotkeyTarget.md new file mode 100644 index 00000000..21877fee --- /dev/null +++ b/docs/framework/react/reference/type-aliases/UseHotkeyTarget.md @@ -0,0 +1,17 @@ +--- +id: UseHotkeyTarget +title: UseHotkeyTarget +--- + +```ts +type UseHotkeyTarget = + | React.RefObject + | HTMLElement + | Document + | Window + | null; +``` + +Defined in: [useHotkey.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/react-hotkeys/src/useHotkey.ts#L17) + +A React ref, element, document, or window to listen on. diff --git a/docs/framework/solid/guides/app-hotkeys.md b/docs/framework/solid/guides/app-hotkeys.md new file mode 100644 index 00000000..c5e5276c --- /dev/null +++ b/docs/framework/solid/guides/app-hotkeys.md @@ -0,0 +1,204 @@ +--- +title: App Hotkeys Guide +id: app-hotkeys +--- + +`createHotkey` and `createHotkeys` register a hotkey while its component is mounted. A cheat sheet built from live registrations therefore misses hotkeys on routes that have not rendered yet, and a settings screen has nowhere to change them. App hotkeys solve both: you declare every hotkey in one place, pass the declarations to `HotkeysProvider`, and attach callbacks where the work happens. Users can then customize any of them, and your app decides how to save their choices. + +A declaration holds a hotkey's id, default hotkey, metadata, and options. It never holds a callback; `createHotkey` and `createHotkeys` still attach the callbacks. + +## Declare app hotkeys + +Use `defineAppHotkeys` in a module that contains only declarations. Keep it free of components and handlers, because it loads with your app shell: + +```ts +// app-hotkeys.ts +import { defineAppHotkeys } from '@tanstack/solid-hotkeys' + +export const appHotkeys = defineAppHotkeys({ + 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save', group: 'Document' } }, + 'doc.find': { + hotkey: 'Mod+F', + meta: { name: 'Find', group: 'Document' }, + context: 'editor', + }, + 'nav.home': { + hotkey: ['G', 'H'], + meta: { name: 'Go home', group: 'Navigation' }, + options: { timeout: 800 }, + }, + 'palette.open': { hotkey: null, meta: { name: 'Command palette' } }, +}) + +declare module '@tanstack/solid-hotkeys' { + interface Register { + appHotkeys: typeof appHotkeys + } +} +``` + +- The object key is the app hotkey's id. Treat ids as a stable contract, because saved custom hotkeys use them as keys. +- `hotkey` is the default: a chord, a sequence, or `null` for an action without a default. An unbound app hotkey registers nothing until the user assigns one. +- `options` describe the hotkey itself: `conflictBehavior`, `eventType`, `ignoreInputs`, `preventDefault`, `stopPropagation`, plus `requireReset` for a chord default or `timeout` for a sequence default. Options that don't match the default's kind are type errors. Pass `target` and `enabled` where you register the hotkey. +- `context` labels where a hotkey applies, such as `'editor'`. App hotkeys with different contexts may share a hotkey. It is used only for conflict checks and does not affect matching. + +The `Register` augmentation types the app hotkeys you read back, the ids accepted when changing custom hotkeys, and the `CustomHotkeys` type. + +## Provide app hotkeys + +```tsx +import { HotkeysProvider } from '@tanstack/solid-hotkeys' +import { appHotkeys } from './app-hotkeys' + +function Root() { + return ( + + + + ) +} +``` + +## Register app hotkeys + +Pass an app hotkey wherever you pass a hotkey: + +```tsx +import { createHotkey, createHotkeys, useAppHotkeys } from '@tanstack/solid-hotkeys' + +function Editor() { + const hotkeys = useAppHotkeys() + + createHotkey(() => hotkeys()['doc.save'], () => save(), () => ({ + enabled: isDirty(), + })) + createHotkeys(() => [ + { hotkey: hotkeys()['nav.home'], callback: () => navigate('/') }, + { hotkey: 'Escape', callback: close }, + ]) +} +``` + +`useAppHotkeys()` returns an accessor. You can also import an app hotkey directly, such as `createHotkey(appHotkeys['doc.save'], save)`. Without provided app hotkeys, the declared default is used. + +The registration takes `meta` and `options` from the declaration. Options merge in this order: default options, then declared options, then call-site options. `meta` comes only from the declaration, so the cheat sheet and the registration cannot disagree. Call-site options are typed by the declared default's kind, so `timeout` is accepted for `nav.home` but not for `doc.save`. + +When the user changes a hotkey, registered app hotkeys move to the new hotkey immediately, even if it changes from a chord to a sequence. + +## Build a cheat sheet + +`createAppHotkeyList` returns every app hotkey in declaration order, with its live status: + +| Status | Meaning | +| --- | --- | +| `'active'` | At least one enabled registration | +| `'disabled'` | Registered, but every registration has `enabled: false` | +| `'inactive'` | Has a hotkey, but nothing has registered it | +| `'unbound'` | No hotkey assigned | + +```tsx +import { For } from 'solid-js' +import { createAppHotkeyList, formatForDisplay } from '@tanstack/solid-hotkeys' + +function CheatSheet() { + const items = createAppHotkeyList() + + return ( +
      + + {({ appHotkey, status, isCustom }) => ( +
    • + {appHotkey.meta?.name}:{' '} + {appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned'} + {isCustom && ' (custom)'} +
    • + )} +
      +
    + ) +} +``` + +An app hotkey registered by many components, such as one per list row, appears once. Its `registrations` array holds each live registration. Devtools also list declared app hotkeys that are not registered. + +## Customize and persist hotkeys + +TanStack Hotkeys does not store anything. Pass the user's saved custom hotkeys in and save them when they change: + +```tsx +import { HotkeysProvider } from '@tanstack/solid-hotkeys' +import type { CustomHotkeys } from '@tanstack/solid-hotkeys' + +const saved: CustomHotkeys = JSON.parse(localStorage.getItem('hotkeys') ?? '{}') + +function Root() { + return ( + + localStorage.setItem('hotkeys', JSON.stringify(custom)) + } + > + + + ) +} +``` + +- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them. +- `customHotkeys` is read once; afterwards the shared store owns the state. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning. +- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile. + +Recorded hotkeys use `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others. + +### Change hotkeys + +`useCustomHotkeys` reads and changes the user's custom hotkeys: + +```tsx +import { + createHotkeyRecorder, + formatForDisplay, + useAppHotkeys, + useCustomHotkeys, +} from '@tanstack/solid-hotkeys' +import type { AppHotkeyId } from '@tanstack/solid-hotkeys' + +function ShortcutRow(props: { id: AppHotkeyId }) { + const hotkeys = useAppHotkeys() + const { setCustomHotkey, resetCustomHotkey, findConflicts } = useCustomHotkeys() + const appHotkey = () => hotkeys()[props.id] + + const recorder = createHotkeyRecorder({ + maxSteps: 3, + validate: (hotkey) => { + const conflicts = findConflicts(props.id, hotkey) + return conflicts.length === 0 || `Used by ${conflicts[0]?.meta?.name}` + }, + onRecord: (hotkey) => setCustomHotkey(props.id, hotkey), + onClear: () => setCustomHotkey(props.id, null), + }) + + return ( +
    + {appHotkey().meta?.name}:{' '} + {appHotkey().hotkey ? formatForDisplay(appHotkey().hotkey!) : 'Unassigned'} + + +
    + ) +} +``` + +| Member | Description | +| --- | --- | +| `customHotkeys()` | The hotkeys that differ from the defaults | +| `setCustomHotkey(id, hotkey)` | Sets a hotkey, or unbinds with `null`. Returns conflicting app hotkeys; when there are any, nothing changes. | +| `findConflicts(id, hotkey)` | App hotkeys that would collide if `id` used `hotkey` | +| `resetCustomHotkey(id)` | Restores one default | +| `resetCustomHotkeys()` | Restores every default | + +Two app hotkeys conflict when their contexts match or either has no `context`, and their hotkeys collide: equal chords, or one sequence starts with the other, including a sequence whose first step equals a chord. Conflicts are checked across every declaration, whether or not it is registered. + +Each app hotkey's `defaultHotkey` holds its declared default, and each cheat sheet item's `isCustom` reports whether the effective hotkey differs from it. diff --git a/docs/framework/solid/guides/formatting-display.md b/docs/framework/solid/guides/formatting-display.md index 73b21212..cf3a4a21 100644 --- a/docs/framework/solid/guides/formatting-display.md +++ b/docs/framework/solid/guides/formatting-display.md @@ -3,7 +3,7 @@ title: Formatting & Display Guide id: formatting-display --- -Use `formatForDisplay` whenever a binding appears in a menu, button, hint, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, and parsed bindings. Store the original binding and format it at render time: the display label is not a registration string. +Use `formatForDisplay` whenever a hotkey appears in a menu, button, hint, cheat sheet, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, parsed bindings, and sequences. Store the original binding and format it at render time: the display label is not a registration string. ## Format a binding @@ -32,16 +32,18 @@ const parts = formatForDisplay(binding, { platform: 'mac', parts: true }) With `parts` omitted or false, the result is a string. A runtime boolean returns `string | string[]`. Parts preserve literal plus keys and ignore `separatorToken`. -Sequences are arrays of bindings. Format their steps individually: +## Format sequences -```ts -import type { HotkeySequence } from '@tanstack/solid-hotkeys' +Pass a sequence to format each step and join the steps with `sequenceSeparator`, a space by default: -const sequence: HotkeySequence = ['Mod+[KeyK]', 'C'] -const label = sequence.map((step) => formatForDisplay(step)).join(' → ') +```ts +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac' }) // '⌘ K C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' → ' }) // 'G → G' +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac', parts: true }) +// [['⌘', 'K'], ['C']] — one array of keycaps per step ``` -`formatHotkeySequence` only joins stored strings with spaces. It intentionally retains brackets and code names, so use the code above for user-facing labels. +With `parts: true`, a sequence returns one array per step, so you can render each step as a group of keycaps. ## Choose symbols and separators @@ -63,7 +65,7 @@ formatForDisplay('Control++', { }) // 'Ctrl · +' ``` -An empty separator joins labels directly. `undefined` or `null` uses the platform default. `formatWithLabels(binding, options)` is the shorthand for `formatForDisplay` with `useSymbols: false`. +An empty separator joins labels directly. `undefined` or `null` uses the platform default. `separatorToken` applies within a chord; `sequenceSeparator` applies between steps. ## Supply layout labels @@ -88,6 +90,10 @@ formatForDisplay('Mod+[KeyQ]', { The precedence is `keyLabels`, then a layout entry, then the fallback label. Layout entries receive normal letter casing and key symbols; missing or empty entries fall back. Explicit labels are final. All of these options affect display only. +## Compare hotkeys + +`areHotkeysEqual(a, b)` compares chords or sequences after resolving modifiers, so `Mod+S` equals `Meta+S` on macOS. Sequences compare step by step. `isHotkeySequence` narrows a `Hotkey` to a sequence, and `simplifyHotkey` turns a one-step sequence into its chord. + ## Parse and store bindings `parseHotkey` returns either a logical `key` or a physical `code`, plus resolved modifier flags. Narrow the union before inspecting the identity: @@ -105,6 +111,6 @@ formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S' Parsed modifiers are already resolved. To display a portable `Mod` binding on another platform, serialize with the original platform first, as above. `normalizeRegisterableHotkey` accepts strings or raw objects and preserves the logical/physical distinction. Do not store display labels or put a bracketed code in a logical `key` field. -Use `validateHotkey` when accepting strings from an external source. It returns `valid`, `errors`, and `warnings`; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). +Use `validateHotkey` when accepting a chord or sequence from an external source. It returns `valid`, `errors`, and `warnings`, and names the failing step of a sequence; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). Try these options together in the [vanilla formatter playground](../../../framework/vanilla/examples/formatForDisplay). diff --git a/docs/framework/solid/guides/hotkey-recording.md b/docs/framework/solid/guides/hotkey-recording.md index e43d099e..33a90c8b 100644 --- a/docs/framework/solid/guides/hotkey-recording.md +++ b/docs/framework/solid/guides/hotkey-recording.md @@ -3,15 +3,11 @@ title: Hotkey Recording Guide id: hotkey-recording --- -TanStack Hotkeys provides the `createHotkeyRecorder` primitive for building keyboard shortcut customization UIs. This lets users record their own shortcuts by pressing the desired key combination, similar to how system preferences or IDE shortcut editors work. +`createHotkeyRecorder` is for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the key combination they want, the same way system preferences or IDE shortcut editors work. By default it records one chord; set `maxSteps` to let users record a [sequence](./sequences) too. Recorders default to physical codes: recording a shortcut stores a string such as `Mod+[KeyS]`. Pass it directly to your hotkey registration and use `formatForDisplay` for the label. Set `recordBy: 'key'` when you intentionally want the produced character instead. -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Allow the framework to run its effect or watcher before relying on the update. +TanStack Hotkeys automatically suppresses registered hotkey callbacks while any recorder is active. You do not need to disable hotkeys while recording. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. ## Basic usage @@ -20,123 +16,123 @@ import { createHotkeyRecorder, formatForDisplay } from '@tanstack/solid-hotkeys' function ShortcutRecorder() { const recorder = createHotkeyRecorder({ - onRecord: (hotkey) => { - console.log('Recorded:', hotkey) // e.g., "Mod+Shift+[KeyS]" - }, + onRecord: (hotkey) => console.log('Recorded:', hotkey), // e.g. 'Mod+Shift+[KeyS]' }) return ( -
    - - - - -
    + ) } ``` -> [!NOTE] -> In Solid, `isRecording` and `recordedHotkey` are accessors (signal getters). You must call them with `()` to read the value: `recorder.isRecording()`, `recorder.recordedHotkey()`. - -## Return value +## Recorder state and actions -The `createHotkeyRecorder` primitive returns an object with: +| Member | Description | +| --- | --- | +| `isRecording` | Whether the recorder is currently listening for key presses | +| `steps` | Chords captured so far in a multi-step recording | +| `recordedHotkey` | The last recorded hotkey, or `null` if nothing recorded | +| `startRecording()` | Start listening for key presses | +| `stopRecording()` | Stop listening without calling `onRecord` or `onCancel` | +| `cancelRecording()` | Stop listening and call `onCancel` | +| `commitRecording()` | Commit the current `steps` now (multi-step recordings) | -| Property | Type | Description | -|----------|------|-------------| -| `isRecording` | `() => boolean` | Accessor returning whether the recorder is currently listening | -| `recordedHotkey` | `() => Hotkey \| null` | Accessor returning the last recorded hotkey, or `null` | -| `startRecording` | `() => void` | Start listening for key presses | -| `stopRecording` | `() => void` | Stop listening and reset recorder state without calling `onRecord` | -| `cancelRecording` | `() => void` | Stop listening and discard any recorded hotkey | +Each state member is an accessor: call `isRecording()`, `steps()`, and `recordedHotkey()`. ## Options ### `recordBy` -Recorders default to `recordBy: 'code'`. Option+S producing `ß` on macOS records `Alt+[KeyS]`; Option+2 producing `™` records `Alt+[Digit2]`. The stored brackets preserve physical identity through state, serialization, and registration; `formatForDisplay` produces a readable label. There is no code-to-letter conversion. Existing authored strings such as `Mod+S` remain logical bindings. - -Set `recordBy: 'key'` to intentionally record the produced logical character. A code-mode event with no usable code is rejected rather than silently switching modes. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the produced character without synthetic Control/Alt while retaining Shift. Recording events, repeats, and their releases do not trigger registered hotkeys or sequences. - -```tsx -createHotkeyRecorder({ - recordBy: 'code', // default; choose 'key' for logical characters - onRecord: (hotkey) => { /* called when a hotkey is recorded */ }, - onCancel: () => { /* called when recording is cancelled */ }, - onClear: () => { /* called when the recorded hotkey is cleared */ }, -}) -``` +Recorders default to `recordBy: 'code'`. Option+S producing `ß` on macOS records `Alt+[KeyS]`; Option+2 producing `™` records `Alt+[Digit2]`. The stored brackets preserve physical identity through state, serialization, and registration; `formatForDisplay` produces a readable label. Existing authored strings such as `Mod+S` remain logical hotkeys. -Options can also be passed as an accessor function for reactive configuration. +Set `recordBy: 'key'` to intentionally record the produced logical character. A code-mode event with no usable code is rejected rather than silently switching modes. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the produced character without synthetic Control/Alt while retaining Shift. ### `onRecord` -Called when the user presses a valid key combination. Receives the recorded `Hotkey` string. +Called when a recording commits. With the default `maxSteps: 1`, it receives a chord string such as `'Mod+[KeyS]'` as soon as the user presses a valid combination (a modifier + a non-modifier key, or a single non-modifier key). With a larger `maxSteps`, it receives a chord when one step was recorded, or a sequence otherwise. ### `onCancel` -Called when recording is cancelled (Escape or `cancelRecording()`). +Called when recording is cancelled, by pressing Escape or calling `cancelRecording()`. ### `onClear` -Called when the recorded hotkey is cleared (Backspace or Delete during recording). +Called when the recording is cleared: Backspace or Delete with no modifiers and no recorded steps. Clearing calls only `onClear`; your app decides whether that removes a hotkey or restores a default. -### Global defaults via provider +### `ignoreInputs` -```tsx -import { HotkeysProvider } from '@tanstack/solid-hotkeys' +Defaults to `true`. The recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Escape still cancels recording even while an input is focused. Set `ignoreInputs: false` to record from inside input elements. + +Options can also be an accessor, such as `createHotkeyRecorder(() => ({ maxSteps: 3, recordBy: mode(), onRecord }))`, so recording policy can follow signals. + +### Default options +Set defaults for every recorder with `HotkeysProvider`. Per-recorder options override them. + +```tsx console.log('Recording cancelled'), - }, + hotkeyRecorder: { idleTimeoutMs: 2000, onCancel: () => console.log('Recording cancelled') }, }} > ``` -## Recording behavior - -| Key | Behavior | -|-----|----------| -| Modifier only | Waits for a non-modifier key | -| Modifier + key | Records the full combination | -| Single key | Records the single key | -| Escape | Cancels the recording | -| Backspace / Delete | Clears the currently recorded hotkey | - -### `ignoreInputs` +## Recording sequences -The `HotkeyRecorderOptions` supports an `ignoreInputs` option (defaults to `true`). When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements. Keystrokes pass through to the input as usual. Pressing Escape still cancels recording even when focused on an input. Set `ignoreInputs: false` if you want the recorder to capture keys from within input elements. +Set `maxSteps` above 1 to record up to that many chords. Each chord adds a step, and the recording commits when the user presses Enter, when it reaches `maxSteps`, or after `idleTimeoutMs` without a new step: ```tsx -createHotkeyRecorder({ - ignoreInputs: false, // record even from inside inputs - onRecord: (hotkey) => console.log(hotkey), +const recorder = createHotkeyRecorder({ + maxSteps: 3, + onRecord: (hotkey) => console.log('Recorded:', hotkey), // 'Mod+[KeyS]' or ['[KeyG]', '[KeyG]'] }) + +// Live preview while recording +const label = () => + recorder.isRecording() + ? formatForDisplay(recorder.steps()) || 'Press chords, then Enter…' + : formatForDisplay(recorder.recordedHotkey() ?? []) ``` -### Mod auto-conversion +A one-step recording commits as a chord, so the result can always be passed straight to your hotkey registration. Use `maxSteps: Infinity` for no limit. + +| Option | Description | +| --- | --- | +| `maxSteps` | Maximum chords to record. Reaching it commits. Default `1`. | +| `commitKeys` | `'enter'` (default) commits on plain Enter. With `'none'`, plain Enter can be recorded as a step, and only `commitRecording()`, `idleTimeoutMs`, or `maxSteps` commit. | +| `idleTimeoutMs` | Commit automatically after this many milliseconds without a new step. The timer does not run before the first chord. | + +`commitKeys` and `idleTimeoutMs` are only accepted when `maxSteps` is set. With `maxSteps` above 1, `onRecord` and `validate` receive `Hotkey` (a chord or a sequence) instead of a chord. + +## Recording behavior -Recorded hotkeys automatically use the portable `Mod` format (Command on Mac, Control elsewhere). +| Key | Behavior | +| --- | --- | +| Modifier only (Shift, Ctrl, etc.) | Waits for a non-modifier key; modifier-only presses don't complete a step | +| Modifier + key (e.g., Ctrl+S) | Records the full combination | +| Single key (e.g., Escape, F1) | Records the single key | +| Enter (multi-step, `commitKeys: 'enter'`) | Commits the recorded steps | +| Escape | Cancels the recording | +| Backspace / Delete | Removes the last step; with no steps, clears and calls `onClear` | + +Repeats and IME composition never add a step. Recorded hotkeys use the portable `Mod` format: Command+S on macOS records `Mod+[KeyS]` rather than `Meta+[KeyS]`, so the primary modifier becomes Control if the hotkey is later used on Windows. ## Validation and conflicts -Both recorder APIs accept `detectConflicts`, `validate`, and `onReject`. For a single-hotkey recorder, these options can be supplied alongside `onRecord`: +The recorder accepts `detectConflicts`, `validate`, and `onReject` alongside `onRecord`: ```ts const options = { detectConflicts: { - // Use registration IDs to exclude the binding being edited: + // Use registration IDs to exclude the hotkey being edited: excludeIds: [registrationId], target: document, eventType: 'keydown', @@ -150,73 +146,68 @@ const options = { } ``` -`validate` returns true to accept, or false/a message to reject. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the candidate when available, and conflicting registration views for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. +`validate(hotkey, context)` returns true to accept, or false/a message to reject. The context holds the last chord's `event` and `parsedHotkey`, plus `events` and `parsedSequence` for every step. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the rejected `hotkey` when available, and conflicting registrations for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. A rejected multi-step recording keeps its steps so the user can edit them with Backspace. -`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a binding. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Both single bindings and sequence prefixes are checked. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can affect actual dispatch. +`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a hotkey. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Chords and sequence prefixes are checked: a recorded chord conflicts with a sequence that starts with it, and a recorded sequence conflicts with a registered chord equal to its first step. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute. -For checks outside recording, use `findHotkeyConflicts(bindingOrSequence, options)`. Without source `events`, it compares binding identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. +For checks outside recording, use `findHotkeyConflicts(hotkey, options)`. Without source `events`, it compares hotkey identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. ## Building a shortcut settings UI +For an app-wide settings screen, declare the hotkeys with [app hotkeys](./app-hotkeys) and save the recorder's result with `setCustomHotkey`. That keeps cheat sheets, conflicts, and persistence in one place. + +For a single component, ordinary state is enough: + ```tsx -import { createSignal } from 'solid-js' -import { - createHotkey, - createHotkeyRecorder, - formatForDisplay, -} from '@tanstack/solid-hotkeys' +import { createSignal, For } from 'solid-js' +import { createHotkeys, createHotkeyRecorder, formatForDisplay } from '@tanstack/solid-hotkeys' import type { Hotkey } from '@tanstack/solid-hotkeys' +const actions = { save, undo, search } +type Action = keyof typeof actions + function ShortcutSettings() { - const [shortcuts, setShortcuts] = createSignal>({ + const [shortcuts, setShortcuts] = createSignal>({ save: 'Mod+S', undo: 'Mod+Z', - search: 'Mod+K', + search: ['G', 'S'], }) - - const [editingAction, setEditingAction] = createSignal(null) + const [editing, setEditing] = createSignal(null) const recorder = createHotkeyRecorder({ + maxSteps: 2, onRecord: (hotkey) => { - const action = editingAction() - if (action) { - setShortcuts((prev) => ({ ...prev, [action]: hotkey })) - setEditingAction(null) - } + const action = editing() + if (action) setShortcuts((prev) => ({ ...prev, [action]: hotkey })) + setEditing(null) }, - onCancel: () => setEditingAction(null), + onCancel: () => setEditing(null), }) - // Register the actual hotkeys with their current bindings - createHotkey(() => shortcuts().save, () => save()) - createHotkey(() => shortcuts().undo, () => undo()) - createHotkey(() => shortcuts().search, () => openSearch()) + createHotkeys(() => + Object.entries(shortcuts()).map(([action, hotkey]) => ({ + hotkey, + callback: actions[action as Action], + })), + ) return ( -
    -

    Keyboard Shortcuts

    - - {([action, hotkey]) => ( -
    - {action} - -
    - )} -
    -
    + + {([action, hotkey]) => ( + + )} + ) } ``` ## Under the hood -The `createHotkeyRecorder` primitive creates a `HotkeyRecorder` class instance and subscribes to its reactive state via `@tanstack/solid-store`. The class manages its own keyboard event listeners and state, and the primitive handles cleanup when the component is disposed. +`createHotkeyRecorder` creates a `HotkeyRecorder` from `@tanstack/hotkeys`, exposes its state as signals, and cleans it up with the owning reactive scope. diff --git a/docs/framework/solid/guides/hotkeys.md b/docs/framework/solid/guides/hotkeys.md index 008681c3..f2bbcded 100644 --- a/docs/framework/solid/guides/hotkeys.md +++ b/docs/framework/solid/guides/hotkeys.md @@ -5,6 +5,18 @@ id: hotkeys The `createHotkey` primitive is the primary way to register keyboard shortcuts in SolidJS applications. It wraps the singleton `HotkeyManager` with automatic lifecycle management and reactive option support. +## Chords and sequences + +A hotkey is either a **chord**, keys pressed together such as `'Mod+S'`, or a **sequence**, chords pressed one after another such as `['G', 'G']`. Every API in this guide accepts both, and a one-step array such as `['Mod+S']` is the chord `'Mod+S'`. + +```tsx +createHotkey('Mod+S', () => save()) // chord +createHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 }) // sequence +createHotkey('Escape', () => close(), { requireReset: true }) // chord-only option +``` + +Most options apply to both kinds. `requireReset` applies only to chords and `timeout` only to sequences, and TypeScript rejects options that don't match the hotkey. When the kind isn't known statically, such as a `Hotkey` loaded from settings, only the shared options are accepted. Defaults apply only to the kinds that support them. See the [Sequences Guide](./sequences) for timing and matching, and the [App Hotkeys Guide](./app-hotkeys) to declare hotkeys up front and let users customize them. + ## Logical keys and physical positions Use a logical binding when the shortcut should follow the character on the active layout. Use a physical binding when it should follow a keyboard position: @@ -349,47 +361,36 @@ Group is descriptive metadata, not an execution scope. A shortcuts panel can gro ## Introspecting registrations -Use the `createHotkeyRegistrations` primitive to get a live view of all hotkey and sequence registrations. This is useful for building shortcut palettes, help dialogs, or devtools. +Use the `createHotkeyRegistrations` primitive to get a live view of every registration, chords and sequences together. It's the building block for shortcut palettes and help dialogs. ```tsx +import { For, Show } from 'solid-js' import { createHotkeyRegistrations, formatForDisplay } from '@tanstack/solid-hotkeys' function ShortcutPalette() { const registrations = createHotkeyRegistrations() return ( -
    -

    Keyboard Shortcuts

    -
      - - {(reg) => ( -
    • - {formatForDisplay(reg.hotkey)} - {reg.options.meta?.name && — {reg.options.meta.name}} - {reg.options.meta?.description &&

      {reg.options.meta.description}

      } -
    • - )} -
      -
    - 0}> -

    Sequences

    -
      - - {(reg) => ( -
    • - {reg.sequence.map((step) => formatForDisplay(step)).join(' → ')} - {reg.options.meta?.name && — {reg.options.meta.name}} -
    • - )} -
      -
    -
    -
    +
      + + {(reg) => ( +
    • + {formatForDisplay(reg.hotkey)} + {reg.options.meta?.name && — {reg.options.meta.name}} + + {(seq) => {seq().matchedStepCount}/{seq().hotkey.length} steps} + +
    • + )} +
      +
    ) } ``` -The returned accessor yields an object with a `hotkeys` array and a `sequences` array. Each hotkey entry carries the hotkey string, its options (including `meta`), and its enabled state; sequence entries have the same structure. +The returned accessor yields one array of registration views. Each view has an `id`, the `hotkey`, its `options` (including `meta`), its `target`, and a `triggerCount`. Narrow with `kind`: chord views add `parsedHotkey`; sequence views add `parsedSequence` and `matchedStepCount`, the steps matched so far in the current attempt. Disabled entries stay listed; unmounted registrations disappear. Format any hotkey with `formatForDisplay`, and group entries by `options.meta?.group` when building a help panel. + +Registrations only exist while their components are mounted. To list hotkeys from routes that have not rendered, declare them with [app hotkeys](./app-hotkeys). ## The hotkey manager diff --git a/docs/framework/solid/guides/key-state-tracking.md b/docs/framework/solid/guides/key-state-tracking.md index 0da9e77e..30b53c26 100644 --- a/docs/framework/solid/guides/key-state-tracking.md +++ b/docs/framework/solid/guides/key-state-tracking.md @@ -198,6 +198,6 @@ tracker.areAllKeysHeld(['Shift', 'Control']) // boolean const visible = createHotkeyHint(() => binding()) // accessor: visible() ``` -For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. +For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. A sequence reveals its hint from its first step, and an app hotkey uses its effective hotkey (no hint while unbound). Combine the boolean with the action's enabled state. The helper does not register a shortcut or determine whether its target is focused. The core equivalent is `matchesHeldModifiers(binding, heldKeys, options)`. diff --git a/docs/framework/solid/guides/sequence-recording.md b/docs/framework/solid/guides/sequence-recording.md deleted file mode 100644 index fcfd66f9..00000000 --- a/docs/framework/solid/guides/sequence-recording.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Sequence Recording Guide -id: sequence-recording ---- - -Use `createHotkeySequenceRecorder` to capture a series of shortcut chords. By default, each step records its physical code: pressing G twice produces `['[KeyG]', '[KeyG]']`. Set `recordBy: 'key'` to follow logical characters instead. Pass the resulting array directly to sequence registration and format each step for display. - -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Allow the framework to run its effect or watcher before relying on the update. - -## Record and display a sequence - -This example uses a Save button so plain Enter can be recorded as a step: - -```tsx -import { Show } from 'solid-js' -import { createHotkeySequenceRecorder, formatForDisplay } from '@tanstack/solid-hotkeys' - -function SequenceRecorder() { - const recorder = createHotkeySequenceRecorder({ - commitKeys: 'none', - onRecord: (sequence) => console.log('Register or persist:', sequence), - onReject: ({ message }) => console.log(message), - }) - const label = () => - (recorder.isRecording() ? recorder.steps() : recorder.recordedSequence() ?? []) - .map((step) => formatForDisplay(step)).join(' → ') - - return ( -
    - -

    {label()}

    - - - - -
    - ) -} -``` - -## State and controls - -The recorder exposes `isRecording`, `steps`, and `recordedSequence` as accessors. `steps` contains the current attempt; `recordedSequence` contains the last committed result. `startRecording()` begins a new session. `commitRecording()` saves a nonempty attempt, while `cancelRecording()` discards it and calls `onCancel`. `stopRecording()` resets recorder state without calling `onRecord` or `onCancel`. - -## Options and keyboard behavior - -- `recordBy`: `'code'` by default; `'key'` records produced logical characters. -- `commitKeys`: `'enter'` by default; plain Enter commits a nonempty sequence. `'none'` lets Enter become a step and requires manual or idle commit. -- `commitOnEnter: false`: also permits Enter as a step when `commitKeys` is `'enter'`. -- `idleTimeoutMs`: optionally commits after inactivity following a completed step. No timer runs before the first step. -- `ignoreInputs`: true by default, including shadow-root inputs. Set false to record from editable fields. Escape still cancels from an input. - -Escape cancels. Unmodified Backspace/Delete removes the last step; when already empty it stops and calls only `onClear`. Modifier-only presses, automatic repeats, and IME composition do not append steps. Recorded events and their releases do not trigger application shortcuts. - -Set provider defaults through `HotkeysProvider` with `defaultOptions.hotkeySequenceRecorder`. - -## Validation and conflicts - -`validate(sequence, { events, parsedSequence })` runs at commit. Return true to accept, or false/a message to reject. `detectConflicts` checks live bindings and sequence prefixes, including physical/logical overlap established by the recorded events. `onReject` receives feedback; rejected steps remain editable with Backspace. - -The shared options and exclusions are described in the [hotkey recording guide](./hotkey-recording.md#validation-and-conflicts). The application owns reset, persistence, and any binding being edited; clearing never calls `onRecord([])`. diff --git a/docs/framework/solid/guides/sequences.md b/docs/framework/solid/guides/sequences.md index fa9ce118..5237d59e 100644 --- a/docs/framework/solid/guides/sequences.md +++ b/docs/framework/solid/guides/sequences.md @@ -3,216 +3,137 @@ title: Sequences Guide id: sequences --- -TanStack Hotkeys supports multi-key sequences: shortcuts where you press keys one after another rather than simultaneously. Common uses are Vim-style navigation, cheat codes, and multi-step commands. +A sequence is a hotkey made of chords pressed one after another rather than together. Think Vim-style navigation, cheat codes, or multi-step commands. Sequences are ordinary hotkeys: register them with `createHotkey` and `createHotkeys`, list them with `createHotkeyRegistrations`, record them with `createHotkeyRecorder`, and format them with `formatForDisplay`. -Sequence steps use the same string syntax as single hotkeys. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. Display steps with `sequence.map((step) => formatForDisplay(step)).join(' → ')`. +Sequence steps use the same syntax as chords. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. ## Basic usage -Use the `createHotkeySequence` primitive to register a key sequence: +Pass an array of chords wherever you pass a hotkey: ```tsx -import { createHotkeySequence } from '@tanstack/solid-hotkeys' +import { createHotkey } from '@tanstack/solid-hotkeys' function App() { // Vim-style: press g then g to scroll to top - createHotkeySequence(['G', 'G'], () => { + createHotkey(['G', 'G'], () => { window.scrollTo({ top: 0, behavior: 'smooth' }) }) } ``` -The first argument is an array of `Hotkey` strings representing each step in the sequence. The user must press them in order within the timeout window. +The user must press the steps in order, each within the timeout window. A one-step array such as `['Escape']` is the chord `'Escape'`. ## Many sequences at once -For several sequences or a dynamic list, use `createHotkeySequences` instead of many `createHotkeySequence` calls. Pass a plain array or an accessor that returns definitions. +`createHotkeys` registers chords and sequences together, including dynamic lists: ```tsx -import { createHotkeySequences } from '@tanstack/solid-hotkeys' +import { createHotkeys } from '@tanstack/solid-hotkeys' -createHotkeySequences([ - { sequence: ['G', 'G'], callback: () => scrollToTop() }, - { sequence: ['D', 'D'], callback: () => deleteLine(), options: { timeout: 500 } }, +createHotkeys([ + { hotkey: ['G', 'G'], callback: () => scrollToTop() }, + { hotkey: ['D', 'D'], callback: () => deleteLine(), options: { timeout: 500 } }, + { hotkey: 'Mod+S', callback: () => save() }, ]) ``` -Options merge like `createHotkeys`: `HotkeysProvider` defaults, then `commonOptions`, then each definition's `options`. For element-scoped multi-sequence registration, use `createHotkeySequencesAttachment`. - -## Reactive options - -Sequence options support the same [property getter patterns](./hotkeys.md#property-getters) as hotkey options, including `enabled`, `timeout`, and `target`. Updates preserve the registration when its sequence, target, and platform stay the same. - -Solid's `createHotkeySequence` accepts accessor functions for reactive sequence and options: - -```tsx -const [isVimMode, setIsVimMode] = createSignal(true) -const [sequence] = createSignal(['G', 'G'] as const) - -createHotkeySequence( - sequence, - () => scrollToTop(), - () => ({ enabled: isVimMode(), timeout: 1500 }), -) -``` - ## Sequence options -The third argument is an options object (or accessor returning options): - -```tsx -createHotkeySequence(['G', 'G'], callback, { - timeout: 1000, // Time allowed between keys (ms) - enabled: true, // Whether the sequence is active - target: document, // Or from an accessor for scoped sequences -}) -``` +Sequences accept the same options as chords, except `requireReset`, plus `timeout`. ### `timeout` -The maximum time (in milliseconds) allowed between consecutive key presses. Defaults to `1000` (1 second). +The maximum time (in milliseconds) allowed between consecutive steps. If the user takes longer than this between any two steps, the sequence resets. Defaults to `1000` (1 second). ```tsx -createHotkeySequence(['D', 'D'], () => deleteLine(), { timeout: 500 }) -createHotkeySequence(['Shift+Z', 'Shift+Z'], () => forceQuit(), { timeout: 2000 }) -``` +// Fast sequence - user must type quickly +createHotkey(['D', 'D'], () => deleteLine(), { timeout: 500 }) -### `enabled` - -Controls whether the sequence is active. Defaults to `true`. Use an accessor for reactive control. - -Disabled sequences remain registered and stay visible in devtools; only execution is suppressed. - -```tsx -const [isVimMode, setIsVimMode] = createSignal(true) - -createHotkeySequence(['G', 'G'], () => scrollToTop(), () => ({ - enabled: isVimMode(), -})) +// Slow sequence - user has more time between keys +createHotkey(['Shift+Z', 'Shift+Z'], () => forceQuit(), { timeout: 2000 }) ``` -### `target` - -The DOM element to attach the sequence listener to. Defaults to `document`. Can be from an accessor when the target becomes available after mount. - -### Global defaults via provider +`timeout` is only accepted when the hotkey is a sequence. Set a default for every sequence with `HotkeysProvider`: ```tsx import { HotkeysProvider } from '@tanstack/solid-hotkeys' - + ``` -### `meta` +### `enabled` and `meta` -Sequences support the same `meta` option as hotkeys. Attach a `name` and `description` for use in shortcut palettes and devtools. +These work exactly as they do for chords. Disabled sequences stay registered and visible in devtools, and `meta` flows to registrations, devtools, and cheat sheets. See the [Hotkeys Guide](./hotkeys). ```tsx -createHotkeySequence(['G', 'G'], () => scrollToTop(), { - meta: { name: 'Go to Top', description: 'Scroll to the top of the page' }, -}) +createHotkey(['G', 'G'], () => scrollToTop(), () => ({ + enabled: isVimMode(), + meta: { name: 'Go to top', description: 'Scroll to the top of the page' }, +})) ``` -See the [Hotkeys Guide](./hotkeys.md#metadata-name--description) for details on declaration merging and introspecting registrations. - ## Sequences with modifiers -Each step in a sequence can include modifiers: +Each step in a sequence can include modifiers, and consecutive steps can repeat the same modifier: ```tsx -createHotkeySequence(['Mod+K', 'Mod+C'], () => commentSelection()) -createHotkeySequence(['G', 'Shift+G'], () => scrollToBottom()) -``` - -## Chained modifier chords - -This example follows physical R and T positions. Brackets retain those positions even when the keys produce different letters. Other sequences can continue using logical characters. - -You can repeat the same modifier across consecutive steps, for example `Shift+R` then `Shift+T`: +// Ctrl+K followed by Ctrl+C (VS Code-style comment) +createHotkey(['Mod+K', 'Mod+C'], () => commentSelection()) -```tsx -createHotkeySequence(['Shift+[KeyR]', 'Shift+[KeyT]'], () => { - doNextAction() -}) +// Physical R then T positions, both with Shift +createHotkey(['Shift+[KeyR]', 'Shift+[KeyT]'], () => doNextAction()) ``` -### Modifier-only keys between steps - -While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone, with no letter or other key) are ignored. They neither advance the sequence nor reset progress, so a user can tap or hold Shift between chords without breaking the sequence. +Brackets retain physical positions even when the keys produce different letters. While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored. They neither advance the sequence nor reset progress, so a user can tap or hold Shift between `Shift+R` and `Shift+T`. -## Common sequence patterns - -### Vim-style navigation +## Vim-style navigation ```tsx -function VimNavigation() { - createHotkeySequence(['G', 'G'], () => scrollToTop()) - createHotkeySequence(['G', 'Shift+G'], () => scrollToBottom()) - createHotkeySequence(['D', 'D'], () => deleteLine()) - createHotkeySequence(['D', 'W'], () => deleteWord()) - createHotkeySequence(['C', 'I', 'W'], () => changeInnerWord()) -} +createHotkeys([ + { hotkey: ['G', 'G'], callback: () => scrollToTop() }, + { hotkey: ['G', 'Shift+G'], callback: () => scrollToBottom() }, + { hotkey: ['D', 'D'], callback: () => deleteLine() }, + { hotkey: ['D', 'W'], callback: () => deleteWord() }, + { hotkey: ['C', 'I', 'W'], callback: () => changeInnerWord() }, +]) ``` -### Konami Code - -```tsx -createHotkeySequence( - [ - 'ArrowUp', 'ArrowUp', - 'ArrowDown', 'ArrowDown', - 'ArrowLeft', 'ArrowRight', - 'ArrowLeft', 'ArrowRight', - 'B', 'A', - ], - () => enableEasterEgg(), - { timeout: 2000 }, -) -``` +## Displaying sequences -### Multi-step commands +`formatForDisplay` formats each step and joins them with `sequenceSeparator`, a space by default: -```tsx -createHotkeySequence(['H', 'E', 'L', 'P'], () => openHelp()) +```ts +formatForDisplay(['Mod+K', 'Mod+C'], { platform: 'mac' }) // '⌘ K ⌘ C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' then ' }) // 'G then G' ``` -## How sequences work +Registration views report each sequence's progress in `matchedStepCount`, so a help panel can show how far the user has typed. See the [Hotkeys Guide](./hotkeys). -Both `SequenceManager` and `createSequenceMatcher` ignore modifier-only events, IME composition, and automatic keydown repeats. These events neither advance the sequence nor refresh its timeout: holding G does not complete a two-press G sequence. The manager prefers exact matches over weaker logical-key fallbacks while preserving equally strong matches. +## How sequences work -The `SequenceManager` (singleton) handles all sequence registrations. When a key is pressed: +The `HotkeyManager` tracks progress for every sequence registration. When a key is pressed: -1. It checks if the key matches the next expected step in any registered sequence -2. If it matches, the sequence advances to the next step +1. It checks whether the key matches the next expected step of each sequence +2. If it matches, that sequence advances 3. If the timeout expires between steps, the sequence resets 4. When all steps are completed, the callback fires -5. Modifier-only keydowns are ignored (they neither advance nor reset the sequence) -### Overlapping sequences +Modifier-only events, IME composition, and automatic keydown repeats neither advance a sequence nor refresh its timeout: holding G does not complete a two-press G sequence. Exact matches take priority over weaker logical-key fallbacks. -Multiple sequences can share the same prefix. The manager tracks progress for each sequence independently: - -```tsx -createHotkeySequence(['D', 'D'], () => deleteLine()) -createHotkeySequence(['D', 'W'], () => deleteWord()) -createHotkeySequence(['D', 'I', 'W'], () => deleteInnerWord()) -``` +Multiple sequences can share a prefix, such as `['D', 'D']`, `['D', 'W']`, and `['D', 'I', 'W']`. Progress is tracked for each sequence independently, so after pressing `D` the manager waits for the next key to determine which sequence completes. ### Conflicting registrations -For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate bindings. `conflictBehavior` applies to equivalent sequences without rewriting their stored strings. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection also checks prefixes and observed physical/logical overlap. +For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate hotkeys. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection and app hotkey conflicts are stricter: there, a sequence also conflicts with a chord equal to its first step and with any sequence it starts. -## The sequence manager +## Standalone matching -Under the hood, `createHotkeySequence` uses the singleton `SequenceManager`. You can also use the core `createSequenceMatcher` function for standalone sequence matching: +For sequence matching without the singleton manager, use `createSequenceMatcher`: -```tsx +```ts import { createSequenceMatcher } from '@tanstack/solid-hotkeys' const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 }) @@ -221,6 +142,6 @@ document.addEventListener('keydown', (e) => { if (matcher.match(e)) { console.log('Sequence completed!') } - console.log('Progress:', matcher.getProgress()) + console.log('Progress:', matcher.getProgress()) // e.g., 1 }) ``` diff --git a/docs/framework/solid/quick-start.md b/docs/framework/solid/quick-start.md new file mode 100644 index 00000000..6849c37b --- /dev/null +++ b/docs/framework/solid/quick-start.md @@ -0,0 +1,163 @@ +--- +title: Quick Start +id: quick-start +--- + +## Installation + +Don't have TanStack Hotkeys installed yet? See the [Installation](../../installation) page for instructions. + +## Your first hotkey + +The `createHotkey` primitive is the primary way to register keyboard shortcuts in Solid: + +```tsx +import { createHotkey } from '@tanstack/solid-hotkeys' + +function App() { + createHotkey('Mod+S', () => { + saveDocument() + }) + + return
    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. The registration is removed when the owning component is disposed. + +## Common patterns + +### Multiple hotkeys + +Call `createHotkey` once per hotkey, or pass a list to `createHotkeys`: + +```tsx +import { createHotkeys } from '@tanstack/solid-hotkeys' + +function Editor() { + createHotkeys([ + { hotkey: 'Mod+S', callback: () => save() }, + { hotkey: 'Mod+Z', callback: () => undo() }, + { hotkey: 'Mod+Shift+Z', callback: () => redo() }, + { hotkey: 'Escape', callback: () => closeDialog() }, + ]) + + return
    Editor with keyboard shortcuts
    +} +``` + +### Scoped and conditional hotkeys + +Pass an accessor for options that change. A `null` target waits until the element is attached: + +```tsx +import { createSignal } from 'solid-js' +import { createHotkey } from '@tanstack/solid-hotkeys' + +function Panel(props: { open: boolean; onClose: () => void }) { + const [panel, setPanel] = createSignal(null) + + createHotkey('Escape', () => props.onClose(), () => ({ + target: panel(), + enabled: props.open, + })) + + return ( +
    + Press Escape while focused here to close +
    + ) +} +``` + +### Multi-key sequences + +Pass an array of chords to register a Vim-style sequence: + +```tsx +createHotkey(['G', 'G'], () => scrollToTop()) +createHotkey(['G', 'Shift+G'], () => scrollToBottom()) +``` + +### Tracking held keys + +```tsx +import { createHeldKeys, createKeyHold } from '@tanstack/solid-hotkeys' + +function StatusBar() { + const isShiftHeld = createKeyHold('Shift') + const heldKeys = createHeldKeys() + + return ( +
    + {isShiftHeld() && Shift mode active} + {heldKeys().length > 0 && Keys: {heldKeys().join('+')}} +
    + ) +} +``` + +### Displaying hotkeys in the UI + +```tsx +import { formatForDisplay } from '@tanstack/solid-hotkeys' + +function SaveButton() { + return ( + + ) +} +``` + +## Declaring app hotkeys + +To list every hotkey in a cheat sheet, including hotkeys whose components haven't mounted, and to let users customize them, declare them once with `defineAppHotkeys` and pass them to `HotkeysProvider`. See the [App Hotkeys Guide](./guides/app-hotkeys). + +## Default options provider + +`HotkeysProvider` also sets default options for every hotkey primitive. Options passed directly to a primitive override the provider defaults. + +```tsx +import { HotkeysProvider } from '@tanstack/solid-hotkeys' + +function Root() { + return ( + console.log('Recording cancelled') }, + }} + > + + + ) +} +``` + +## Setting up devtools + +```tsx +import { TanStackDevtools } from '@tanstack/solid-devtools' +import { hotkeysDevtoolsPlugin } from '@tanstack/solid-hotkeys-devtools' + +function App() { + return ( +
    + {/* Your app */} + +
    + ) +} +``` + +## Next steps + +- [Hotkeys Guide](./guides/hotkeys) - Deep dive into `createHotkey` options and patterns +- [Sequences Guide](./guides/sequences) - Multi-key sequence handling +- [App Hotkeys Guide](./guides/app-hotkeys) - Declaring, listing, and customizing every hotkey +- [Hotkey Recording Guide](./guides/hotkey-recording) - Recording chords and sequences in settings UIs +- [Key State Tracking Guide](./guides/key-state-tracking) - Real-time key state monitoring +- [Formatting & Display Guide](./guides/formatting-display) - Platform-aware hotkey formatting diff --git a/docs/framework/solid/reference/functions/createAppHotkeyList.md b/docs/framework/solid/reference/functions/createAppHotkeyList.md new file mode 100644 index 00000000..b3bfce71 --- /dev/null +++ b/docs/framework/solid/reference/functions/createAppHotkeyList.md @@ -0,0 +1,18 @@ +--- +id: createAppHotkeyList +title: createAppHotkeyList +--- + +```ts +function createAppHotkeyList(): () => AppHotkeyListItem>[]; +``` + +Defined in: appHotkeys.ts:49 + +Lists every app hotkey with its live status, in declaration order, for +cheat sheets and settings screens. App hotkeys whose components have not +mounted are `'inactive'`; those without a hotkey are `'unbound'`. + +## Returns + +() => `AppHotkeyListItem`\<`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>[] diff --git a/docs/framework/solid/reference/functions/createHotkey.md b/docs/framework/solid/reference/functions/createHotkey.md index 94a5e5e8..4dc8be87 100644 --- a/docs/framework/solid/reference/functions/createHotkey.md +++ b/docs/framework/solid/reference/functions/createHotkey.md @@ -3,85 +3,106 @@ id: createHotkey title: createHotkey --- +## Call Signature + ```ts -function createHotkey( +function createHotkey( hotkey, callback, options?): void; ``` -Defined in: [createHotkey.ts:82](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkey.ts#L82) - -SolidJS primitive for registering a keyboard hotkey. +Defined in: [createHotkey.ts:64](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkey.ts#L64) -Uses the singleton HotkeyManager for efficient event handling. -The callback receives both the keyboard event and a context object -containing the hotkey string and parsed hotkey. +Solid primitive for registering a hotkey: a chord such as `'Mod+S'`, a +sequence such as `['G', 'G']`, or an app hotkey from `defineAppHotkeys`. +The hotkey and options may be accessors. -This primitive automatically tracks reactive dependencies and updates -the registration when options or the callback change. +Chords accept `requireReset`; sequences accept `timeout`. A one-step +sequence registers as its chord. An app hotkey uses its effective hotkey +from `HotkeysProvider` (including the user's custom hotkey) and its +declared meta and options, and registers nothing while unbound. -## Parameters +### Type Parameters -### hotkey +#### T -`RegisterableHotkey` \| (() => `RegisterableHotkey`) +`T` *extends* `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\> -The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object (supports `mod` for cross-platform) +### Parameters -### callback +#### hotkey -`HotkeyCallback` +`MaybeAccessor`\<`T`\> -The function to call when the hotkey is pressed +#### callback -### options? +`HotkeyCallback` - \| [`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md) - \| (() => [`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md)) +#### options? -Options for the hotkey behavior +`MaybeAccessor`\<[`CreateAppHotkeyOptions`](../type-aliases/CreateAppHotkeyOptions.md)\<`T`\>\> -## Returns +### Returns `void` -## Examples +### Example ```tsx -function SaveButton() { - const [count, setCount] = createSignal(0) +createHotkey('Mod+S', () => save()) +createHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 }) +createHotkey('Escape', () => close(), () => ({ enabled: isOpen() })) +``` - // Callback always has access to latest count value - createHotkey('Mod+S', (event, { hotkey }) => { - console.log(`Save triggered, count is ${count()}`) - handleSave() - }) +## Call Signature - return -} +```ts +function createHotkey( + hotkey, + callback, + options?): void; ``` -```tsx -function Modal(props) { - // enabled option is reactive - createHotkey('Escape', () => { - props.onClose() - }, () => ({ enabled: props.isOpen })) - - return - - -} -``` +Defined in: [createHotkey.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkey.ts#L69) -```tsx -function Editor() { - const [editorRef, setEditorRef] = createSignal(null) +Solid primitive for registering a hotkey: a chord such as `'Mod+S'`, a +sequence such as `['G', 'G']`, or an app hotkey from `defineAppHotkeys`. +The hotkey and options may be accessors. + +Chords accept `requireReset`; sequences accept `timeout`. A one-step +sequence registers as its chord. An app hotkey uses its effective hotkey +from `HotkeysProvider` (including the user's custom hotkey) and its +declared meta and options, and registers nothing while unbound. + +### Type Parameters - // Scoped to a specific element - use accessor so hotkey waits for ref - createHotkey('Mod+S', save, () => ({ target: editorRef() })) +#### T - return
    ...
    -} +`T` *extends* `RegisterableHotkey` + +### Parameters + +#### hotkey + +`MaybeAccessor`\<`T`\> + +#### callback + +`HotkeyCallback` + +#### options? + +`MaybeAccessor`\<`AdapterOptionsFor`\<`T`, [`CreateHotkeyTarget`](../type-aliases/CreateHotkeyTarget.md)\>\> + +### Returns + +`void` + +### Example + +```tsx +createHotkey('Mod+S', () => save()) +createHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 }) +createHotkey('Escape', () => close(), () => ({ enabled: isOpen() })) ``` diff --git a/docs/framework/solid/reference/functions/createHotkeyHint.md b/docs/framework/solid/reference/functions/createHotkeyHint.md index 79e10f9c..444c8d59 100644 --- a/docs/framework/solid/reference/functions/createHotkeyHint.md +++ b/docs/framework/solid/reference/functions/createHotkeyHint.md @@ -7,19 +7,20 @@ title: createHotkeyHint function createHotkeyHint(hotkey, options?): () => boolean; ``` -Defined in: [createHotkeyHint.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyHint.ts#L13) +Defined in: [createHotkeyHint.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyHint.ts#L22) -Returns a boolean accessor indicating whether held modifiers reveal this shortcut. -Pass accessors to track changing bindings and options. -Uses the nonempty-subset/AltGr rules of `matchesHeldModifiers`; `exact` requires -every modifier. Combine with the action's enabled state before showing a badge. -This helper does not register a shortcut or check whether its target is focused. +Returns an accessor indicating whether held modifiers reveal this shortcut. +A sequence reveals its hint from its first step, and an app hotkey uses its +effective hotkey (false while unbound). This helper does not register a +shortcut or check whether its target is focused. ## Parameters ### hotkey -`RegisterableHotkey` \| (() => `RegisterableHotkey`) + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\> + \| (() => RegisterableHotkey \| AppHotkey\) ### options? diff --git a/docs/framework/solid/reference/functions/createHotkeyRecorder.md b/docs/framework/solid/reference/functions/createHotkeyRecorder.md index 201e3e84..37b0eb1b 100644 --- a/docs/framework/solid/reference/functions/createHotkeyRecorder.md +++ b/docs/framework/solid/reference/functions/createHotkeyRecorder.md @@ -3,60 +3,162 @@ id: createHotkeyRecorder title: createHotkeyRecorder --- +## Call Signature + +```ts +function createHotkeyRecorder(options): SolidHotkeyRecorder; +``` + +Defined in: [createHotkeyRecorder.ts:45](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L45) + +Solid primitive for recording keyboard shortcuts. Options may be an accessor. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### options + +`HotkeyChordRecorderOptions` + +### Returns + +[`SolidHotkeyRecorder`](../interfaces/SolidHotkeyRecorder.md)\<`HotkeyChord`\> + +### Example + +```tsx +const recorder = createHotkeyRecorder({ onRecord: setShortcut }) +const sequenceRecorder = createHotkeyRecorder({ maxSteps: 3, onRecord: setShortcut }) +``` + +## Call Signature + ```ts function createHotkeyRecorder(options): SolidHotkeyRecorder; ``` -Defined in: [createHotkeyRecorder.ts:61](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L61) +Defined in: [createHotkeyRecorder.ts:48](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L48) -SolidJS primitive for recording keyboard shortcuts. +Solid primitive for recording keyboard shortcuts. Options may be an accessor. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### options + +`HotkeySequenceRecorderOptions` + +### Returns + +[`SolidHotkeyRecorder`](../interfaces/SolidHotkeyRecorder.md) + +### Example + +```tsx +const recorder = createHotkeyRecorder({ onRecord: setShortcut }) +const sequenceRecorder = createHotkeyRecorder({ maxSteps: 3, onRecord: setShortcut }) +``` + +## Call Signature + +```ts +function createHotkeyRecorder(options): SolidHotkeyRecorder; +``` -This primitive 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. +Defined in: [createHotkeyRecorder.ts:53](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L53) -This primitive uses `useSelector` from `@tanstack/solid-store` to subscribe -to the recorder's store state (same pattern as useHotkeyRecorder in React). +Solid primitive for recording keyboard shortcuts. Options may be an accessor. -## Parameters +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. -### options +### Parameters -`HotkeyRecorderOptions` \| (() => `HotkeyRecorderOptions`) +#### options -Configuration options for the recorder (or accessor function) +() => `HotkeySequenceRecorderOptions` -## Returns +### Returns [`SolidHotkeyRecorder`](../interfaces/SolidHotkeyRecorder.md) -An object with recording state signals and control functions +### Example + +```tsx +const recorder = createHotkeyRecorder({ onRecord: setShortcut }) +const sequenceRecorder = createHotkeyRecorder({ maxSteps: 3, onRecord: setShortcut }) +``` + +## Call Signature + +```ts +function createHotkeyRecorder(options): SolidHotkeyRecorder; +``` + +Defined in: [createHotkeyRecorder.ts:56](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L56) + +Solid primitive for recording keyboard shortcuts. Options may be an accessor. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### options + +() => `HotkeyChordRecorderOptions` + +### Returns + +[`SolidHotkeyRecorder`](../interfaces/SolidHotkeyRecorder.md)\<`HotkeyChord`\> + +### Example + +```tsx +const recorder = createHotkeyRecorder({ onRecord: setShortcut }) +const sequenceRecorder = createHotkeyRecorder({ maxSteps: 3, onRecord: setShortcut }) +``` + +## Call Signature + +```ts +function createHotkeyRecorder(options): SolidHotkeyRecorder; +``` + +Defined in: [createHotkeyRecorder.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L59) + +Solid primitive for recording keyboard shortcuts. Options may be an accessor. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### options + +() => `HotkeyRecorderOptions` + +### Returns + +[`SolidHotkeyRecorder`](../interfaces/SolidHotkeyRecorder.md) -## Example +### Example ```tsx -function ShortcutSettings() { - const [shortcut, setShortcut] = createSignal('Mod+S') - - const recorder = createHotkeyRecorder({ - onRecord: (hotkey) => { - setShortcut(hotkey) - }, - onCancel: () => { - console.log('Recording cancelled') - }, - }) - - return ( -
    - - -
    Recording: {recorder.recordedHotkey()}
    -
    -
    - ) -} +const recorder = createHotkeyRecorder({ onRecord: setShortcut }) +const sequenceRecorder = createHotkeyRecorder({ maxSteps: 3, onRecord: setShortcut }) ``` diff --git a/docs/framework/solid/reference/functions/createHotkeyRegistrations.md b/docs/framework/solid/reference/functions/createHotkeyRegistrations.md index 3e6a9b7f..eecdb55b 100644 --- a/docs/framework/solid/reference/functions/createHotkeyRegistrations.md +++ b/docs/framework/solid/reference/functions/createHotkeyRegistrations.md @@ -4,20 +4,27 @@ title: createHotkeyRegistrations --- ```ts -function createHotkeyRegistrations(): HotkeyRegistrationsResult; +function createHotkeyRegistrations(): () => HotkeyRegistrationView[]; ``` -Defined in: [createHotkeyRegistrations.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRegistrations.ts#L32) +Defined in: [createHotkeyRegistrations.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRegistrations.ts#L21) -Solid primitive that reactively reads all hotkey and sequence registrations -from the singleton managers. +Solid primitive that reactively reads every hotkey registration, chords and +sequences, from the singleton HotkeyManager. Narrow each view with `kind`. -This is a standalone primitive that does NOT require the HotkeysProvider. -It subscribes to both HotkeyManager and SequenceManager stores and -triggers reactivity when registrations change. +This primitive does NOT require the HotkeysProvider. ## Returns -[`HotkeyRegistrationsResult`](../interfaces/HotkeyRegistrationsResult.md) +An accessor for all registrations, without callbacks -Object with `hotkeys` and `sequences` signal accessors +() => `HotkeyRegistrationView`[] + +## Example + +```tsx +const registrations = createHotkeyRegistrations() + + {(reg) =>
  • {reg.options.meta?.name}: {formatForDisplay(reg.hotkey)}
  • } +
    +``` diff --git a/docs/framework/solid/reference/functions/createHotkeySequence.md b/docs/framework/solid/reference/functions/createHotkeySequence.md deleted file mode 100644 index c209c8a0..00000000 --- a/docs/framework/solid/reference/functions/createHotkeySequence.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -id: createHotkeySequence -title: createHotkeySequence ---- - -```ts -function createHotkeySequence( - sequence, - callback, - options?): void; -``` - -Defined in: [createHotkeySequence.ts:64](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequence.ts#L64) - -SolidJS primitive for registering a keyboard shortcut sequence (Vim-style). - -This primitive allows you to register multi-key sequences like 'g g' or 'd d' -that trigger when the full sequence is pressed within a timeout. - -Each step may include modifiers. You can chain the same modifier across -steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift, -Control, Alt, or Meta pressed alone) are ignored while matching—they do not -advance the sequence or reset progress. - -## Parameters - -### sequence - -`HotkeySequence` \| (() => `HotkeySequence`) - -Array of hotkey strings that form the sequence (or accessor function) - -### callback - -`HotkeyCallback` - -Function to call when the sequence is completed - -### options? - - \| [`CreateHotkeySequenceOptions`](../interfaces/CreateHotkeySequenceOptions.md) - \| (() => [`CreateHotkeySequenceOptions`](../interfaces/CreateHotkeySequenceOptions.md)) - -Options for the sequence behavior (or accessor function) - -## Returns - -`void` - -## Example - -```tsx -function VimEditor() { - // 'g g' to go to top - createHotkeySequence(['G', 'G'], () => { - scrollToTop() - }) - - // 'd d' to delete line - createHotkeySequence(['D', 'D'], () => { - deleteLine() - }) - - // 'd i w' to delete inner word - createHotkeySequence(['D', 'I', 'W'], () => { - deleteInnerWord() - }, { timeout: 500 }) - - // Same modifier on consecutive steps (bare Shift between chords is ignored) - createHotkeySequence(['Shift+R', 'Shift+T'], () => { - nextAction() - }) - - return
    ...
    -} -``` diff --git a/docs/framework/solid/reference/functions/createHotkeySequenceRecorder.md b/docs/framework/solid/reference/functions/createHotkeySequenceRecorder.md deleted file mode 100644 index d5d53630..00000000 --- a/docs/framework/solid/reference/functions/createHotkeySequenceRecorder.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: createHotkeySequenceRecorder -title: createHotkeySequenceRecorder ---- - -```ts -function createHotkeySequenceRecorder(options): SolidHotkeySequenceRecorder; -``` - -Defined in: [createHotkeySequenceRecorder.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequenceRecorder.ts#L23) - -SolidJS primitive for recording multi-chord sequences (Vim-style shortcuts). - -## Parameters - -### options - -`HotkeySequenceRecorderOptions` \| (() => `HotkeySequenceRecorderOptions`) - -## Returns - -[`SolidHotkeySequenceRecorder`](../interfaces/SolidHotkeySequenceRecorder.md) diff --git a/docs/framework/solid/reference/functions/createHotkeySequences.md b/docs/framework/solid/reference/functions/createHotkeySequences.md deleted file mode 100644 index e6f05294..00000000 --- a/docs/framework/solid/reference/functions/createHotkeySequences.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -id: createHotkeySequences -title: createHotkeySequences ---- - -```ts -function createHotkeySequences(sequences, commonOptions?): void; -``` - -Defined in: [createHotkeySequences.ts:61](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequences.ts#L61) - -SolidJS primitive for registering multiple keyboard shortcut sequences at once (Vim-style). - -Uses the singleton SequenceManager. Accepts a dynamic array of definitions, or accessors, -so you can react to variable-length lists. - -Options are merged in this order: -HotkeysProvider defaults < commonOptions < per-definition options - -Definitions with an empty `sequence` are skipped (no registration). - -## Parameters - -### sequences - - \| [`CreateHotkeySequenceDefinition`](../interfaces/CreateHotkeySequenceDefinition.md)[] - \| (() => [`CreateHotkeySequenceDefinition`](../interfaces/CreateHotkeySequenceDefinition.md)[]) - -Array of sequence definitions, or accessor returning them - -### commonOptions? - - \| [`CreateHotkeySequenceOptions`](../interfaces/CreateHotkeySequenceOptions.md) - \| (() => [`CreateHotkeySequenceOptions`](../interfaces/CreateHotkeySequenceOptions.md)) - -Shared options for all sequences, or accessor - -## Returns - -`void` - -## Examples - -```tsx -function VimPalette() { - createHotkeySequences([ - { sequence: ['G', 'G'], callback: () => scrollToTop() }, - { sequence: ['D', 'D'], callback: () => deleteLine() }, - ]) -} -``` - -```tsx -function Dynamic(props) { - createHotkeySequences( - () => props.items.map((item) => ({ - sequence: item.chords, - callback: item.action, - options: { enabled: item.enabled }, - })), - { preventDefault: true }, - ) -} -``` diff --git a/docs/framework/solid/reference/functions/createHotkeys.md b/docs/framework/solid/reference/functions/createHotkeys.md index 66430452..87c1cf2f 100644 --- a/docs/framework/solid/reference/functions/createHotkeys.md +++ b/docs/framework/solid/reference/functions/createHotkeys.md @@ -7,58 +7,37 @@ title: createHotkeys function createHotkeys(hotkeys, commonOptions?): void; ``` -Defined in: [createHotkeys.ts:66](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeys.ts#L66) +Defined in: [createHotkeys.ts:39](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeys.ts#L39) -SolidJS primitive for registering multiple keyboard hotkeys at once. +Solid primitive for registering several hotkeys at once: chords, sequences, +and app hotkeys. Pass an accessor for a reactive list. -Uses the singleton HotkeyManager for efficient event handling. -Accepts a dynamic array of hotkey definitions, making it safe to use -with variable-length lists. - -Options are merged in this order: -HotkeysProvider defaults < commonOptions < per-definition options +Options merge in this order: provider defaults, app hotkey declaration, +commonOptions, per-hotkey options. Each option applies only to the kinds +that support it. Unbound app hotkeys and empty sequences register nothing. +A `null` target skips registration until it is set. ## Parameters ### hotkeys - \| [`CreateHotkeyDefinition`](../interfaces/CreateHotkeyDefinition.md)[] - \| (() => [`CreateHotkeyDefinition`](../interfaces/CreateHotkeyDefinition.md)[]) - -Array of hotkey definitions, or accessor returning them + \| readonly [`CreateHotkeyDefinition`](../type-aliases/CreateHotkeyDefinition.md)[] + \| (() => readonly [`CreateHotkeyDefinition`](../type-aliases/CreateHotkeyDefinition.md)[]) ### commonOptions? - \| [`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md) - \| (() => [`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md)) - -Shared options applied to all hotkeys, or accessor + \| [`CreateHotkeyCommonOptions`](../type-aliases/CreateHotkeyCommonOptions.md) + \| (() => [`CreateHotkeyCommonOptions`](../type-aliases/CreateHotkeyCommonOptions.md)) ## Returns `void` -## Examples - -```tsx -function Editor() { - createHotkeys([ - { hotkey: 'Mod+S', callback: () => save() }, - { hotkey: 'Mod+Z', callback: () => undo() }, - { hotkey: 'Escape', callback: () => close() }, - ]) -} -``` +## Example ```tsx -function MenuShortcuts(props) { - createHotkeys( - () => props.items.map((item) => ({ - hotkey: item.shortcut, - callback: item.action, - options: { enabled: item.enabled }, - })), - { preventDefault: true }, - ) -} +createHotkeys([ + { hotkey: 'Mod+S', callback: () => save() }, + { hotkey: ['G', 'G'], callback: () => scrollToTop(), options: { timeout: 500 } }, +]) ``` diff --git a/docs/framework/solid/reference/functions/useAppHotkeys.md b/docs/framework/solid/reference/functions/useAppHotkeys.md new file mode 100644 index 00000000..6b402aa0 --- /dev/null +++ b/docs/framework/solid/reference/functions/useAppHotkeys.md @@ -0,0 +1,24 @@ +--- +id: useAppHotkeys +title: useAppHotkeys +--- + +```ts +function useAppHotkeys(): () => RegisteredAppHotkeys; +``` + +Defined in: appHotkeys.ts:36 + +Returns an accessor for the effective app hotkeys from `HotkeysProvider`, +typed by `Register`: each declaration with the user's custom hotkey applied. + +## Returns + +() => `RegisteredAppHotkeys` + +## Example + +```tsx +const hotkeys = useAppHotkeys() +createHotkey(() => hotkeys()['doc.save'], () => save()) +``` diff --git a/docs/framework/solid/reference/functions/useCustomHotkeys.md b/docs/framework/solid/reference/functions/useCustomHotkeys.md new file mode 100644 index 00000000..987c6603 --- /dev/null +++ b/docs/framework/solid/reference/functions/useCustomHotkeys.md @@ -0,0 +1,18 @@ +--- +id: useCustomHotkeys +title: useCustomHotkeys +--- + +```ts +function useCustomHotkeys(): UseCustomHotkeysResult; +``` + +Defined in: appHotkeys.ts:88 + +Reads and changes the user's custom hotkeys. Changes apply to every +registered app hotkey immediately and are passed to the provider's +`onCustomHotkeysChange`, ready to save. + +## Returns + +[`UseCustomHotkeysResult`](../interfaces/UseCustomHotkeysResult.md) diff --git a/docs/framework/solid/reference/functions/useDefaultHotkeysOptions.md b/docs/framework/solid/reference/functions/useDefaultHotkeysOptions.md index d5367b09..3da71490 100644 --- a/docs/framework/solid/reference/functions/useDefaultHotkeysOptions.md +++ b/docs/framework/solid/reference/functions/useDefaultHotkeysOptions.md @@ -7,7 +7,7 @@ title: useDefaultHotkeysOptions function useDefaultHotkeysOptions(): HotkeysProviderOptions; ``` -Defined in: [HotkeysProvider.tsx:50](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L50) +Defined in: [HotkeysProvider.tsx:114](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L114) ## Returns diff --git a/docs/framework/solid/reference/functions/useHotkeysContext.md b/docs/framework/solid/reference/functions/useHotkeysContext.md index 3d7c3926..1d763b18 100644 --- a/docs/framework/solid/reference/functions/useHotkeysContext.md +++ b/docs/framework/solid/reference/functions/useHotkeysContext.md @@ -7,7 +7,7 @@ title: useHotkeysContext function useHotkeysContext(): HotkeysContextValue | null; ``` -Defined in: [HotkeysProvider.tsx:46](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L46) +Defined in: [HotkeysProvider.tsx:110](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L110) ## Returns diff --git a/docs/framework/solid/reference/index.md b/docs/framework/solid/reference/index.md index 19bbb278..7d0d1ae1 100644 --- a/docs/framework/solid/reference/index.md +++ b/docs/framework/solid/reference/index.md @@ -5,15 +5,21 @@ title: "@tanstack/solid-hotkeys" ## Interfaces -- [CreateHotkeyDefinition](interfaces/CreateHotkeyDefinition.md) -- [CreateHotkeyOptions](interfaces/CreateHotkeyOptions.md) -- [CreateHotkeySequenceDefinition](interfaces/CreateHotkeySequenceDefinition.md) -- [CreateHotkeySequenceOptions](interfaces/CreateHotkeySequenceOptions.md) -- [HotkeyRegistrationsResult](interfaces/HotkeyRegistrationsResult.md) - [HotkeysProviderOptions](interfaces/HotkeysProviderOptions.md) - [HotkeysProviderProps](interfaces/HotkeysProviderProps.md) - [SolidHotkeyRecorder](interfaces/SolidHotkeyRecorder.md) -- [SolidHotkeySequenceRecorder](interfaces/SolidHotkeySequenceRecorder.md) +- [UseCustomHotkeysResult](interfaces/UseCustomHotkeysResult.md) + +## Type Aliases + +- [CreateAppHotkeyOptions](type-aliases/CreateAppHotkeyOptions.md) +- [CreateHotkeyChordOptions](type-aliases/CreateHotkeyChordOptions.md) +- [CreateHotkeyCommonOptions](type-aliases/CreateHotkeyCommonOptions.md) +- [CreateHotkeyDefinition](type-aliases/CreateHotkeyDefinition.md) +- [CreateHotkeyOptions](type-aliases/CreateHotkeyOptions.md) +- [CreateHotkeyOptionsFor](type-aliases/CreateHotkeyOptionsFor.md) +- [CreateHotkeySequenceOptions](type-aliases/CreateHotkeySequenceOptions.md) +- [CreateHotkeyTarget](type-aliases/CreateHotkeyTarget.md) ## Variables @@ -21,6 +27,7 @@ title: "@tanstack/solid-hotkeys" ## Functions +- [createAppHotkeyList](functions/createAppHotkeyList.md) - [createHeldKeyCodes](functions/createHeldKeyCodes.md) - [createHeldKeys](functions/createHeldKeys.md) - [createHotkey](functions/createHotkey.md) @@ -28,9 +35,8 @@ title: "@tanstack/solid-hotkeys" - [createHotkeyRecorder](functions/createHotkeyRecorder.md) - [createHotkeyRegistrations](functions/createHotkeyRegistrations.md) - [createHotkeys](functions/createHotkeys.md) -- [createHotkeySequence](functions/createHotkeySequence.md) -- [createHotkeySequenceRecorder](functions/createHotkeySequenceRecorder.md) -- [createHotkeySequences](functions/createHotkeySequences.md) - [createKeyHold](functions/createKeyHold.md) +- [useAppHotkeys](functions/useAppHotkeys.md) +- [useCustomHotkeys](functions/useCustomHotkeys.md) - [useDefaultHotkeysOptions](functions/useDefaultHotkeysOptions.md) - [useHotkeysContext](functions/useHotkeysContext.md) diff --git a/docs/framework/solid/reference/interfaces/CreateHotkeyDefinition.md b/docs/framework/solid/reference/interfaces/CreateHotkeyDefinition.md deleted file mode 100644 index 65242a37..00000000 --- a/docs/framework/solid/reference/interfaces/CreateHotkeyDefinition.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: CreateHotkeyDefinition -title: CreateHotkeyDefinition ---- - -Defined in: [createHotkeys.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeys.ts#L19) - -A single hotkey definition for use with `createHotkeys`. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [createHotkeys.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeys.ts#L23) - -The function to call when the hotkey is pressed - -*** - -### hotkey - -```ts -hotkey: RegisterableHotkey; -``` - -Defined in: [createHotkeys.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeys.ts#L21) - -The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object - -*** - -### options? - -```ts -optional options?: CreateHotkeyOptions; -``` - -Defined in: [createHotkeys.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeys.ts#L25) - -Per-hotkey options (merged on top of commonOptions) diff --git a/docs/framework/solid/reference/interfaces/CreateHotkeyOptions.md b/docs/framework/solid/reference/interfaces/CreateHotkeyOptions.md deleted file mode 100644 index 3381ec00..00000000 --- a/docs/framework/solid/reference/interfaces/CreateHotkeyOptions.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -id: CreateHotkeyOptions -title: CreateHotkeyOptions ---- - -Defined in: [createHotkey.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkey.ts#L16) - -## Extends - -- `Omit`\<`HotkeyOptions`, `"target"`\> - -## Properties - -### target? - -```ts -optional target?: Document | Window | HTMLElement | null; -``` - -Defined in: [createHotkey.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkey.ts#L24) - -The DOM element to attach the event listener to. -Can be a direct DOM element, an accessor (for reactive targets that become -available after mount), or null. Defaults to document. -When using scoped targets, pass an accessor: () => ({ target: elementSignal() }) -so the hotkey waits for the element to be attached before registering. diff --git a/docs/framework/solid/reference/interfaces/CreateHotkeySequenceDefinition.md b/docs/framework/solid/reference/interfaces/CreateHotkeySequenceDefinition.md deleted file mode 100644 index c8e38b8b..00000000 --- a/docs/framework/solid/reference/interfaces/CreateHotkeySequenceDefinition.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: CreateHotkeySequenceDefinition -title: CreateHotkeySequenceDefinition ---- - -Defined in: [createHotkeySequences.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequences.ts#L14) - -A single sequence definition for use with `createHotkeySequences`. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [createHotkeySequences.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequences.ts#L18) - -The function to call when the sequence is completed - -*** - -### options? - -```ts -optional options?: CreateHotkeySequenceOptions; -``` - -Defined in: [createHotkeySequences.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequences.ts#L20) - -Per-sequence options (merged on top of commonOptions) - -*** - -### sequence - -```ts -sequence: HotkeySequence; -``` - -Defined in: [createHotkeySequences.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequences.ts#L16) - -Array of hotkey strings that form the sequence diff --git a/docs/framework/solid/reference/interfaces/CreateHotkeySequenceOptions.md b/docs/framework/solid/reference/interfaces/CreateHotkeySequenceOptions.md deleted file mode 100644 index e786ce29..00000000 --- a/docs/framework/solid/reference/interfaces/CreateHotkeySequenceOptions.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -id: CreateHotkeySequenceOptions -title: CreateHotkeySequenceOptions ---- - -Defined in: [createHotkeySequence.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequence.ts#L11) - -## Extends - -- `Omit`\<`SequenceOptions`, `"target"`\> - -## Properties - -### target? - -```ts -optional target?: Document | Window | HTMLElement | null; -``` - -Defined in: [createHotkeySequence.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequence.ts#L19) - -The DOM element to attach the event listener to. -Can be a direct DOM element, an accessor, or null. Defaults to document. diff --git a/docs/framework/solid/reference/interfaces/HotkeyRegistrationsResult.md b/docs/framework/solid/reference/interfaces/HotkeyRegistrationsResult.md deleted file mode 100644 index 16076648..00000000 --- a/docs/framework/solid/reference/interfaces/HotkeyRegistrationsResult.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -id: HotkeyRegistrationsResult -title: HotkeyRegistrationsResult ---- - -Defined in: [createHotkeyRegistrations.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRegistrations.ts#L15) - -Return type for createHotkeyRegistrations. - -## Properties - -### hotkeys - -```ts -hotkeys: () => HotkeyRegistrationView[]; -``` - -Defined in: [createHotkeyRegistrations.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRegistrations.ts#L17) - -Accessor for all registered hotkeys (public view, no callbacks) - -#### Returns - -`HotkeyRegistrationView`[] - -*** - -### sequences - -```ts -sequences: () => SequenceRegistrationView[]; -``` - -Defined in: [createHotkeyRegistrations.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRegistrations.ts#L19) - -Accessor for all registered sequences - -#### Returns - -`SequenceRegistrationView`[] diff --git a/docs/framework/solid/reference/interfaces/HotkeysProviderOptions.md b/docs/framework/solid/reference/interfaces/HotkeysProviderOptions.md index baf3e599..f2c42ce8 100644 --- a/docs/framework/solid/reference/interfaces/HotkeysProviderOptions.md +++ b/docs/framework/solid/reference/interfaces/HotkeysProviderOptions.md @@ -3,44 +3,29 @@ id: HotkeysProviderOptions title: HotkeysProviderOptions --- -Defined in: [HotkeysProvider.tsx:10](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L10) +Defined in: [HotkeysProvider.tsx:18](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L18) ## Properties ### hotkey? ```ts -optional hotkey?: Partial; +optional hotkey?: CreateHotkeyCommonOptions; ``` -Defined in: [HotkeysProvider.tsx:11](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L11) +Defined in: [HotkeysProvider.tsx:23](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L23) -*** - -### hotkeyRecorder? - -```ts -optional hotkeyRecorder?: Partial; -``` - -Defined in: [HotkeysProvider.tsx:12](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L12) +Defaults for every hotkey registered by `createHotkey` and `createHotkeys`. +`requireReset` applies to chords and `timeout` applies to sequences. *** -### hotkeySequence? +### hotkeyRecorder? ```ts -optional hotkeySequence?: Partial; +optional hotkeyRecorder?: Partial>; ``` -Defined in: [HotkeysProvider.tsx:14](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L14) - -*** - -### hotkeySequenceRecorder? - -```ts -optional hotkeySequenceRecorder?: Partial; -``` +Defined in: [HotkeysProvider.tsx:25](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L25) -Defined in: [HotkeysProvider.tsx:13](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L13) +Defaults for every `createHotkeyRecorder`. diff --git a/docs/framework/solid/reference/interfaces/HotkeysProviderProps.md b/docs/framework/solid/reference/interfaces/HotkeysProviderProps.md index edef41fc..dccccc63 100644 --- a/docs/framework/solid/reference/interfaces/HotkeysProviderProps.md +++ b/docs/framework/solid/reference/interfaces/HotkeysProviderProps.md @@ -3,17 +3,45 @@ id: HotkeysProviderProps title: HotkeysProviderProps --- -Defined in: [HotkeysProvider.tsx:23](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L23) +Defined in: [HotkeysProvider.tsx:40](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L40) ## Properties +### appHotkeys? + +```ts +optional appHotkeys?: Readonly>>; +``` + +Defined in: [HotkeysProvider.tsx:46](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L46) + +Every app hotkey, from `defineAppHotkeys`. Read when the provider is +created; listed by `createAppHotkeyList` whether or not they are registered. + +*** + ### children ```ts children: Element; ``` -Defined in: [HotkeysProvider.tsx:24](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L24) +Defined in: [HotkeysProvider.tsx:41](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L41) + +*** + +### customHotkeys? + +```ts +optional customHotkeys?: + | CustomHotkeys>>> + | null; +``` + +Defined in: [HotkeysProvider.tsx:51](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L51) + +Starting custom hotkeys, such as saved data. Read once; the provider owns +the state afterwards. Invalid entries are ignored with a warning. *** @@ -23,4 +51,26 @@ Defined in: [HotkeysProvider.tsx:24](https://github.com/TanStack/hotkeys/blob/ma optional defaultOptions?: HotkeysProviderOptions; ``` -Defined in: [HotkeysProvider.tsx:25](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L25) +Defined in: [HotkeysProvider.tsx:54](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L54) + +*** + +### onCustomHotkeysChange? + +```ts +optional onCustomHotkeysChange?: (customHotkeys) => void; +``` + +Defined in: [HotkeysProvider.tsx:53](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L53) + +Called with the custom hotkeys after every change, ready to save. + +#### Parameters + +##### customHotkeys + +`CustomHotkeys` + +#### Returns + +`void` diff --git a/docs/framework/solid/reference/interfaces/SolidHotkeyRecorder.md b/docs/framework/solid/reference/interfaces/SolidHotkeyRecorder.md index 4990262a..5090ea08 100644 --- a/docs/framework/solid/reference/interfaces/SolidHotkeyRecorder.md +++ b/docs/framework/solid/reference/interfaces/SolidHotkeyRecorder.md @@ -3,7 +3,13 @@ id: SolidHotkeyRecorder title: SolidHotkeyRecorder --- -Defined in: [createHotkeyRecorder.ts:7](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L7) +Defined in: [createHotkeyRecorder.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L14) + +## Type Parameters + +### THotkey + +`THotkey` *extends* `Hotkey` = `Hotkey` ## Properties @@ -13,9 +19,25 @@ Defined in: [createHotkeyRecorder.ts:7](https://github.com/TanStack/hotkeys/blob cancelRecording: () => void; ``` -Defined in: [createHotkeyRecorder.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L17) +Defined in: [createHotkeyRecorder.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L26) + +Cancel recording and call onCancel + +#### Returns + +`void` + +*** + +### commitRecording + +```ts +commitRecording: () => void; +``` + +Defined in: [createHotkeyRecorder.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L28) -Cancel recording without saving +Commit the recorded steps now (multi-step recordings) #### Returns @@ -29,7 +51,7 @@ Cancel recording without saving isRecording: () => boolean; ``` -Defined in: [createHotkeyRecorder.ts:9](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L9) +Defined in: [createHotkeyRecorder.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L16) Whether recording is currently active @@ -42,16 +64,16 @@ Whether recording is currently active ### recordedHotkey ```ts -recordedHotkey: () => Hotkey | null; +recordedHotkey: () => THotkey | null; ``` -Defined in: [createHotkeyRecorder.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L11) +Defined in: [createHotkeyRecorder.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L20) -The currently recorded hotkey (for live preview) +The last committed hotkey #### Returns -`Hotkey` \| `null` +`THotkey` \| `null` *** @@ -61,7 +83,7 @@ The currently recorded hotkey (for live preview) startRecording: () => void; ``` -Defined in: [createHotkeyRecorder.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L13) +Defined in: [createHotkeyRecorder.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L22) Start recording a new hotkey @@ -71,15 +93,31 @@ Start recording a new hotkey *** +### steps + +```ts +steps: () => HotkeySequence; +``` + +Defined in: [createHotkeyRecorder.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L18) + +Chords recorded so far in a multi-step recording (for live preview) + +#### Returns + +`HotkeySequence` + +*** + ### stopRecording ```ts stopRecording: () => void; ``` -Defined in: [createHotkeyRecorder.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L15) +Defined in: [createHotkeyRecorder.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeyRecorder.ts#L24) -Stop recording (same as cancel) +Stop recording without calling onCancel #### Returns diff --git a/docs/framework/solid/reference/interfaces/SolidHotkeySequenceRecorder.md b/docs/framework/solid/reference/interfaces/SolidHotkeySequenceRecorder.md deleted file mode 100644 index 71f59976..00000000 --- a/docs/framework/solid/reference/interfaces/SolidHotkeySequenceRecorder.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -id: SolidHotkeySequenceRecorder -title: SolidHotkeySequenceRecorder ---- - -Defined in: [createHotkeySequenceRecorder.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequenceRecorder.ts#L10) - -## Properties - -### cancelRecording - -```ts -cancelRecording: () => void; -``` - -Defined in: [createHotkeySequenceRecorder.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequenceRecorder.ts#L16) - -#### Returns - -`void` - -*** - -### commitRecording - -```ts -commitRecording: () => void; -``` - -Defined in: [createHotkeySequenceRecorder.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequenceRecorder.ts#L17) - -#### Returns - -`void` - -*** - -### isRecording - -```ts -isRecording: () => boolean; -``` - -Defined in: [createHotkeySequenceRecorder.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequenceRecorder.ts#L11) - -#### Returns - -`boolean` - -*** - -### recordedSequence - -```ts -recordedSequence: () => HotkeySequence | null; -``` - -Defined in: [createHotkeySequenceRecorder.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequenceRecorder.ts#L13) - -#### Returns - -`HotkeySequence` \| `null` - -*** - -### startRecording - -```ts -startRecording: () => void; -``` - -Defined in: [createHotkeySequenceRecorder.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequenceRecorder.ts#L14) - -#### Returns - -`void` - -*** - -### steps - -```ts -steps: () => HotkeySequence; -``` - -Defined in: [createHotkeySequenceRecorder.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequenceRecorder.ts#L12) - -#### Returns - -`HotkeySequence` - -*** - -### stopRecording - -```ts -stopRecording: () => void; -``` - -Defined in: [createHotkeySequenceRecorder.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeySequenceRecorder.ts#L15) - -#### Returns - -`void` diff --git a/docs/framework/solid/reference/interfaces/UseCustomHotkeysResult.md b/docs/framework/solid/reference/interfaces/UseCustomHotkeysResult.md new file mode 100644 index 00000000..64459be7 --- /dev/null +++ b/docs/framework/solid/reference/interfaces/UseCustomHotkeysResult.md @@ -0,0 +1,115 @@ +--- +id: UseCustomHotkeysResult +title: UseCustomHotkeysResult +--- + +Defined in: appHotkeys.ts:61 + +Custom hotkey state and actions returned by `useCustomHotkeys`. + +## Properties + +### customHotkeys + +```ts +customHotkeys: () => CustomHotkeys; +``` + +Defined in: appHotkeys.ts:63 + +The user's hotkeys that differ from the defaults. + +#### Returns + +`CustomHotkeys` + +*** + +### findConflicts + +```ts +findConflicts: (id, hotkey) => AppHotkey[]; +``` + +Defined in: appHotkeys.ts:77 + +Other app hotkeys that would collide if `id` used `hotkey`. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] + +*** + +### resetCustomHotkey + +```ts +resetCustomHotkey: (id) => void; +``` + +Defined in: appHotkeys.ts:73 + +Restores the default hotkey for an app hotkey. + +#### Parameters + +##### id + +`string` + +#### Returns + +`void` + +*** + +### resetCustomHotkeys + +```ts +resetCustomHotkeys: () => void; +``` + +Defined in: appHotkeys.ts:75 + +Restores every default hotkey. + +#### Returns + +`void` + +*** + +### setCustomHotkey + +```ts +setCustomHotkey: (id, hotkey) => AppHotkey[]; +``` + +Defined in: appHotkeys.ts:68 + +Sets the user's hotkey for an app hotkey. `null` unbinds it. +Returns conflicting app hotkeys; when any exist, nothing changes. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` \| `null` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] diff --git a/docs/framework/solid/reference/type-aliases/CreateAppHotkeyOptions.md b/docs/framework/solid/reference/type-aliases/CreateAppHotkeyOptions.md new file mode 100644 index 00000000..ba36224a --- /dev/null +++ b/docs/framework/solid/reference/type-aliases/CreateAppHotkeyOptions.md @@ -0,0 +1,18 @@ +--- +id: CreateAppHotkeyOptions +title: CreateAppHotkeyOptions +--- + +```ts +type CreateAppHotkeyOptions = AdapterAppHotkeyOptions; +``` + +Defined in: [createHotkey.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkey.ts#L42) + +Call-site options for an app hotkey, typed by its declared default kind. + +## Type Parameters + +### T + +`T` *extends* `AppHotkey` = `AppHotkey` diff --git a/docs/framework/solid/reference/type-aliases/CreateHotkeyChordOptions.md b/docs/framework/solid/reference/type-aliases/CreateHotkeyChordOptions.md new file mode 100644 index 00000000..70b8d44e --- /dev/null +++ b/docs/framework/solid/reference/type-aliases/CreateHotkeyChordOptions.md @@ -0,0 +1,12 @@ +--- +id: CreateHotkeyChordOptions +title: CreateHotkeyChordOptions +--- + +```ts +type CreateHotkeyChordOptions = AdapterChordOptions; +``` + +Defined in: [createHotkey.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkey.ts#L28) + +Options for a chord such as `'Mod+S'`. diff --git a/docs/framework/solid/reference/type-aliases/CreateHotkeyCommonOptions.md b/docs/framework/solid/reference/type-aliases/CreateHotkeyCommonOptions.md new file mode 100644 index 00000000..b79b306b --- /dev/null +++ b/docs/framework/solid/reference/type-aliases/CreateHotkeyCommonOptions.md @@ -0,0 +1,12 @@ +--- +id: CreateHotkeyCommonOptions +title: CreateHotkeyCommonOptions +--- + +```ts +type CreateHotkeyCommonOptions = AdapterCommonOptions; +``` + +Defined in: [createHotkey.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkey.ts#L35) + +Options applied to every hotkey, such as provider defaults. Each applies to the kinds that support it. diff --git a/docs/framework/solid/reference/type-aliases/CreateHotkeyDefinition.md b/docs/framework/solid/reference/type-aliases/CreateHotkeyDefinition.md new file mode 100644 index 00000000..e54b2f0c --- /dev/null +++ b/docs/framework/solid/reference/type-aliases/CreateHotkeyDefinition.md @@ -0,0 +1,13 @@ +--- +id: CreateHotkeyDefinition +title: CreateHotkeyDefinition +--- + +```ts +type CreateHotkeyDefinition = AdapterHotkeyDefinition; +``` + +Defined in: [createHotkeys.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkeys.ts#L20) + +One hotkey for `createHotkeys`. Chords accept `requireReset`; sequences +accept `timeout`; hotkeys of unknown kind accept only shared options. diff --git a/docs/framework/solid/reference/type-aliases/CreateHotkeyOptions.md b/docs/framework/solid/reference/type-aliases/CreateHotkeyOptions.md new file mode 100644 index 00000000..9592e42f --- /dev/null +++ b/docs/framework/solid/reference/type-aliases/CreateHotkeyOptions.md @@ -0,0 +1,12 @@ +--- +id: CreateHotkeyOptions +title: CreateHotkeyOptions +--- + +```ts +type CreateHotkeyOptions = AdapterHotkeyOptions; +``` + +Defined in: [createHotkey.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkey.ts#L25) + +Options shared by chords and sequences. diff --git a/docs/framework/solid/reference/type-aliases/CreateHotkeyOptionsFor.md b/docs/framework/solid/reference/type-aliases/CreateHotkeyOptionsFor.md new file mode 100644 index 00000000..d422cf9a --- /dev/null +++ b/docs/framework/solid/reference/type-aliases/CreateHotkeyOptionsFor.md @@ -0,0 +1,18 @@ +--- +id: CreateHotkeyOptionsFor +title: CreateHotkeyOptionsFor +--- + +```ts +type CreateHotkeyOptionsFor = AdapterOptionsFor; +``` + +Defined in: [createHotkey.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkey.ts#L38) + +The options type for a hotkey: chord options for chords, sequence options for sequences. + +## Type Parameters + +### T + +`T` *extends* `RegisterableHotkey` diff --git a/docs/framework/solid/reference/type-aliases/CreateHotkeySequenceOptions.md b/docs/framework/solid/reference/type-aliases/CreateHotkeySequenceOptions.md new file mode 100644 index 00000000..23665b1e --- /dev/null +++ b/docs/framework/solid/reference/type-aliases/CreateHotkeySequenceOptions.md @@ -0,0 +1,12 @@ +--- +id: CreateHotkeySequenceOptions +title: CreateHotkeySequenceOptions +--- + +```ts +type CreateHotkeySequenceOptions = AdapterSequenceOptions; +``` + +Defined in: [createHotkey.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkey.ts#L31) + +Options for a sequence such as `['G', 'G']`. diff --git a/docs/framework/solid/reference/type-aliases/CreateHotkeyTarget.md b/docs/framework/solid/reference/type-aliases/CreateHotkeyTarget.md new file mode 100644 index 00000000..0d9c6307 --- /dev/null +++ b/docs/framework/solid/reference/type-aliases/CreateHotkeyTarget.md @@ -0,0 +1,14 @@ +--- +id: CreateHotkeyTarget +title: CreateHotkeyTarget +--- + +```ts +type CreateHotkeyTarget = Target | null; +``` + +Defined in: [createHotkey.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/createHotkey.ts#L22) + +The element to listen on. A `null` target skips registration, so pass an +accessor such as `() => ({ target: element() })` for elements that attach +after mount. diff --git a/docs/framework/solid/reference/variables/HotkeysProvider.md b/docs/framework/solid/reference/variables/HotkeysProvider.md index 4804d333..2d9ebcf6 100644 --- a/docs/framework/solid/reference/variables/HotkeysProvider.md +++ b/docs/framework/solid/reference/variables/HotkeysProvider.md @@ -7,4 +7,20 @@ title: HotkeysProvider const HotkeysProvider: ParentComponent; ``` -Defined in: [HotkeysProvider.tsx:30](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L30) +Defined in: [HotkeysProvider.tsx:75](https://github.com/TanStack/hotkeys/blob/main/packages/solid-hotkeys/src/HotkeysProvider.tsx#L75) + +Provides default options, app hotkeys, and the user's custom hotkeys. + +## Example + +```tsx + + localStorage.setItem('hotkeys', JSON.stringify(custom)) + } +> + + +``` diff --git a/docs/framework/svelte/guides/app-hotkeys.md b/docs/framework/svelte/guides/app-hotkeys.md new file mode 100644 index 00000000..54b92b33 --- /dev/null +++ b/docs/framework/svelte/guides/app-hotkeys.md @@ -0,0 +1,194 @@ +--- +title: App Hotkeys Guide +id: app-hotkeys +--- + +`createHotkey` and `createHotkeys` register a hotkey while its component is mounted. A cheat sheet built from live registrations therefore misses hotkeys on routes that have not rendered yet, and a settings screen has nowhere to change them. App hotkeys solve both: you declare every hotkey in one place, pass the declarations to `HotkeysProvider` (or `setHotkeysContext` in a root component's script), and attach callbacks where the work happens. Users can then customize any of them, and your app decides how to save their choices. + +A declaration holds a hotkey's id, default hotkey, metadata, and options. It never holds a callback; `createHotkey` and `createHotkeys` still attach the callbacks. + +## Declare app hotkeys + +Use `defineAppHotkeys` in a module that contains only declarations. Keep it free of components and handlers, because it loads with your app shell: + +```ts +// app-hotkeys.ts +import { defineAppHotkeys } from '@tanstack/svelte-hotkeys' + +export const appHotkeys = defineAppHotkeys({ + 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save', group: 'Document' } }, + 'doc.find': { + hotkey: 'Mod+F', + meta: { name: 'Find', group: 'Document' }, + context: 'editor', + }, + 'nav.home': { + hotkey: ['G', 'H'], + meta: { name: 'Go home', group: 'Navigation' }, + options: { timeout: 800 }, + }, + 'palette.open': { hotkey: null, meta: { name: 'Command palette' } }, +}) + +declare module '@tanstack/svelte-hotkeys' { + interface Register { + appHotkeys: typeof appHotkeys + } +} +``` + +- The object key is the app hotkey's id. Treat ids as a stable contract, because saved custom hotkeys use them as keys. +- `hotkey` is the default: a chord, a sequence, or `null` for an action without a default. An unbound app hotkey registers nothing until the user assigns one. +- `options` describe the hotkey itself: `conflictBehavior`, `eventType`, `ignoreInputs`, `preventDefault`, `stopPropagation`, plus `requireReset` for a chord default or `timeout` for a sequence default. Options that don't match the default's kind are type errors. Pass `target` and `enabled` where you register the hotkey. +- `context` labels where a hotkey applies, such as `'editor'`. App hotkeys with different contexts may share a hotkey. It is used only for conflict checks and does not affect matching. + +The `Register` augmentation types the app hotkeys you read back, the ids accepted when changing custom hotkeys, and the `CustomHotkeys` type. + +## Provide app hotkeys + +```svelte + + + + + +``` + +## Register app hotkeys + +Pass an app hotkey wherever you pass a hotkey: + +```ts +import { createHotkey, createHotkeys, getAppHotkeys } from '@tanstack/svelte-hotkeys' + +const hotkeys = getAppHotkeys() + +createHotkey(() => hotkeys.current['doc.save'], () => save(), () => ({ + enabled: isDirty, +})) +createHotkeys(() => [ + { hotkey: hotkeys.current['nav.home'], callback: () => goto('/') }, + { hotkey: 'Escape', callback: close }, +]) +``` + +Read `getAppHotkeys().current` inside getters so registrations follow changes. You can also import an app hotkey directly, such as `createHotkey(appHotkeys['doc.save'], save)`. Without provided app hotkeys, the declared default is used. + +The registration takes `meta` and `options` from the declaration. Options merge in this order: default options, then declared options, then call-site options. `meta` comes only from the declaration, so the cheat sheet and the registration cannot disagree. Call-site options are typed by the declared default's kind, so `timeout` is accepted for `nav.home` but not for `doc.save`. + +When the user changes a hotkey, registered app hotkeys move to the new hotkey immediately, even if it changes from a chord to a sequence. + +## Build a cheat sheet + +`getAppHotkeyList` returns every app hotkey in declaration order, with its live status: + +| Status | Meaning | +| --- | --- | +| `'active'` | At least one enabled registration | +| `'disabled'` | Registered, but every registration has `enabled: false` | +| `'inactive'` | Has a hotkey, but nothing has registered it | +| `'unbound'` | No hotkey assigned | + +```svelte + + +
      + {#each items.current as { appHotkey, status, isCustom } (appHotkey.id)} +
    • + {appHotkey.meta?.name}: + {appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned'} + {#if isCustom}(custom){/if} +
    • + {/each} +
    +``` + +An app hotkey registered by many components, such as one per list row, appears once. Its `registrations` array holds each live registration. Devtools also list declared app hotkeys that are not registered. + +## Customize and persist hotkeys + +TanStack Hotkeys does not store anything. Pass the user's saved custom hotkeys in and save them when they change: + +```svelte + + + + localStorage.setItem('hotkeys', JSON.stringify(custom))} +> + + +``` + +`setHotkeysContext` accepts the same `appHotkeys`, `customHotkeys`, and `onCustomHotkeysChange` options. + +- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them. +- `customHotkeys` is read once; afterwards the shared store owns the state. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning. +- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile. + +Recorded hotkeys use `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others. + +### Change hotkeys + +`getCustomHotkeys` reads and changes the user's custom hotkeys: + +```svelte + + +
    + {appHotkey.meta?.name}: + {appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned'} + + +
    +``` + +| Member | Description | +| --- | --- | +| `current` | The hotkeys that differ from the defaults | +| `setCustomHotkey(id, hotkey)` | Sets a hotkey, or unbinds with `null`. Returns conflicting app hotkeys; when there are any, nothing changes. | +| `findConflicts(id, hotkey)` | App hotkeys that would collide if `id` used `hotkey` | +| `resetCustomHotkey(id)` | Restores one default | +| `resetCustomHotkeys()` | Restores every default | + +Two app hotkeys conflict when their contexts match or either has no `context`, and their hotkeys collide: equal chords, or one sequence starts with the other, including a sequence whose first step equals a chord. Conflicts are checked across every declaration, whether or not it is registered. + +Each app hotkey's `defaultHotkey` holds its declared default, and each cheat sheet item's `isCustom` reports whether the effective hotkey differs from it. diff --git a/docs/framework/svelte/guides/formatting-display.md b/docs/framework/svelte/guides/formatting-display.md index f7e4a8fa..c050a311 100644 --- a/docs/framework/svelte/guides/formatting-display.md +++ b/docs/framework/svelte/guides/formatting-display.md @@ -3,7 +3,7 @@ title: Formatting & Display Guide id: formatting-display --- -Use `formatForDisplay` whenever a binding appears in a menu, button, hint, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, and parsed bindings. Store the original binding and format it at render time: the display label is not a registration string. +Use `formatForDisplay` whenever a hotkey appears in a menu, button, hint, cheat sheet, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, parsed bindings, and sequences. Store the original binding and format it at render time: the display label is not a registration string. ## Format a binding @@ -32,16 +32,18 @@ const parts = formatForDisplay(binding, { platform: 'mac', parts: true }) With `parts` omitted or false, the result is a string. A runtime boolean returns `string | string[]`. Parts preserve literal plus keys and ignore `separatorToken`. -Sequences are arrays of bindings. Format their steps individually: +## Format sequences -```ts -import type { HotkeySequence } from '@tanstack/svelte-hotkeys' +Pass a sequence to format each step and join the steps with `sequenceSeparator`, a space by default: -const sequence: HotkeySequence = ['Mod+[KeyK]', 'C'] -const label = sequence.map((step) => formatForDisplay(step)).join(' → ') +```ts +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac' }) // '⌘ K C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' → ' }) // 'G → G' +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac', parts: true }) +// [['⌘', 'K'], ['C']] — one array of keycaps per step ``` -`formatHotkeySequence` only joins stored strings with spaces. It intentionally retains brackets and code names, so use the code above for user-facing labels. +With `parts: true`, a sequence returns one array per step, so you can render each step as a group of keycaps. ## Choose symbols and separators @@ -63,7 +65,7 @@ formatForDisplay('Control++', { }) // 'Ctrl · +' ``` -An empty separator joins labels directly. `undefined` or `null` uses the platform default. `formatWithLabels(binding, options)` is the shorthand for `formatForDisplay` with `useSymbols: false`. +An empty separator joins labels directly. `undefined` or `null` uses the platform default. `separatorToken` applies within a chord; `sequenceSeparator` applies between steps. ## Supply layout labels @@ -88,6 +90,10 @@ formatForDisplay('Mod+[KeyQ]', { The precedence is `keyLabels`, then a layout entry, then the fallback label. Layout entries receive normal letter casing and key symbols; missing or empty entries fall back. Explicit labels are final. All of these options affect display only. +## Compare hotkeys + +`areHotkeysEqual(a, b)` compares chords or sequences after resolving modifiers, so `Mod+S` equals `Meta+S` on macOS. Sequences compare step by step. `isHotkeySequence` narrows a `Hotkey` to a sequence, and `simplifyHotkey` turns a one-step sequence into its chord. + ## Parse and store bindings `parseHotkey` returns either a logical `key` or a physical `code`, plus resolved modifier flags. Narrow the union before inspecting the identity: @@ -105,6 +111,6 @@ formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S' Parsed modifiers are already resolved. To display a portable `Mod` binding on another platform, serialize with the original platform first, as above. `normalizeRegisterableHotkey` accepts strings or raw objects and preserves the logical/physical distinction. Do not store display labels or put a bracketed code in a logical `key` field. -Use `validateHotkey` when accepting strings from an external source. It returns `valid`, `errors`, and `warnings`; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). +Use `validateHotkey` when accepting a chord or sequence from an external source. It returns `valid`, `errors`, and `warnings`, and names the failing step of a sequence; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). Try these options together in the [vanilla formatter playground](../../../framework/vanilla/examples/formatForDisplay). diff --git a/docs/framework/svelte/guides/hotkey-recording.md b/docs/framework/svelte/guides/hotkey-recording.md index 57608648..70bcf8c4 100644 --- a/docs/framework/svelte/guides/hotkey-recording.md +++ b/docs/framework/svelte/guides/hotkey-recording.md @@ -3,114 +3,131 @@ title: Hotkey Recording Guide id: hotkey-recording --- -TanStack Hotkeys provides the `createHotkeyRecorder` function for building shortcut customization UIs in Svelte. +`createHotkeyRecorder` is for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the key combination they want, the same way system preferences or IDE shortcut editors work. By default it records one chord; set `maxSteps` to let users record a [sequence](./sequences) too. Recorders default to physical codes: recording a shortcut stores a string such as `Mod+[KeyS]`. Pass it directly to your hotkey registration and use `formatForDisplay` for the label. Set `recordBy: 'key'` when you intentionally want the produced character instead. -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Allow the framework to run its effect or watcher before relying on the update. +TanStack Hotkeys automatically suppresses registered hotkey callbacks while any recorder is active. You do not need to disable hotkeys while recording. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. ## Basic usage ```svelte -
    - - {#if recorder.isRecording} - - {/if} -
    + ``` -## Return value +## Recorder state and actions + +| Member | Description | +| --- | --- | +| `isRecording` | Whether the recorder is currently listening for key presses | +| `steps` | Chords captured so far in a multi-step recording | +| `recordedHotkey` | The last recorded hotkey, or `null` if nothing recorded | +| `startRecording()` | Start listening for key presses | +| `stopRecording()` | Stop listening without calling `onRecord` or `onCancel` | +| `cancelRecording()` | Stop listening and call `onCancel` | +| `commitRecording()` | Commit the current `steps` now (multi-step recordings) | -- `isRecording`: whether recording is active -- `recordedHotkey`: the most recently recorded hotkey -- `startRecording()`: start listening for key presses -- `stopRecording()`: stop listening and reset recorder state without calling `onRecord` -- `cancelRecording()`: stop listening and discard the in-progress recording +State members are reactive getters. Read them from the recorder object instead of destructuring them once. ## Options ### `recordBy` -Recorders default to `recordBy: 'code'`. Option+S producing `ß` on macOS records `Alt+[KeyS]`; Option+2 producing `™` records `Alt+[Digit2]`. The stored brackets preserve physical identity through state, serialization, and registration; `formatForDisplay` produces a readable label. There is no code-to-letter conversion. Existing authored strings such as `Mod+S` remain logical bindings. +Recorders default to `recordBy: 'code'`. Option+S producing `ß` on macOS records `Alt+[KeyS]`; Option+2 producing `™` records `Alt+[Digit2]`. The stored brackets preserve physical identity through state, serialization, and registration; `formatForDisplay` produces a readable label. Existing authored strings such as `Mod+S` remain logical hotkeys. -Set `recordBy: 'key'` to intentionally record the produced logical character. A code-mode event with no usable code is rejected rather than silently switching modes. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the produced character without synthetic Control/Alt while retaining Shift. Recording events, repeats, and their releases do not trigger registered hotkeys or sequences. +Set `recordBy: 'key'` to intentionally record the produced logical character. A code-mode event with no usable code is rejected rather than silently switching modes. IME composition is ignored. AltGraph character entry is rejected in code mode; key mode preserves the produced character without synthetic Control/Alt while retaining Shift. -```ts -createHotkeyRecorder({ - recordBy: 'code', // default; choose 'key' for logical characters - onRecord: (hotkey) => {}, - onCancel: () => {}, - onClear: () => {}, -}) -``` +### `onRecord` -Options can also be reactive: +Called when a recording commits. With the default `maxSteps: 1`, it receives a chord string such as `'Mod+[KeyS]'` as soon as the user presses a valid combination (a modifier + a non-modifier key, or a single non-modifier key). With a larger `maxSteps`, it receives a chord when one step was recorded, or a sequence otherwise. -```svelte - -``` +### `onClear` + +Called when the recording is cleared: Backspace or Delete with no modifiers and no recorded steps. Clearing calls only `onClear`; your app decides whether that removes a hotkey or restores a default. ### `ignoreInputs` -The `HotkeyRecorderOptions` supports an `ignoreInputs` option (defaults to `true`). When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements. Keystrokes pass through to the input as usual. Pressing Escape still cancels recording even when focused on an input. Set `ignoreInputs: false` if you want the recorder to capture keys from within input elements. +Defaults to `true`. The recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Escape still cancels recording even while an input is focused. Set `ignoreInputs: false` to record from inside input elements. + +Options can also be a getter, such as `createHotkeyRecorder(() => ({ maxSteps: 3, recordBy: mode, onRecord }))`, so recording policy can follow `$state`. + +### Default options + +Set defaults for every recorder with `HotkeysProvider` (or `setHotkeysContext` in a root component's script). Per-recorder options override them. + +```svelte + + + +``` + +## Recording sequences + +Set `maxSteps` above 1 to record up to that many chords. Each chord adds a step, and the recording commits when the user presses Enter, when it reaches `maxSteps`, or after `idleTimeoutMs` without a new step: ```ts -createHotkeyRecorder({ - ignoreInputs: false, // record even from inside inputs - onRecord: (hotkey) => console.log(hotkey), +const recorder = createHotkeyRecorder({ + maxSteps: 3, + onRecord: (hotkey) => console.log('Recorded:', hotkey), // 'Mod+[KeyS]' or ['[KeyG]', '[KeyG]'] }) + +// Live preview while recording +const label = $derived( + recorder.isRecording + ? formatForDisplay(recorder.steps) || 'Press chords, then Enter…' + : formatForDisplay(recorder.recordedHotkey ?? []), +) ``` +A one-step recording commits as a chord, so the result can always be passed straight to your hotkey registration. Use `maxSteps: Infinity` for no limit. + +| Option | Description | +| --- | --- | +| `maxSteps` | Maximum chords to record. Reaching it commits. Default `1`. | +| `commitKeys` | `'enter'` (default) commits on plain Enter. With `'none'`, plain Enter can be recorded as a step, and only `commitRecording()`, `idleTimeoutMs`, or `maxSteps` commit. | +| `idleTimeoutMs` | Commit automatically after this many milliseconds without a new step. The timer does not run before the first chord. | + +`commitKeys` and `idleTimeoutMs` are only accepted when `maxSteps` is set. With `maxSteps` above 1, `onRecord` and `validate` receive `Hotkey` (a chord or a sequence) instead of a chord. + ## 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. +| Key | Behavior | +| --- | --- | +| Modifier only (Shift, Ctrl, etc.) | Waits for a non-modifier key; modifier-only presses don't complete a step | +| Modifier + key (e.g., Ctrl+S) | Records the full combination | +| Single key (e.g., Escape, F1) | Records the single key | +| Enter (multi-step, `commitKeys: 'enter'`) | Commits the recorded steps | +| Escape | Cancels the recording | +| Backspace / Delete | Removes the last step; with no steps, clears and calls `onClear` | + +Repeats and IME composition never add a step. Recorded hotkeys use the portable `Mod` format: Command+S on macOS records `Mod+[KeyS]` rather than `Meta+[KeyS]`, so the primary modifier becomes Control if the hotkey is later used on Windows. ## Validation and conflicts -Both recorder APIs accept `detectConflicts`, `validate`, and `onReject`. For a single-hotkey recorder, these options can be supplied alongside `onRecord`: +The recorder accepts `detectConflicts`, `validate`, and `onReject` alongside `onRecord`: ```ts const options = { detectConflicts: { - // Use registration IDs to exclude the binding being edited: + // Use registration IDs to exclude the hotkey being edited: excludeIds: [registrationId], target: document, eventType: 'keydown', @@ -124,8 +141,62 @@ const options = { } ``` -`validate` returns true to accept, or false/a message to reject. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the candidate when available, and conflicting registration views for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. +`validate(hotkey, context)` returns true to accept, or false/a message to reject. The context holds the last chord's `event` and `parsedHotkey`, plus `events` and `parsedSequence` for every step. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the rejected `hotkey` when available, and conflicting registrations for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. A rejected multi-step recording keeps its steps so the user can edit them with Backspace. + +`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a hotkey. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Chords and sequence prefixes are checked: a recorded chord conflicts with a sequence that starts with it, and a recorded sequence conflicts with a registered chord equal to its first step. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute. + +For checks outside recording, use `findHotkeyConflicts(hotkey, options)`. Without source `events`, it compares hotkey identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. + +## Building a shortcut settings UI + +For an app-wide settings screen, declare the hotkeys with [app hotkeys](./app-hotkeys) and save the recorder's result with `setCustomHotkey`. That keeps cheat sheets, conflicts, and persistence in one place. + +For a single component, ordinary state is enough: + +```svelte + + +{#each Object.entries(shortcuts) as [action, hotkey] (action)} + +{/each} +``` -`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a binding. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Both single bindings and sequence prefixes are checked. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can affect actual dispatch. +## Under the hood -For checks outside recording, use `findHotkeyConflicts(bindingOrSequence, options)`. Without source `events`, it compares binding identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. +`createHotkeyRecorder` creates a `HotkeyRecorder` from `@tanstack/hotkeys`, exposes its state through reactive getters, and destroys it when the component unmounts. diff --git a/docs/framework/svelte/guides/hotkeys.md b/docs/framework/svelte/guides/hotkeys.md index 66fa0f1b..241ded19 100644 --- a/docs/framework/svelte/guides/hotkeys.md +++ b/docs/framework/svelte/guides/hotkeys.md @@ -5,6 +5,18 @@ id: hotkeys Use `createHotkey` for global shortcuts and `createHotkeyAttachment` for element-scoped shortcuts. The common global case stays simple, and scoped behavior feels native to Svelte 5. +## Chords and sequences + +A hotkey is either a **chord**, keys pressed together such as `'Mod+S'`, or a **sequence**, chords pressed one after another such as `['G', 'G']`. Every API in this guide accepts both, and a one-step array such as `['Mod+S']` is the chord `'Mod+S'`. + +```ts +createHotkey('Mod+S', () => save()) // chord +createHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 }) // sequence +createHotkey('Escape', () => close(), { requireReset: true }) // chord-only option +``` + +Most options apply to both kinds. `requireReset` applies only to chords and `timeout` only to sequences, and TypeScript rejects options that don't match the hotkey. When the kind isn't known statically, such as a `Hotkey` loaded from settings, only the shared options are accepted. Defaults apply only to the kinds that support them. See the [Sequences Guide](./sequences) for timing and matching, and the [App Hotkeys Guide](./app-hotkeys) to declare hotkeys up front and let users customize them. + ## Logical keys and physical positions Use a logical binding when the shortcut should follow the character on the active layout. Use a physical binding when it should follow a keyboard position: @@ -265,7 +277,7 @@ Group is descriptive metadata, not an execution scope. A shortcuts panel can gro ## Introspecting registrations -Use the `getHotkeyRegistrations` function to get a live view of all hotkey and sequence registrations. This is useful for building shortcut palettes, help dialogs, or devtools. +Use the `getHotkeyRegistrations` function to get a live view of every registration, chords and sequences together. It's the building block for shortcut palettes and help dialogs. ```svelte -
    -

    Keyboard Shortcuts

    -
      - {#each registrations.hotkeys as reg (reg.hotkey)} -
    • - {formatForDisplay(reg.hotkey)} - {#if reg.options.meta?.name} - — {reg.options.meta.name} - {/if} - {#if reg.options.meta?.description} -

      {reg.options.meta.description}

      - {/if} -
    • - {/each} -
    - {#if registrations.sequences.length > 0} -

    Sequences

    -
      - {#each registrations.sequences as reg (reg.sequence.join(' '))} -
    • - {reg.sequence.map((step) => formatForDisplay(step)).join(' → ')} - {#if reg.options.meta?.name} - — {reg.options.meta.name} - {/if} -
    • - {/each} -
    - {/if} -
    +
      + {#each registrations.hotkeys as reg (reg.id)} +
    • + {formatForDisplay(reg.hotkey)} + {#if reg.options.meta?.name} — {reg.options.meta.name}{/if} + {#if reg.kind === 'sequence'} + {reg.matchedStepCount}/{reg.hotkey.length} steps + {/if} +
    • + {/each} +
    ``` -The returned object contains a `hotkeys` array with registration objects including the hotkey string, options (including `meta`), and enabled state, and a `sequences` array containing sequence registrations with the same structure. +The returned object's reactive `hotkeys` getter holds one array of registration views. Each view has an `id`, the `hotkey`, its `options` (including `meta`), its `target`, and a `triggerCount`. Narrow with `kind`: chord views add `parsedHotkey`; sequence views add `parsedSequence` and `matchedStepCount`, the steps matched so far in the current attempt. Disabled entries stay listed; unmounted registrations disappear. Format any hotkey with `formatForDisplay`, and group entries by `options.meta?.group` when building a help panel. + +Registrations only exist while their components are mounted. To list hotkeys from routes that have not rendered, declare them with [app hotkeys](./app-hotkeys). ## The hotkey manager diff --git a/docs/framework/svelte/guides/key-state-tracking.md b/docs/framework/svelte/guides/key-state-tracking.md index af575606..f12149be 100644 --- a/docs/framework/svelte/guides/key-state-tracking.md +++ b/docs/framework/svelte/guides/key-state-tracking.md @@ -104,6 +104,6 @@ tracker.isKeyHeld('Shift') const hint = getHotkeyHint(() => binding) // reactive getter: hint.visible ``` -For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. +For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. A sequence reveals its hint from its first step, and an app hotkey uses its effective hotkey (no hint while unbound). Combine the boolean with the action's enabled state. The helper does not register a shortcut or determine whether its target is focused. The core equivalent is `matchesHeldModifiers(binding, heldKeys, options)`. diff --git a/docs/framework/svelte/guides/sequence-recording.md b/docs/framework/svelte/guides/sequence-recording.md deleted file mode 100644 index 14eab8c5..00000000 --- a/docs/framework/svelte/guides/sequence-recording.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: Sequence Recording Guide -id: sequence-recording ---- - -Use `createHotkeySequenceRecorder` to capture a series of shortcut chords. By default, each step records its physical code: pressing G twice produces `['[KeyG]', '[KeyG]']`. Set `recordBy: 'key'` to follow logical characters instead. Pass the resulting array directly to sequence registration and format each step for display. - -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Allow the framework to run its effect or watcher before relying on the update. - -## Record and display a sequence - -This example uses a Save button so plain Enter can be recorded as a step: - -```svelte - - - -

    {displayedSteps.map((step) => formatForDisplay(step)).join(' → ')}

    -{#if recorder.isRecording} - - -{/if} -``` - -## State and controls - -The recorder exposes `isRecording`, `steps`, and `recordedSequence` as reactive getters. `steps` contains the current attempt; `recordedSequence` contains the last committed result. `startRecording()` begins a new session. `commitRecording()` saves a nonempty attempt, while `cancelRecording()` discards it and calls `onCancel`. `stopRecording()` resets recorder state without calling `onRecord` or `onCancel`. - -## Options and keyboard behavior - -- `recordBy`: `'code'` by default; `'key'` records produced logical characters. -- `commitKeys`: `'enter'` by default; plain Enter commits a nonempty sequence. `'none'` lets Enter become a step and requires manual or idle commit. -- `commitOnEnter: false`: also permits Enter as a step when `commitKeys` is `'enter'`. -- `idleTimeoutMs`: optionally commits after inactivity following a completed step. No timer runs before the first step. -- `ignoreInputs`: true by default, including shadow-root inputs. Set false to record from editable fields. Escape still cancels from an input. - -Escape cancels. Unmodified Backspace/Delete removes the last step; when already empty it stops and calls only `onClear`. Modifier-only presses, automatic repeats, and IME composition do not append steps. Recorded events and their releases do not trigger application shortcuts. - -Set provider defaults through `HotkeysProvider` with `defaultOptions.hotkeySequenceRecorder`. - -## Validation and conflicts - -`validate(sequence, { events, parsedSequence })` runs at commit. Return true to accept, or false/a message to reject. `detectConflicts` checks live bindings and sequence prefixes, including physical/logical overlap established by the recorded events. `onReject` receives feedback; rejected steps remain editable with Backspace. - -The shared options and exclusions are described in the [hotkey recording guide](./hotkey-recording.md#validation-and-conflicts). The application owns reset, persistence, and any binding being edited; clearing never calls `onRecord([])`. diff --git a/docs/framework/svelte/guides/sequences.md b/docs/framework/svelte/guides/sequences.md index 33c344f5..e5b993a1 100644 --- a/docs/framework/svelte/guides/sequences.md +++ b/docs/framework/svelte/guides/sequences.md @@ -3,162 +3,143 @@ title: Sequences Guide id: sequences --- -TanStack Hotkeys supports multi-key sequences in Svelte, where keys are pressed one after another rather than simultaneously. +A sequence is a hotkey made of chords pressed one after another rather than together. Think Vim-style navigation, cheat codes, or multi-step commands. Sequences are ordinary hotkeys: register them with `createHotkey` and `createHotkeys`, list them with `getHotkeyRegistrations`, record them with `createHotkeyRecorder`, and format them with `formatForDisplay`. -Sequence steps use the same string syntax as single hotkeys. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. Display steps with `sequence.map((step) => formatForDisplay(step)).join(' → ')`. +Sequence steps use the same syntax as chords. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. -## Reactive options +## Basic usage -Sequence options support the same [property getter patterns](./hotkeys.md#property-getters) as hotkey options, including `enabled`, `timeout`, and `target`. Updates preserve the registration when its sequence, target, and platform stay the same. - -## Global sequences +Pass an array of chords wherever you pass a hotkey: ```svelte ``` +The user must press the steps in order, each within the timeout window. A one-step array such as `['Escape']` is the chord `'Escape'`. + ## Many sequences at once -Use `createHotkeySequences` to register several global sequences in one place (including from a reactive getter). For multiple sequences on a focused element, use `createHotkeySequencesAttachment` the same way you would use `createHotkeySequenceAttachment`. +`createHotkeys` registers chords and sequences together, including dynamic lists: -```svelte - +createHotkeys([ + { hotkey: ['G', 'G'], callback: () => scrollToTop() }, + { hotkey: ['D', 'D'], callback: () => deleteLine(), options: { timeout: 500 } }, + { hotkey: 'Mod+S', callback: () => save() }, +]) ``` -## Scoped sequences +## Sequence options -Use `createHotkeySequenceAttachment` when a sequence should only be active while a specific element owns focus. +Sequences accept the same options as chords, except `requireReset`, plus `timeout`. -```svelte - +The maximum time (in milliseconds) allowed between consecutive steps. If the user takes longer than this between any two steps, the sequence resets. Defaults to `1000` (1 second). -
    - Focus here, then press g then g -
    +```ts +// Fast sequence - user must type quickly +createHotkey(['D', 'D'], () => deleteLine(), { timeout: 500 }) + +// Slow sequence - user has more time between keys +createHotkey(['Shift+Z', 'Shift+Z'], () => forceQuit(), { timeout: 2000 }) ``` -## Matching steps +`timeout` is only accepted when the hotkey is a sequence. Set a default for every sequence with `HotkeysProvider` (or `setHotkeysContext` in a root component's script): + +```svelte + + + +``` -Both `SequenceManager` and `createSequenceMatcher` ignore modifier-only events, IME composition, and automatic keydown repeats. These events neither advance the sequence nor refresh its timeout: holding G does not complete a two-press G sequence. The manager prefers exact matches over weaker logical-key fallbacks while preserving equally strong matches. +### `enabled` and `meta` -## Sequence options +These work exactly as they do for chords. Disabled sequences stay registered and visible in devtools, and `meta` flows to registrations, devtools, and cheat sheets. See the [Hotkeys Guide](./hotkeys). ```ts -createHotkeySequence(['G', 'G'], callback, { - timeout: 1000, - enabled: true, -}) +createHotkey(['G', 'G'], () => scrollToTop(), () => ({ + enabled: isVimMode, + meta: { name: 'Go to top', description: 'Scroll to the top of the page' }, +})) ``` -### Reactive `enabled` +## Sequences with modifiers -When disabled, the sequence stays registered (visible in devtools); only execution is suppressed. +Each step in a sequence can include modifiers, and consecutive steps can repeat the same modifier: -```svelte - +// Physical R then T positions, both with Shift +createHotkey(['Shift+[KeyR]', 'Shift+[KeyT]'], () => doNextAction()) ``` -## Default options +Brackets retain physical positions even when the keys produce different letters. While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored. They neither advance the sequence nor reset progress, so a user can tap or hold Shift between `Shift+R` and `Shift+T`. -```svelte - +```ts +createHotkeys([ + { hotkey: ['G', 'G'], callback: () => scrollToTop() }, + { hotkey: ['G', 'Shift+G'], callback: () => scrollToBottom() }, + { hotkey: ['D', 'D'], callback: () => deleteLine() }, + { hotkey: ['D', 'W'], callback: () => deleteWord() }, + { hotkey: ['C', 'I', 'W'], callback: () => changeInnerWord() }, +]) ``` -### `meta` +## Displaying sequences -Sequences support the same `meta` option as hotkeys, so you can attach a `name` and `description` for use in shortcut palettes and devtools. +`formatForDisplay` formats each step and joins them with `sequenceSeparator`, a space by default: ```ts -createHotkeySequence(['G', 'G'], () => scrollToTop(), { - meta: { name: 'Go to Top', description: 'Scroll to the top of the page' }, -}) +formatForDisplay(['Mod+K', 'Mod+C'], { platform: 'mac' }) // '⌘ K ⌘ C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' then ' }) // 'G then G' ``` -See the [Hotkeys Guide](./hotkeys.md#metadata-name--description) for details on declaration merging and introspecting registrations. - -## Chained modifier chords - -This example follows physical R and T positions. Brackets retain those positions even when the keys produce different letters. Other sequences can continue using logical characters. +Registration views report each sequence's progress in `matchedStepCount`, so a help panel can show how far the user has typed. See the [Hotkeys Guide](./hotkeys). -You can use the same modifier on consecutive steps (for example `Shift+R` then `Shift+T`): - -```ts -createHotkeySequence(['Shift+[KeyR]', 'Shift+[KeyT]'], () => doNextAction()) -``` +## How sequences work -While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored: they do not advance the sequence and do not reset progress. +The `HotkeyManager` tracks progress for every sequence registration. When a key is pressed: -## Common patterns +1. It checks whether the key matches the next expected step of each sequence +2. If it matches, that sequence advances +3. If the timeout expires between steps, the sequence resets +4. When all steps are completed, the callback fires -### Vim-style navigation +Modifier-only events, IME composition, and automatic keydown repeats neither advance a sequence nor refresh its timeout: holding G does not complete a two-press G sequence. Exact matches take priority over weaker logical-key fallbacks. -```ts -createHotkeySequence(['G', 'G'], () => scrollToTop()) -createHotkeySequence(['G', 'Shift+G'], () => scrollToBottom()) -createHotkeySequence(['D', 'D'], () => deleteLine()) -createHotkeySequence(['D', 'W'], () => deleteWord()) -createHotkeySequence(['C', 'I', 'W'], () => changeInnerWord()) -``` +Multiple sequences can share a prefix, such as `['D', 'D']`, `['D', 'W']`, and `['D', 'I', 'W']`. Progress is tracked for each sequence independently, so after pressing `D` the manager waits for the next key to determine which sequence completes. -### Konami Code +### Conflicting registrations -```ts -createHotkeySequence( - ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'B', 'A'], - () => enableEasterEgg(), - { timeout: 2000 }, -) -``` +For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate hotkeys. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection and app hotkey conflicts are stricter: there, a sequence also conflicts with a chord equal to its first step and with any sequence it starts. -## Under the hood +## Standalone matching -`createHotkeySequence` uses the singleton `SequenceManager`. You can also access it directly: +For sequence matching without the singleton manager, use `createSequenceMatcher`: ```ts -import { - createSequenceMatcher, - getSequenceManager, -} from '@tanstack/svelte-hotkeys' +import { createSequenceMatcher } from '@tanstack/svelte-hotkeys' -const manager = getSequenceManager() const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 }) -``` - -### Conflicting registrations -For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate bindings. `conflictBehavior` applies to equivalent sequences without rewriting their stored strings. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection also checks prefixes and observed physical/logical overlap. +document.addEventListener('keydown', (e) => { + if (matcher.match(e)) { + console.log('Sequence completed!') + } + console.log('Progress:', matcher.getProgress()) // e.g., 1 +}) +``` diff --git a/docs/framework/svelte/quick-start.md b/docs/framework/svelte/quick-start.md index 2ef2c748..066280a9 100644 --- a/docs/framework/svelte/quick-start.md +++ b/docs/framework/svelte/quick-start.md @@ -79,9 +79,10 @@ The `Mod` modifier automatically resolves to `Meta` (Command) on macOS and `Cont ```svelte @@ -155,18 +156,21 @@ Use `setHotkeysContext` when you want defaults for a subtree. This is an advance import { setHotkeysContext } from '@tanstack/svelte-hotkeys' setHotkeysContext({ - hotkey: { preventDefault: true }, - hotkeySequence: { timeout: 1500 }, + defaultOptions: { hotkey: { preventDefault: true, timeout: 1500 } }, }) ``` +## Declaring app hotkeys + +To list every hotkey in a cheat sheet, including hotkeys whose components haven't mounted, and to let users customize them, declare them once with `defineAppHotkeys` and provide them with `HotkeysProvider` or `setHotkeysContext`. See the [App Hotkeys Guide](./guides/app-hotkeys). + ## Next steps - [Hotkeys Guide](./guides/hotkeys) - [Sequences Guide](./guides/sequences) - [Hotkey Recording Guide](./guides/hotkey-recording) -- [Sequence Recording Guide](./guides/sequence-recording) +- [App Hotkeys Guide](./guides/app-hotkeys) - [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 index 025eba12..aa155b36 100644 --- a/docs/framework/svelte/reference/functions/createHotkey.md +++ b/docs/framework/svelte/reference/functions/createHotkey.md @@ -3,43 +3,112 @@ id: createHotkey title: createHotkey --- +## Call Signature + ```ts -function createHotkey( +function createHotkey( hotkey, callback, options?): void; ``` -Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:56](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L56) +Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:62](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L62) + +Svelte function for registering a hotkey on the document: a chord such as +`'Mod+S'`, a sequence such as `['G', 'G']`, or an app hotkey from +`defineAppHotkeys`. Pass getters for reactive values. Use +`createHotkeyAttachment` for element-scoped hotkeys. + +Chords accept `requireReset`; sequences accept `timeout`. A one-step +sequence registers as its chord. An app hotkey uses its effective hotkey +from `HotkeysProvider` (including the user's custom hotkey) and its +declared meta and options, and registers nothing while unbound. + +### Type Parameters + +#### T -Register a global hotkey for the current component. +`T` *extends* `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\> -## Parameters +### Parameters -### hotkey +#### hotkey -`MaybeGetter`\<`RegisterableHotkey`\> +`MaybeGetter`\<`T`\> -### callback +#### callback `HotkeyCallback` -### options? +#### options? -`MaybeGetter`\<[`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md)\> = `{}` +`MaybeGetter`\<[`CreateAppHotkeyOptions`](../type-aliases/CreateAppHotkeyOptions.md)\<`T`\>\> -## Returns +### Returns `void` -## Example +### Example ```svelte +``` + +## Call Signature + +```ts +function createHotkey( + hotkey, + callback, + options?): void; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:67](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L67) + +Svelte function for registering a hotkey on the document: a chord such as +`'Mod+S'`, a sequence such as `['G', 'G']`, or an app hotkey from +`defineAppHotkeys`. Pass getters for reactive values. Use +`createHotkeyAttachment` for element-scoped hotkeys. + +Chords accept `requireReset`; sequences accept `timeout`. A one-step +sequence registers as its chord. An app hotkey uses its effective hotkey +from `HotkeysProvider` (including the user's custom hotkey) and its +declared meta and options, and registers nothing while unbound. - createHotkey('Mod+S', () => { - console.log('Mod+S pressed') - }) +### Type Parameters + +#### T + +`T` *extends* `RegisterableHotkey` + +### Parameters + +#### hotkey + +`MaybeGetter`\<`T`\> + +#### callback + +`HotkeyCallback` + +#### options? + +`MaybeGetter`\<`AdapterOptionsFor`\<`T`, [`CreateHotkeyTarget`](../type-aliases/CreateHotkeyTarget.md)\>\> + +### Returns + +`void` + +### Example + +```svelte + ``` diff --git a/docs/framework/svelte/reference/functions/createHotkeyAttachment.md b/docs/framework/svelte/reference/functions/createHotkeyAttachment.md index fa0775cf..1cc82d33 100644 --- a/docs/framework/svelte/reference/functions/createHotkeyAttachment.md +++ b/docs/framework/svelte/reference/functions/createHotkeyAttachment.md @@ -3,49 +3,98 @@ id: createHotkeyAttachment title: createHotkeyAttachment --- +## Call Signature + ```ts -function createHotkeyAttachment( +function createHotkeyAttachment( hotkey, callback, options?): Attachment; ``` -Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:130](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L130) +Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:93](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L93) + +Create an attachment for an element-scoped hotkey: a chord, a sequence, or +an app hotkey that listens on the attached element. + +### Type Parameters + +#### T -Create an attachment for element-scoped hotkeys. +`T` *extends* `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\> -## Parameters +### Parameters -### hotkey +#### hotkey -`MaybeGetter`\<`RegisterableHotkey`\> +`MaybeGetter`\<`T`\> -### callback +#### callback `HotkeyCallback` -### options? +#### options? -`MaybeGetter`\<[`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md)\> = `{}` +`MaybeGetter`\<[`CreateAppHotkeyOptions`](../type-aliases/CreateAppHotkeyOptions.md)\<`T`\>\> -## Returns +### Returns `Attachment`\<`HTMLElement`\> -## Example +### Example ```svelte + +
    Editor content
    +``` + +## Call Signature + +```ts +function createHotkeyAttachment( + hotkey, + callback, +options?): Attachment; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:98](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L98) + +Create an attachment for an element-scoped hotkey: a chord, a sequence, or +an app hotkey that listens on the attached element. - let count = $state(0) - const saveHotkey = createHotkeyAttachment('Mod+S', () => { - count++ - }) +### Type Parameters +#### T + +`T` *extends* `RegisterableHotkey` + +### Parameters + +#### hotkey + +`MaybeGetter`\<`T`\> + +#### callback + +`HotkeyCallback` + +#### options? + +`MaybeGetter`\<`AdapterOptionsFor`\<`T`, [`CreateHotkeyTarget`](../type-aliases/CreateHotkeyTarget.md)\>\> + +### Returns + +`Attachment`\<`HTMLElement`\> + +### Example + +```svelte + -
    - Count: {count} -
    +
    Editor content
    ``` diff --git a/docs/framework/svelte/reference/functions/createHotkeyRecorder.md b/docs/framework/svelte/reference/functions/createHotkeyRecorder.md index 265d7e7a..70c2fa31 100644 --- a/docs/framework/svelte/reference/functions/createHotkeyRecorder.md +++ b/docs/framework/svelte/reference/functions/createHotkeyRecorder.md @@ -3,18 +3,192 @@ id: createHotkeyRecorder title: createHotkeyRecorder --- +## Call Signature + +```ts +function createHotkeyRecorder(options): SvelteHotkeyRecorder; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:102](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L102) + +Svelte function for recording keyboard shortcuts. Options may be a getter. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### options + +`HotkeyChordRecorderOptions` + +### Returns + +[`SvelteHotkeyRecorder`](../interfaces/SvelteHotkeyRecorder.md)\<`HotkeyChord`\> + +### Example + +```svelte + + + +``` + +## Call Signature + +```ts +function createHotkeyRecorder(options): SvelteHotkeyRecorder; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:105](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L105) + +Svelte function for recording keyboard shortcuts. Options may be a getter. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### options + +`HotkeySequenceRecorderOptions` + +### Returns + +[`SvelteHotkeyRecorder`](../interfaces/SvelteHotkeyRecorder.md) + +### Example + +```svelte + + + +``` + +## Call Signature + ```ts function createHotkeyRecorder(options): SvelteHotkeyRecorder; ``` -Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:101](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L101) +Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:110](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L110) + +Svelte function for recording keyboard shortcuts. Options may be a getter. -## Parameters +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. -### options +### Parameters -`MaybeGetter`\<`HotkeyRecorderOptions`\> +#### options -## Returns +() => `HotkeySequenceRecorderOptions` + +### Returns [`SvelteHotkeyRecorder`](../interfaces/SvelteHotkeyRecorder.md) + +### Example + +```svelte + + + +``` + +## Call Signature + +```ts +function createHotkeyRecorder(options): SvelteHotkeyRecorder; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:113](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L113) + +Svelte function for recording keyboard shortcuts. Options may be a getter. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### options + +() => `HotkeyChordRecorderOptions` + +### Returns + +[`SvelteHotkeyRecorder`](../interfaces/SvelteHotkeyRecorder.md)\<`HotkeyChord`\> + +### Example + +```svelte + + + +``` + +## Call Signature + +```ts +function createHotkeyRecorder(options): SvelteHotkeyRecorder; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:116](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L116) + +Svelte function for recording keyboard shortcuts. Options may be a getter. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### options + +() => `HotkeyRecorderOptions` + +### Returns + +[`SvelteHotkeyRecorder`](../interfaces/SvelteHotkeyRecorder.md) + +### Example + +```svelte + + + +``` diff --git a/docs/framework/svelte/reference/functions/createHotkeySequence.md b/docs/framework/svelte/reference/functions/createHotkeySequence.md deleted file mode 100644 index 7cca3164..00000000 --- a/docs/framework/svelte/reference/functions/createHotkeySequence.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -id: createHotkeySequence -title: createHotkeySequence ---- - -```ts -function createHotkeySequence( - sequence, - callback, - options?): void; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts:72](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts#L72) - -Register a global keyboard shortcut sequence for the current component. - -Each step may include modifiers. You can chain the same modifier across -steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift, -Control, Alt, or Meta pressed alone) are ignored while matching—they do not -advance the sequence or reset progress. - -## 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 deleted file mode 100644 index 63d9819b..00000000 --- a/docs/framework/svelte/reference/functions/createHotkeySequenceAttachment.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -id: createHotkeySequenceAttachment -title: createHotkeySequenceAttachment ---- - -```ts -function createHotkeySequenceAttachment( - sequence, - callback, -options?): Attachment; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts:151](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts#L151) - -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/createHotkeySequenceRecorder.md b/docs/framework/svelte/reference/functions/createHotkeySequenceRecorder.md deleted file mode 100644 index 263480aa..00000000 --- a/docs/framework/svelte/reference/functions/createHotkeySequenceRecorder.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: createHotkeySequenceRecorder -title: createHotkeySequenceRecorder ---- - -```ts -function createHotkeySequenceRecorder(options): SvelteHotkeySequenceRecorder; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts:77](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts#L77) - -Svelte helper for recording multi-chord sequences (Vim-style shortcuts). - -## Parameters - -### options - -`MaybeGetter`\<`HotkeySequenceRecorderOptions`\> - -## Returns - -[`SvelteHotkeySequenceRecorder`](../interfaces/SvelteHotkeySequenceRecorder.md) diff --git a/docs/framework/svelte/reference/functions/createHotkeySequences.md b/docs/framework/svelte/reference/functions/createHotkeySequences.md deleted file mode 100644 index b2a03bf0..00000000 --- a/docs/framework/svelte/reference/functions/createHotkeySequences.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -id: createHotkeySequences -title: createHotkeySequences ---- - -```ts -function createHotkeySequences(definitions, commonOptions?): void; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequences.svelte.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequences.svelte.ts#L59) - -Register multiple global keyboard shortcut sequences for the current component. - -## Parameters - -### definitions - -`MaybeGetter`\<[`CreateHotkeySequenceDefinition`](../interfaces/CreateHotkeySequenceDefinition.md)[]\> - -### commonOptions? - -`MaybeGetter`\<[`CreateHotkeySequenceOptions`](../interfaces/CreateHotkeySequenceOptions.md)\> = `{}` - -## Returns - -`void` - -## Example - -```svelte - -``` diff --git a/docs/framework/svelte/reference/functions/createHotkeySequencesAttachment.md b/docs/framework/svelte/reference/functions/createHotkeySequencesAttachment.md deleted file mode 100644 index 633414b1..00000000 --- a/docs/framework/svelte/reference/functions/createHotkeySequencesAttachment.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -id: createHotkeySequencesAttachment -title: createHotkeySequencesAttachment ---- - -```ts -function createHotkeySequencesAttachment(definitions, commonOptions?): Attachment; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequences.svelte.ts:183](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequences.svelte.ts#L183) - -Create an attachment for element-scoped multi-sequence registration. - -## Parameters - -### definitions - -`MaybeGetter`\<[`CreateHotkeySequenceDefinition`](../interfaces/CreateHotkeySequenceDefinition.md)[]\> - -### commonOptions? - -`MaybeGetter`\<[`CreateHotkeySequenceOptions`](../interfaces/CreateHotkeySequenceOptions.md)\> = `{}` - -## Returns - -`Attachment`\<`HTMLElement`\> - -## Example - -```svelte - - -
    Editor
    -``` diff --git a/docs/framework/svelte/reference/functions/createHotkeys.md b/docs/framework/svelte/reference/functions/createHotkeys.md index 2fef9b8c..5d309363 100644 --- a/docs/framework/svelte/reference/functions/createHotkeys.md +++ b/docs/framework/svelte/reference/functions/createHotkeys.md @@ -7,19 +7,25 @@ title: createHotkeys function createHotkeys(hotkeys, commonOptions?): void; ``` -Defined in: [packages/svelte-hotkeys/src/createHotkeys.svelte.ts:65](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeys.svelte.ts#L65) +Defined in: [packages/svelte-hotkeys/src/createHotkeys.svelte.ts:128](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeys.svelte.ts#L128) -Register multiple global hotkeys for the current component. +Svelte function for registering several hotkeys at once on the document: +chords, sequences, and app hotkeys. Pass getters for reactive values. Use +`createHotkeysAttachment` for element-scoped hotkeys. + +Options merge in this order: provider defaults, app hotkey declaration, +commonOptions, per-hotkey options. Each option applies only to the kinds +that support it. Unbound app hotkeys and empty sequences register nothing. ## Parameters ### hotkeys -`MaybeGetter`\<[`CreateHotkeyDefinition`](../interfaces/CreateHotkeyDefinition.md)[]\> +`MaybeGetter`\ ### commonOptions? -`MaybeGetter`\<[`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md)\> = `{}` +`MaybeGetter`\<[`CreateHotkeyCommonOptions`](../type-aliases/CreateHotkeyCommonOptions.md)\> = `{}` ## Returns @@ -29,12 +35,9 @@ Register multiple global hotkeys for the current component. ```svelte ``` diff --git a/docs/framework/svelte/reference/functions/createHotkeysAttachment.md b/docs/framework/svelte/reference/functions/createHotkeysAttachment.md index b90e5943..9be741b2 100644 --- a/docs/framework/svelte/reference/functions/createHotkeysAttachment.md +++ b/docs/framework/svelte/reference/functions/createHotkeysAttachment.md @@ -7,19 +7,20 @@ title: createHotkeysAttachment function createHotkeysAttachment(hotkeys, commonOptions?): Attachment; ``` -Defined in: [packages/svelte-hotkeys/src/createHotkeys.svelte.ts:188](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeys.svelte.ts#L188) +Defined in: [packages/svelte-hotkeys/src/createHotkeys.svelte.ts:152](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeys.svelte.ts#L152) -Create an attachment for element-scoped multi-hotkey registration. +Create an attachment for element-scoped hotkeys: chords, sequences, and app +hotkeys listen on the attached element. ## Parameters ### hotkeys -`MaybeGetter`\<[`CreateHotkeyDefinition`](../interfaces/CreateHotkeyDefinition.md)[]\> +`MaybeGetter`\ ### commonOptions? -`MaybeGetter`\<[`CreateHotkeyOptions`](../interfaces/CreateHotkeyOptions.md)\> = `{}` +`MaybeGetter`\<[`CreateHotkeyCommonOptions`](../type-aliases/CreateHotkeyCommonOptions.md)\> = `{}` ## Returns @@ -29,15 +30,11 @@ Create an attachment for element-scoped multi-hotkey registration. ```svelte -
    - Editor content -
    +
    Editor content
    ``` diff --git a/docs/framework/svelte/reference/functions/getAppHotkeyList.md b/docs/framework/svelte/reference/functions/getAppHotkeyList.md new file mode 100644 index 00000000..030325f2 --- /dev/null +++ b/docs/framework/svelte/reference/functions/getAppHotkeyList.md @@ -0,0 +1,18 @@ +--- +id: getAppHotkeyList +title: getAppHotkeyList +--- + +```ts +function getAppHotkeyList(): SvelteAppHotkeyList; +``` + +Defined in: packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:63 + +Lists every app hotkey with its live status, for cheat sheets and settings +screens. App hotkeys whose components have not mounted are `'inactive'`; +those without a hotkey are `'unbound'`. + +## Returns + +[`SvelteAppHotkeyList`](../interfaces/SvelteAppHotkeyList.md) diff --git a/docs/framework/svelte/reference/functions/getAppHotkeys.md b/docs/framework/svelte/reference/functions/getAppHotkeys.md new file mode 100644 index 00000000..146d6275 --- /dev/null +++ b/docs/framework/svelte/reference/functions/getAppHotkeys.md @@ -0,0 +1,26 @@ +--- +id: getAppHotkeys +title: getAppHotkeys +--- + +```ts +function getAppHotkeys(): SvelteAppHotkeys; +``` + +Defined in: packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:41 + +Returns reactive access to the effective app hotkeys from `HotkeysProvider`, +typed by `Register`. + +## Returns + +[`SvelteAppHotkeys`](../interfaces/SvelteAppHotkeys.md) + +## Example + +```svelte + +``` diff --git a/docs/framework/svelte/reference/functions/getCustomHotkeys.md b/docs/framework/svelte/reference/functions/getCustomHotkeys.md new file mode 100644 index 00000000..a27543e6 --- /dev/null +++ b/docs/framework/svelte/reference/functions/getCustomHotkeys.md @@ -0,0 +1,18 @@ +--- +id: getCustomHotkeys +title: getCustomHotkeys +--- + +```ts +function getCustomHotkeys(): SvelteCustomHotkeys; +``` + +Defined in: packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:101 + +Reads and changes the user's custom hotkeys. Changes apply to every +registered app hotkey immediately and are passed to the provider's +`onCustomHotkeysChange`, ready to save. + +## Returns + +[`SvelteCustomHotkeys`](../interfaces/SvelteCustomHotkeys.md) diff --git a/docs/framework/svelte/reference/functions/getDefaultHotkeysOptions.md b/docs/framework/svelte/reference/functions/getDefaultHotkeysOptions.md index 6faf5355..f09464c0 100644 --- a/docs/framework/svelte/reference/functions/getDefaultHotkeysOptions.md +++ b/docs/framework/svelte/reference/functions/getDefaultHotkeysOptions.md @@ -7,7 +7,7 @@ title: getDefaultHotkeysOptions function getDefaultHotkeysOptions(): HotkeysProviderOptions; ``` -Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L59) +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:110](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L110) ## Returns diff --git a/docs/framework/svelte/reference/functions/getHotkeyHint.md b/docs/framework/svelte/reference/functions/getHotkeyHint.md index 835014ac..99f9eda9 100644 --- a/docs/framework/svelte/reference/functions/getHotkeyHint.md +++ b/docs/framework/svelte/reference/functions/getHotkeyHint.md @@ -7,19 +7,21 @@ title: getHotkeyHint function getHotkeyHint(hotkey, options?): SvelteHotkeyHint; ``` -Defined in: [packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts#L18) +Defined in: [packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts#L28) -Returns an object with a reactive `visible` getter indicating whether held modifiers reveal this shortcut. -Pass getters to track changing bindings and options. -Uses the nonempty-subset/AltGr rules of `matchesHeldModifiers`; `exact` requires -every modifier. Combine with the action's enabled state before showing a badge. -This helper does not register a shortcut or check whether its target is focused. +Returns an object with a reactive `visible` getter indicating whether held +modifiers reveal this shortcut. Pass getters to track changing hotkeys and +options. A sequence reveals its hint from its first step, and an app hotkey +uses its effective hotkey (false while unbound). This helper does not +register a shortcut or check whether its target is focused. ## Parameters ### hotkey -`MaybeGetter`\<`RegisterableHotkey`\> +`MaybeGetter`\< + \| `RegisterableHotkey` + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\>\> ### options? diff --git a/docs/framework/svelte/reference/functions/getHotkeyRegistrations.md b/docs/framework/svelte/reference/functions/getHotkeyRegistrations.md index 892d2a43..6879ded4 100644 --- a/docs/framework/svelte/reference/functions/getHotkeyRegistrations.md +++ b/docs/framework/svelte/reference/functions/getHotkeyRegistrations.md @@ -7,28 +7,25 @@ title: getHotkeyRegistrations function getHotkeyRegistrations(): SvelteHotkeyRegistrations; ``` -Defined in: [packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts:58](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts#L58) +Defined in: [packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts:39](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts#L39) -Svelte function that returns reactive access to all hotkey and sequence -registrations from the singleton managers. +Svelte function that returns reactive access to every hotkey registration, +chords and sequences, from the singleton HotkeyManager. -This is a standalone function that does NOT require the HotkeysProvider. +This function does NOT require the HotkeysProvider. ## Returns [`SvelteHotkeyRegistrations`](../interfaces/SvelteHotkeyRegistrations.md) -Object with reactive `hotkeys` and `sequences` properties - ## Example ```svelte - -{#each registrations.hotkeys as reg} -
    {reg.options.meta?.name} — {reg.triggerCount} triggers
    + +{#each registrations.hotkeys as reg (reg.id)} +
  • {reg.options.meta?.name}: {formatForDisplay(reg.hotkey)}
  • {/each} ``` diff --git a/docs/framework/svelte/reference/functions/getHotkeysContext.md b/docs/framework/svelte/reference/functions/getHotkeysContext.md index 9ded3519..09acdd27 100644 --- a/docs/framework/svelte/reference/functions/getHotkeysContext.md +++ b/docs/framework/svelte/reference/functions/getHotkeysContext.md @@ -7,7 +7,7 @@ title: getHotkeysContext function getHotkeysContext(): HotkeysContextValue | null; ``` -Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L51) +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:102](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L102) ## Returns diff --git a/docs/framework/svelte/reference/functions/setHotkeysContext.md b/docs/framework/svelte/reference/functions/setHotkeysContext.md index e45cd9fb..983424e8 100644 --- a/docs/framework/svelte/reference/functions/setHotkeysContext.md +++ b/docs/framework/svelte/reference/functions/setHotkeysContext.md @@ -4,17 +4,33 @@ title: setHotkeysContext --- ```ts -function setHotkeysContext(defaultOptions?): HotkeysContextValue; +function setHotkeysContext(options?): HotkeysContextValue; ``` -Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L31) +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:74](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L74) + +Sets the hotkeys context for descendants. `HotkeysProvider` calls this; call +it directly in a root component's script instead of rendering the provider. ## Parameters -### defaultOptions? +### options? -[`HotkeysProviderOptions`](../interfaces/HotkeysProviderOptions.md) = `DEFAULT_OPTIONS` +[`HotkeysContextOptions`](../interfaces/HotkeysContextOptions.md) = `{}` ## Returns `HotkeysContextValue` + +## Example + +```svelte + +``` diff --git a/docs/framework/svelte/reference/index.md b/docs/framework/svelte/reference/index.md index a3277bff..e5ca3fc1 100644 --- a/docs/framework/svelte/reference/index.md +++ b/docs/framework/svelte/reference/index.md @@ -5,22 +5,29 @@ title: "@tanstack/svelte-hotkeys" ## Interfaces -- [CreateHotkeyDefinition](interfaces/CreateHotkeyDefinition.md) -- [CreateHotkeyOptions](interfaces/CreateHotkeyOptions.md) -- [CreateHotkeySequenceDefinition](interfaces/CreateHotkeySequenceDefinition.md) -- [CreateHotkeySequenceOptions](interfaces/CreateHotkeySequenceOptions.md) +- [HotkeysContextOptions](interfaces/HotkeysContextOptions.md) - [HotkeysProviderOptions](interfaces/HotkeysProviderOptions.md) - [HotkeysProviderProps](interfaces/HotkeysProviderProps.md) +- [SvelteAppHotkeyList](interfaces/SvelteAppHotkeyList.md) +- [SvelteAppHotkeys](interfaces/SvelteAppHotkeys.md) +- [SvelteCustomHotkeys](interfaces/SvelteCustomHotkeys.md) - [SvelteHeldKeyCodesMap](interfaces/SvelteHeldKeyCodesMap.md) - [SvelteHeldKeys](interfaces/SvelteHeldKeys.md) - [SvelteHeldKeyState](interfaces/SvelteHeldKeyState.md) - [SvelteHotkeyHint](interfaces/SvelteHotkeyHint.md) - [SvelteHotkeyRecorder](interfaces/SvelteHotkeyRecorder.md) - [SvelteHotkeyRegistrations](interfaces/SvelteHotkeyRegistrations.md) -- [SvelteHotkeySequenceRecorder](interfaces/SvelteHotkeySequenceRecorder.md) ## Type Aliases +- [CreateAppHotkeyOptions](type-aliases/CreateAppHotkeyOptions.md) +- [CreateHotkeyChordOptions](type-aliases/CreateHotkeyChordOptions.md) +- [CreateHotkeyCommonOptions](type-aliases/CreateHotkeyCommonOptions.md) +- [CreateHotkeyDefinition](type-aliases/CreateHotkeyDefinition.md) +- [CreateHotkeyOptions](type-aliases/CreateHotkeyOptions.md) +- [CreateHotkeyOptionsFor](type-aliases/CreateHotkeyOptionsFor.md) +- [CreateHotkeySequenceOptions](type-aliases/CreateHotkeySequenceOptions.md) +- [CreateHotkeyTarget](type-aliases/CreateHotkeyTarget.md) - [HotkeysProvider](type-aliases/HotkeysProvider.md) ## Variables @@ -35,11 +42,9 @@ title: "@tanstack/svelte-hotkeys" - [createHotkeyRecorder](functions/createHotkeyRecorder.md) - [createHotkeys](functions/createHotkeys.md) - [createHotkeysAttachment](functions/createHotkeysAttachment.md) -- [createHotkeySequence](functions/createHotkeySequence.md) -- [createHotkeySequenceAttachment](functions/createHotkeySequenceAttachment.md) -- [createHotkeySequenceRecorder](functions/createHotkeySequenceRecorder.md) -- [createHotkeySequences](functions/createHotkeySequences.md) -- [createHotkeySequencesAttachment](functions/createHotkeySequencesAttachment.md) +- [getAppHotkeyList](functions/getAppHotkeyList.md) +- [getAppHotkeys](functions/getAppHotkeys.md) +- [getCustomHotkeys](functions/getCustomHotkeys.md) - [getDefaultHotkeysOptions](functions/getDefaultHotkeysOptions.md) - [getHeldKeyCodesMap](functions/getHeldKeyCodesMap.md) - [getHeldKeys](functions/getHeldKeys.md) diff --git a/docs/framework/svelte/reference/interfaces/CreateHotkeyDefinition.md b/docs/framework/svelte/reference/interfaces/CreateHotkeyDefinition.md deleted file mode 100644 index b1717f48..00000000 --- a/docs/framework/svelte/reference/interfaces/CreateHotkeyDefinition.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: CreateHotkeyDefinition -title: CreateHotkeyDefinition ---- - -Defined in: [packages/svelte-hotkeys/src/createHotkeys.svelte.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeys.svelte.ts#L23) - -A single hotkey definition for use with `createHotkeys`. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeys.svelte.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeys.svelte.ts#L27) - -The function to call when the hotkey is pressed - -*** - -### hotkey - -```ts -hotkey: MaybeGetter; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeys.svelte.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeys.svelte.ts#L25) - -The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object - -*** - -### options? - -```ts -optional options?: MaybeGetter; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeys.svelte.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeys.svelte.ts#L29) - -Per-hotkey options (merged on top of commonOptions) diff --git a/docs/framework/svelte/reference/interfaces/CreateHotkeyOptions.md b/docs/framework/svelte/reference/interfaces/CreateHotkeyOptions.md deleted file mode 100644 index cdf7a6f6..00000000 --- a/docs/framework/svelte/reference/interfaces/CreateHotkeyOptions.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: CreateHotkeyOptions -title: CreateHotkeyOptions ---- - -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) - -## Extends - -- `Omit`\<`HotkeyOptions`, `"target"`\> - -## Properties - -### target? - -```ts -optional target?: Document | Window; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L20) diff --git a/docs/framework/svelte/reference/interfaces/CreateHotkeySequenceDefinition.md b/docs/framework/svelte/reference/interfaces/CreateHotkeySequenceDefinition.md deleted file mode 100644 index 067cd87b..00000000 --- a/docs/framework/svelte/reference/interfaces/CreateHotkeySequenceDefinition.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: CreateHotkeySequenceDefinition -title: CreateHotkeySequenceDefinition ---- - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequences.svelte.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequences.svelte.ts#L18) - -A single sequence definition for use with `createHotkeySequences`. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequences.svelte.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequences.svelte.ts#L22) - -The function to call when the sequence is completed - -*** - -### options? - -```ts -optional options?: MaybeGetter; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequences.svelte.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequences.svelte.ts#L24) - -Per-sequence options (merged on top of commonOptions) - -*** - -### sequence - -```ts -sequence: MaybeGetter; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequences.svelte.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequences.svelte.ts#L20) - -Array of hotkey strings that form the sequence diff --git a/docs/framework/svelte/reference/interfaces/CreateHotkeySequenceOptions.md b/docs/framework/svelte/reference/interfaces/CreateHotkeySequenceOptions.md deleted file mode 100644 index 38795f01..00000000 --- a/docs/framework/svelte/reference/interfaces/CreateHotkeySequenceOptions.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: CreateHotkeySequenceOptions -title: CreateHotkeySequenceOptions ---- - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts#L14) - -## Extends - -- `Omit`\<`SequenceOptions`, `"target"`\> - -## Properties - -### target? - -```ts -optional target?: Document | Window; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequence.svelte.ts#L18) diff --git a/docs/framework/svelte/reference/interfaces/HotkeysContextOptions.md b/docs/framework/svelte/reference/interfaces/HotkeysContextOptions.md new file mode 100644 index 00000000..1fdb2d10 --- /dev/null +++ b/docs/framework/svelte/reference/interfaces/HotkeysContextOptions.md @@ -0,0 +1,72 @@ +--- +id: HotkeysContextOptions +title: HotkeysContextOptions +--- + +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L28) + +Options for `setHotkeysContext` and props for `HotkeysProvider`. + +## Extended by + +- [`HotkeysProviderProps`](HotkeysProviderProps.md) + +## Properties + +### appHotkeys? + +```ts +optional appHotkeys?: Readonly>>; +``` + +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:34](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L34) + +Every app hotkey, from `defineAppHotkeys`. Read when the context is set; +listed by `getAppHotkeyList` whether or not they are registered. + +*** + +### customHotkeys? + +```ts +optional customHotkeys?: + | CustomHotkeys>>> + | null; +``` + +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:39](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L39) + +Starting custom hotkeys, such as saved data. Read once; the context owns +the state afterwards. Invalid entries are ignored with a warning. + +*** + +### defaultOptions? + +```ts +optional defaultOptions?: HotkeysProviderOptions; +``` + +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L29) + +*** + +### onCustomHotkeysChange? + +```ts +optional onCustomHotkeysChange?: (customHotkeys) => void; +``` + +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:41](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L41) + +Called with the custom hotkeys after every change, ready to save. + +#### Parameters + +##### customHotkeys + +`CustomHotkeys` + +#### Returns + +`void` diff --git a/docs/framework/svelte/reference/interfaces/HotkeysProviderOptions.md b/docs/framework/svelte/reference/interfaces/HotkeysProviderOptions.md index 46d15c5d..a9e3fc27 100644 --- a/docs/framework/svelte/reference/interfaces/HotkeysProviderOptions.md +++ b/docs/framework/svelte/reference/interfaces/HotkeysProviderOptions.md @@ -3,44 +3,29 @@ id: HotkeysProviderOptions title: HotkeysProviderOptions --- -Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L10) +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L12) ## Properties ### hotkey? ```ts -optional hotkey?: Partial; +optional hotkey?: CreateHotkeyCommonOptions; ``` -Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L11) - -*** - -### hotkeyRecorder? - -```ts -optional hotkeyRecorder?: Partial; -``` +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L17) -Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L12) +Defaults for every hotkey registered by `createHotkey` and `createHotkeys`. +`requireReset` applies to chords and `timeout` applies to sequences. *** -### hotkeySequence? +### hotkeyRecorder? ```ts -optional hotkeySequence?: Partial; +optional hotkeyRecorder?: Partial>; ``` -Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L14) - -*** - -### hotkeySequenceRecorder? - -```ts -optional hotkeySequenceRecorder?: Partial; -``` +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L19) -Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L13) +Defaults for every `createHotkeyRecorder`. diff --git a/docs/framework/svelte/reference/interfaces/HotkeysProviderProps.md b/docs/framework/svelte/reference/interfaces/HotkeysProviderProps.md index 45f0218d..d2ad38f0 100644 --- a/docs/framework/svelte/reference/interfaces/HotkeysProviderProps.md +++ b/docs/framework/svelte/reference/interfaces/HotkeysProviderProps.md @@ -3,17 +3,59 @@ id: HotkeysProviderProps title: HotkeysProviderProps --- -Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L17) +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:44](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L44) + +Options for `setHotkeysContext` and props for `HotkeysProvider`. + +## Extends + +- [`HotkeysContextOptions`](HotkeysContextOptions.md) ## Properties +### appHotkeys? + +```ts +optional appHotkeys?: Readonly>>; +``` + +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:34](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L34) + +Every app hotkey, from `defineAppHotkeys`. Read when the context is set; +listed by `getAppHotkeyList` whether or not they are registered. + +#### Inherited from + +[`HotkeysContextOptions`](HotkeysContextOptions.md).[`appHotkeys`](HotkeysContextOptions.md#apphotkeys) + +*** + ### children ```ts children: Snippet; ``` -Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L18) +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:45](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L45) + +*** + +### customHotkeys? + +```ts +optional customHotkeys?: + | CustomHotkeys>>> + | null; +``` + +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:39](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L39) + +Starting custom hotkeys, such as saved data. Read once; the context owns +the state afterwards. Invalid entries are ignored with a warning. + +#### Inherited from + +[`HotkeysContextOptions`](HotkeysContextOptions.md).[`customHotkeys`](HotkeysContextOptions.md#customhotkeys) *** @@ -23,4 +65,34 @@ Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:18](https://github.com/Ta optional defaultOptions?: HotkeysProviderOptions; ``` -Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L19) +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L29) + +#### Inherited from + +[`HotkeysContextOptions`](HotkeysContextOptions.md).[`defaultOptions`](HotkeysContextOptions.md#defaultoptions) + +*** + +### onCustomHotkeysChange? + +```ts +optional onCustomHotkeysChange?: (customHotkeys) => void; +``` + +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:41](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L41) + +Called with the custom hotkeys after every change, ready to save. + +#### Parameters + +##### customHotkeys + +`CustomHotkeys` + +#### Returns + +`void` + +#### Inherited from + +[`HotkeysContextOptions`](HotkeysContextOptions.md).[`onCustomHotkeysChange`](HotkeysContextOptions.md#oncustomhotkeyschange) diff --git a/docs/framework/svelte/reference/interfaces/SvelteAppHotkeyList.md b/docs/framework/svelte/reference/interfaces/SvelteAppHotkeyList.md new file mode 100644 index 00000000..fc99a7f1 --- /dev/null +++ b/docs/framework/svelte/reference/interfaces/SvelteAppHotkeyList.md @@ -0,0 +1,18 @@ +--- +id: SvelteAppHotkeyList +title: SvelteAppHotkeyList +--- + +Defined in: packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:51 + +## Properties + +### current + +```ts +readonly current: AppHotkeyListItem>[]; +``` + +Defined in: packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:53 + +Every app hotkey with its live status, in declaration order. diff --git a/docs/framework/svelte/reference/interfaces/SvelteAppHotkeys.md b/docs/framework/svelte/reference/interfaces/SvelteAppHotkeys.md new file mode 100644 index 00000000..d0fd73ef --- /dev/null +++ b/docs/framework/svelte/reference/interfaces/SvelteAppHotkeys.md @@ -0,0 +1,18 @@ +--- +id: SvelteAppHotkeys +title: SvelteAppHotkeys +--- + +Defined in: packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:24 + +## Properties + +### current + +```ts +readonly current: RegisteredAppHotkeys; +``` + +Defined in: packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:26 + +Effective app hotkeys: each declaration with the user's custom hotkey applied. diff --git a/docs/framework/svelte/reference/interfaces/SvelteCustomHotkeys.md b/docs/framework/svelte/reference/interfaces/SvelteCustomHotkeys.md new file mode 100644 index 00000000..69f742a9 --- /dev/null +++ b/docs/framework/svelte/reference/interfaces/SvelteCustomHotkeys.md @@ -0,0 +1,109 @@ +--- +id: SvelteCustomHotkeys +title: SvelteCustomHotkeys +--- + +Defined in: packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:74 + +## Properties + +### current + +```ts +readonly current: CustomHotkeys; +``` + +Defined in: packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:76 + +The user's hotkeys that differ from the defaults. + +*** + +### findConflicts + +```ts +findConflicts: (id, hotkey) => AppHotkey[]; +``` + +Defined in: packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:90 + +Other app hotkeys that would collide if `id` used `hotkey`. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] + +*** + +### resetCustomHotkey + +```ts +resetCustomHotkey: (id) => void; +``` + +Defined in: packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:86 + +Restores the default hotkey for an app hotkey. + +#### Parameters + +##### id + +`string` + +#### Returns + +`void` + +*** + +### resetCustomHotkeys + +```ts +resetCustomHotkeys: () => void; +``` + +Defined in: packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:88 + +Restores every default hotkey. + +#### Returns + +`void` + +*** + +### setCustomHotkey + +```ts +setCustomHotkey: (id, hotkey) => AppHotkey[]; +``` + +Defined in: packages/svelte-hotkeys/src/getAppHotkeys.svelte.ts:81 + +Sets the user's hotkey for an app hotkey. `null` unbinds it. +Returns conflicting app hotkeys; when any exist, nothing changes. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` \| `null` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] diff --git a/docs/framework/svelte/reference/interfaces/SvelteHotkeyHint.md b/docs/framework/svelte/reference/interfaces/SvelteHotkeyHint.md index 7e2e0447..376c27a6 100644 --- a/docs/framework/svelte/reference/interfaces/SvelteHotkeyHint.md +++ b/docs/framework/svelte/reference/interfaces/SvelteHotkeyHint.md @@ -3,7 +3,7 @@ id: SvelteHotkeyHint title: SvelteHotkeyHint --- -Defined in: [packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts:7](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts#L7) +Defined in: [packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts#L17) ## Properties @@ -13,4 +13,4 @@ Defined in: [packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts:7](https://gith readonly visible: boolean; ``` -Defined in: [packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts:8](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts#L8) +Defined in: [packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyHint.svelte.ts#L18) diff --git a/docs/framework/svelte/reference/interfaces/SvelteHotkeyRecorder.md b/docs/framework/svelte/reference/interfaces/SvelteHotkeyRecorder.md index 2d734c1c..e4cca7c6 100644 --- a/docs/framework/svelte/reference/interfaces/SvelteHotkeyRecorder.md +++ b/docs/framework/svelte/reference/interfaces/SvelteHotkeyRecorder.md @@ -3,7 +3,13 @@ id: SvelteHotkeyRecorder title: 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) +Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L15) + +## Type Parameters + +### THotkey + +`THotkey` *extends* `Hotkey` = `Hotkey` ## Properties @@ -13,9 +19,25 @@ Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:8](https 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) +Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L27) -Cancel recording without saving +Cancel recording and call onCancel + +#### Returns + +`void` + +*** + +### commitRecording + +```ts +commitRecording: () => void; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L29) + +Commit the recorded steps now (multi-step recordings) #### Returns @@ -29,7 +51,7 @@ Cancel recording without saving 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) +Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L17) Whether recording is currently active @@ -38,12 +60,12 @@ Whether recording is currently active ### recordedHotkey ```ts -readonly recordedHotkey: Hotkey | null; +readonly recordedHotkey: THotkey | 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) +Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L21) -The currently recorded hotkey (for live preview) +The last committed hotkey *** @@ -53,7 +75,7 @@ The currently recorded hotkey (for live preview) 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) +Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L23) Start recording a new hotkey @@ -63,15 +85,27 @@ Start recording a new hotkey *** +### steps + +```ts +readonly steps: HotkeySequence; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L19) + +Chords recorded so far in a multi-step recording (for live preview) + +*** + ### 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) +Defined in: [packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeyRecorder.svelte.ts#L25) -Stop recording (same as cancel) +Stop recording without calling onCancel #### Returns diff --git a/docs/framework/svelte/reference/interfaces/SvelteHotkeyRegistrations.md b/docs/framework/svelte/reference/interfaces/SvelteHotkeyRegistrations.md index a1485bb6..620a9bc9 100644 --- a/docs/framework/svelte/reference/interfaces/SvelteHotkeyRegistrations.md +++ b/docs/framework/svelte/reference/interfaces/SvelteHotkeyRegistrations.md @@ -3,7 +3,7 @@ id: SvelteHotkeyRegistrations title: SvelteHotkeyRegistrations --- -Defined in: [packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts#L12) +Defined in: [packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts:5](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts#L5) ## Properties @@ -13,14 +13,6 @@ Defined in: [packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts:12](ht readonly hotkeys: HotkeyRegistrationView[]; ``` -Defined in: [packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts#L13) +Defined in: [packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts:7](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts#L7) -*** - -### sequences - -```ts -readonly sequences: SequenceRegistrationView[]; -``` - -Defined in: [packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/getHotkeyRegistrations.svelte.ts#L14) +Every registration, chords and sequences. Narrow each view with `kind`. diff --git a/docs/framework/svelte/reference/interfaces/SvelteHotkeySequenceRecorder.md b/docs/framework/svelte/reference/interfaces/SvelteHotkeySequenceRecorder.md deleted file mode 100644 index 821788f0..00000000 --- a/docs/framework/svelte/reference/interfaces/SvelteHotkeySequenceRecorder.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -id: SvelteHotkeySequenceRecorder -title: SvelteHotkeySequenceRecorder ---- - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts#L11) - -## Properties - -### cancelRecording - -```ts -cancelRecording: () => void; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts#L17) - -#### Returns - -`void` - -*** - -### commitRecording - -```ts -commitRecording: () => void; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts#L18) - -#### Returns - -`void` - -*** - -### isRecording - -```ts -readonly isRecording: boolean; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts#L12) - -*** - -### recordedSequence - -```ts -readonly recordedSequence: HotkeySequence | null; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts#L14) - -*** - -### startRecording - -```ts -startRecording: () => void; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts#L15) - -#### Returns - -`void` - -*** - -### steps - -```ts -readonly steps: HotkeySequence; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts#L13) - -*** - -### stopRecording - -```ts -stopRecording: () => void; -``` - -Defined in: [packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeySequenceRecorder.svelte.ts#L16) - -#### Returns - -`void` diff --git a/docs/framework/svelte/reference/type-aliases/CreateAppHotkeyOptions.md b/docs/framework/svelte/reference/type-aliases/CreateAppHotkeyOptions.md new file mode 100644 index 00000000..34538ddd --- /dev/null +++ b/docs/framework/svelte/reference/type-aliases/CreateAppHotkeyOptions.md @@ -0,0 +1,18 @@ +--- +id: CreateAppHotkeyOptions +title: CreateAppHotkeyOptions +--- + +```ts +type CreateAppHotkeyOptions = AdapterAppHotkeyOptions; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:39](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L39) + +Call-site options for an app hotkey, typed by its declared default kind. + +## Type Parameters + +### T + +`T` *extends* `AppHotkey` = `AppHotkey` diff --git a/docs/framework/svelte/reference/type-aliases/CreateHotkeyChordOptions.md b/docs/framework/svelte/reference/type-aliases/CreateHotkeyChordOptions.md new file mode 100644 index 00000000..bef6972d --- /dev/null +++ b/docs/framework/svelte/reference/type-aliases/CreateHotkeyChordOptions.md @@ -0,0 +1,12 @@ +--- +id: CreateHotkeyChordOptions +title: CreateHotkeyChordOptions +--- + +```ts +type CreateHotkeyChordOptions = AdapterChordOptions; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L25) + +Options for a chord such as `'Mod+S'`. diff --git a/docs/framework/svelte/reference/type-aliases/CreateHotkeyCommonOptions.md b/docs/framework/svelte/reference/type-aliases/CreateHotkeyCommonOptions.md new file mode 100644 index 00000000..e88bf32f --- /dev/null +++ b/docs/framework/svelte/reference/type-aliases/CreateHotkeyCommonOptions.md @@ -0,0 +1,12 @@ +--- +id: CreateHotkeyCommonOptions +title: CreateHotkeyCommonOptions +--- + +```ts +type CreateHotkeyCommonOptions = AdapterCommonOptions; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L32) + +Options applied to every hotkey, such as provider defaults. Each applies to the kinds that support it. diff --git a/docs/framework/svelte/reference/type-aliases/CreateHotkeyDefinition.md b/docs/framework/svelte/reference/type-aliases/CreateHotkeyDefinition.md new file mode 100644 index 00000000..7c47acff --- /dev/null +++ b/docs/framework/svelte/reference/type-aliases/CreateHotkeyDefinition.md @@ -0,0 +1,166 @@ +--- +id: CreateHotkeyDefinition +title: CreateHotkeyDefinition +--- + +```ts +type CreateHotkeyDefinition = + | { + callback: HotkeyCallback; + hotkey: MaybeGetter; + options?: MaybeGetter; +} + | { + callback: HotkeyCallback; + hotkey: MaybeGetter; + options?: MaybeGetter; +} + | { + callback: HotkeyCallback; + hotkey: MaybeGetter; + options?: MaybeGetter; +} + | { + callback: HotkeyCallback; + hotkey: MaybeGetter; + options?: MaybeGetter; +}; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkeys.svelte.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkeys.svelte.ts#L35) + +One hotkey for `createHotkeys`. The hotkey and options may be getters. +Chords accept `requireReset`; sequences accept `timeout`; hotkeys of unknown +kind accept only shared options. + +## Union Members + +### Type Literal + +```ts +{ + callback: HotkeyCallback; + hotkey: MaybeGetter; + options?: MaybeGetter; +} +``` + +#### callback + +```ts +callback: HotkeyCallback; +``` + +The function to call when the hotkey is pressed + +#### hotkey + +```ts +hotkey: MaybeGetter; +``` + +A chord string such as `'Mod+S'`, or a RawHotkey object + +#### options? + +```ts +optional options?: MaybeGetter; +``` + +Per-hotkey options (merged on top of commonOptions) + +*** + +### Type Literal + +```ts +{ + callback: HotkeyCallback; + hotkey: MaybeGetter; + options?: MaybeGetter; +} +``` + +#### callback + +```ts +callback: HotkeyCallback; +``` + +#### hotkey + +```ts +hotkey: MaybeGetter; +``` + +A sequence such as `['G', 'G']` + +#### options? + +```ts +optional options?: MaybeGetter; +``` + +*** + +### Type Literal + +```ts +{ + callback: HotkeyCallback; + hotkey: MaybeGetter; + options?: MaybeGetter; +} +``` + +#### callback + +```ts +callback: HotkeyCallback; +``` + +#### hotkey + +```ts +hotkey: MaybeGetter; +``` + +An app hotkey from `defineAppHotkeys` + +#### options? + +```ts +optional options?: MaybeGetter; +``` + +*** + +### Type Literal + +```ts +{ + callback: HotkeyCallback; + hotkey: MaybeGetter; + options?: MaybeGetter; +} +``` + +#### callback + +```ts +callback: HotkeyCallback; +``` + +#### hotkey + +```ts +hotkey: MaybeGetter; +``` + +A hotkey whose kind is not known statically. Accepts only shared options. + +#### options? + +```ts +optional options?: MaybeGetter; +``` diff --git a/docs/framework/svelte/reference/type-aliases/CreateHotkeyOptions.md b/docs/framework/svelte/reference/type-aliases/CreateHotkeyOptions.md new file mode 100644 index 00000000..e61e715c --- /dev/null +++ b/docs/framework/svelte/reference/type-aliases/CreateHotkeyOptions.md @@ -0,0 +1,12 @@ +--- +id: CreateHotkeyOptions +title: CreateHotkeyOptions +--- + +```ts +type CreateHotkeyOptions = AdapterHotkeyOptions; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L22) + +Options shared by chords and sequences. diff --git a/docs/framework/svelte/reference/type-aliases/CreateHotkeyOptionsFor.md b/docs/framework/svelte/reference/type-aliases/CreateHotkeyOptionsFor.md new file mode 100644 index 00000000..0e388d8a --- /dev/null +++ b/docs/framework/svelte/reference/type-aliases/CreateHotkeyOptionsFor.md @@ -0,0 +1,18 @@ +--- +id: CreateHotkeyOptionsFor +title: CreateHotkeyOptionsFor +--- + +```ts +type CreateHotkeyOptionsFor = AdapterOptionsFor; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L35) + +The options type for a hotkey: chord options for chords, sequence options for sequences. + +## Type Parameters + +### T + +`T` *extends* `RegisterableHotkey` diff --git a/docs/framework/svelte/reference/type-aliases/CreateHotkeySequenceOptions.md b/docs/framework/svelte/reference/type-aliases/CreateHotkeySequenceOptions.md new file mode 100644 index 00000000..5db8a0e4 --- /dev/null +++ b/docs/framework/svelte/reference/type-aliases/CreateHotkeySequenceOptions.md @@ -0,0 +1,12 @@ +--- +id: CreateHotkeySequenceOptions +title: CreateHotkeySequenceOptions +--- + +```ts +type CreateHotkeySequenceOptions = AdapterSequenceOptions; +``` + +Defined in: [packages/svelte-hotkeys/src/createHotkey.svelte.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/createHotkey.svelte.ts#L28) + +Options for a sequence such as `['G', 'G']`. diff --git a/docs/framework/svelte/reference/type-aliases/CreateHotkeyTarget.md b/docs/framework/svelte/reference/type-aliases/CreateHotkeyTarget.md new file mode 100644 index 00000000..e01c4a71 --- /dev/null +++ b/docs/framework/svelte/reference/type-aliases/CreateHotkeyTarget.md @@ -0,0 +1,12 @@ +--- +id: CreateHotkeyTarget +title: CreateHotkeyTarget +--- + +```ts +type CreateHotkeyTarget = 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) + +The document or window to listen on. Use an attachment for elements. diff --git a/docs/framework/svelte/reference/variables/DEFAULT_OPTIONS.md b/docs/framework/svelte/reference/variables/DEFAULT_OPTIONS.md index 55ea1d73..b4ca4b88 100644 --- a/docs/framework/svelte/reference/variables/DEFAULT_OPTIONS.md +++ b/docs/framework/svelte/reference/variables/DEFAULT_OPTIONS.md @@ -7,4 +7,4 @@ title: DEFAULT_OPTIONS const DEFAULT_OPTIONS: HotkeysProviderOptions = {}; ``` -Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L22) +Defined in: [packages/svelte-hotkeys/src/HotkeysCtx.ts:48](https://github.com/TanStack/hotkeys/blob/main/packages/svelte-hotkeys/src/HotkeysCtx.ts#L48) diff --git a/docs/framework/vue/guides/app-hotkeys.md b/docs/framework/vue/guides/app-hotkeys.md new file mode 100644 index 00000000..f2fa03b5 --- /dev/null +++ b/docs/framework/vue/guides/app-hotkeys.md @@ -0,0 +1,201 @@ +--- +title: App Hotkeys Guide +id: app-hotkeys +--- + +`useHotkey` and `useHotkeys` register a hotkey while its component is mounted. A cheat sheet built from live registrations therefore misses hotkeys on routes that have not rendered yet, and a settings screen has nowhere to change them. App hotkeys solve both: you declare every hotkey in one place, pass the declarations to `HotkeysProvider` (or `provideHotkeysContext` in a root component's setup), and attach callbacks where the work happens. Users can then customize any of them, and your app decides how to save their choices. + +A declaration holds a hotkey's id, default hotkey, metadata, and options. It never holds a callback; `useHotkey` and `useHotkeys` still attach the callbacks. + +## Declare app hotkeys + +Use `defineAppHotkeys` in a module that contains only declarations. Keep it free of components and handlers, because it loads with your app shell: + +```ts +// app-hotkeys.ts +import { defineAppHotkeys } from '@tanstack/vue-hotkeys' + +export const appHotkeys = defineAppHotkeys({ + 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save', group: 'Document' } }, + 'doc.find': { + hotkey: 'Mod+F', + meta: { name: 'Find', group: 'Document' }, + context: 'editor', + }, + 'nav.home': { + hotkey: ['G', 'H'], + meta: { name: 'Go home', group: 'Navigation' }, + options: { timeout: 800 }, + }, + 'palette.open': { hotkey: null, meta: { name: 'Command palette' } }, +}) + +declare module '@tanstack/vue-hotkeys' { + interface Register { + appHotkeys: typeof appHotkeys + } +} +``` + +- The object key is the app hotkey's id. Treat ids as a stable contract, because saved custom hotkeys use them as keys. +- `hotkey` is the default: a chord, a sequence, or `null` for an action without a default. An unbound app hotkey registers nothing until the user assigns one. +- `options` describe the hotkey itself: `conflictBehavior`, `eventType`, `ignoreInputs`, `preventDefault`, `stopPropagation`, plus `requireReset` for a chord default or `timeout` for a sequence default. Options that don't match the default's kind are type errors. Pass `target` and `enabled` where you register the hotkey. +- `context` labels where a hotkey applies, such as `'editor'`. App hotkeys with different contexts may share a hotkey. It is used only for conflict checks and does not affect matching. + +The `Register` augmentation types the app hotkeys you read back, the ids accepted when changing custom hotkeys, and the `CustomHotkeys` type. + +## Provide app hotkeys + +```vue + + + +``` + +## Register app hotkeys + +Pass an app hotkey wherever you pass a hotkey: + +```ts +import { useAppHotkeys, useHotkey, useHotkeys } from '@tanstack/vue-hotkeys' + +const hotkeys = useAppHotkeys() + +useHotkey(() => hotkeys.value['doc.save'], () => save(), () => ({ + enabled: isDirty.value, +})) +useHotkeys(() => [ + { hotkey: hotkeys.value['nav.home'], callback: () => router.push('/') }, + { hotkey: 'Escape', callback: close }, +]) +``` + +`useAppHotkeys()` returns a computed ref. You can also import an app hotkey directly, such as `useHotkey(appHotkeys['doc.save'], save)`. Without provided app hotkeys, the declared default is used. + +The registration takes `meta` and `options` from the declaration. Options merge in this order: default options, then declared options, then call-site options. `meta` comes only from the declaration, so the cheat sheet and the registration cannot disagree. Call-site options are typed by the declared default's kind, so `timeout` is accepted for `nav.home` but not for `doc.save`. + +When the user changes a hotkey, registered app hotkeys move to the new hotkey immediately, even if it changes from a chord to a sequence. + +## Build a cheat sheet + +`useAppHotkeyList` returns every app hotkey in declaration order, with its live status: + +| Status | Meaning | +| --- | --- | +| `'active'` | At least one enabled registration | +| `'disabled'` | Registered, but every registration has `enabled: false` | +| `'inactive'` | Has a hotkey, but nothing has registered it | +| `'unbound'` | No hotkey assigned | + +```vue + + + +``` + +An app hotkey registered by many components, such as one per list row, appears once. Its `registrations` array holds each live registration. Devtools also list declared app hotkeys that are not registered. + +## Customize and persist hotkeys + +TanStack Hotkeys does not store anything. Pass the user's saved custom hotkeys in and save them when they change: + +```vue + + + +``` + +The provider emits `customHotkeysChange` with the complete custom hotkeys after every change. With `provideHotkeysContext`, pass `customHotkeys` and `onCustomHotkeysChange` in its options instead. + +- `customHotkeys` holds only the hotkeys that differ from the declared defaults, keyed by id. `null` means the user unbound that hotkey. Saving differences means users who never changed a hotkey get new defaults when you change them. +- `customHotkeys` is read once; afterwards the shared store owns the state. Saved data can outlive the code that wrote it, so unknown ids and invalid hotkeys are ignored with a warning. +- `onCustomHotkeysChange` receives the complete custom hotkeys after every change, ready to save. Storage can be anything: local storage, a server, or a user profile. + +Recorded hotkeys use `Mod` rather than platform-specific modifiers, so a hotkey saved on one platform works on others. + +### Change hotkeys + +`useCustomHotkeys` reads and changes the user's custom hotkeys: + +```vue + + + +``` + +| Member | Description | +| --- | --- | +| `customHotkeys.value` | The hotkeys that differ from the defaults | +| `setCustomHotkey(id, hotkey)` | Sets a hotkey, or unbinds with `null`. Returns conflicting app hotkeys; when there are any, nothing changes. | +| `findConflicts(id, hotkey)` | App hotkeys that would collide if `id` used `hotkey` | +| `resetCustomHotkey(id)` | Restores one default | +| `resetCustomHotkeys()` | Restores every default | + +Two app hotkeys conflict when their contexts match or either has no `context`, and their hotkeys collide: equal chords, or one sequence starts with the other, including a sequence whose first step equals a chord. Conflicts are checked across every declaration, whether or not it is registered. + +Each app hotkey's `defaultHotkey` holds its declared default, and each cheat sheet item's `isCustom` reports whether the effective hotkey differs from it. diff --git a/docs/framework/vue/guides/formatting-display.md b/docs/framework/vue/guides/formatting-display.md index 46c42dfc..f23ab316 100644 --- a/docs/framework/vue/guides/formatting-display.md +++ b/docs/framework/vue/guides/formatting-display.md @@ -3,7 +3,7 @@ title: Formatting & Display Guide id: formatting-display --- -Use `formatForDisplay` whenever a binding appears in a menu, button, hint, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, and parsed bindings. Store the original binding and format it at render time: the display label is not a registration string. +Use `formatForDisplay` whenever a hotkey appears in a menu, button, hint, cheat sheet, or shortcut settings panel. It accepts logical strings, bracketed physical strings, raw objects, parsed bindings, and sequences. Store the original binding and format it at render time: the display label is not a registration string. ## Format a binding @@ -32,16 +32,18 @@ const parts = formatForDisplay(binding, { platform: 'mac', parts: true }) With `parts` omitted or false, the result is a string. A runtime boolean returns `string | string[]`. Parts preserve literal plus keys and ignore `separatorToken`. -Sequences are arrays of bindings. Format their steps individually: +## Format sequences -```ts -import type { HotkeySequence } from '@tanstack/vue-hotkeys' +Pass a sequence to format each step and join the steps with `sequenceSeparator`, a space by default: -const sequence: HotkeySequence = ['Mod+[KeyK]', 'C'] -const label = sequence.map((step) => formatForDisplay(step)).join(' → ') +```ts +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac' }) // '⌘ K C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' → ' }) // 'G → G' +formatForDisplay(['Mod+[KeyK]', 'C'], { platform: 'mac', parts: true }) +// [['⌘', 'K'], ['C']] — one array of keycaps per step ``` -`formatHotkeySequence` only joins stored strings with spaces. It intentionally retains brackets and code names, so use the code above for user-facing labels. +With `parts: true`, a sequence returns one array per step, so you can render each step as a group of keycaps. ## Choose symbols and separators @@ -63,7 +65,7 @@ formatForDisplay('Control++', { }) // 'Ctrl · +' ``` -An empty separator joins labels directly. `undefined` or `null` uses the platform default. `formatWithLabels(binding, options)` is the shorthand for `formatForDisplay` with `useSymbols: false`. +An empty separator joins labels directly. `undefined` or `null` uses the platform default. `separatorToken` applies within a chord; `sequenceSeparator` applies between steps. ## Supply layout labels @@ -88,6 +90,10 @@ formatForDisplay('Mod+[KeyQ]', { The precedence is `keyLabels`, then a layout entry, then the fallback label. Layout entries receive normal letter casing and key symbols; missing or empty entries fall back. Explicit labels are final. All of these options affect display only. +## Compare hotkeys + +`areHotkeysEqual(a, b)` compares chords or sequences after resolving modifiers, so `Mod+S` equals `Meta+S` on macOS. Sequences compare step by step. `isHotkeySequence` narrows a `Hotkey` to a sequence, and `simplifyHotkey` turns a one-step sequence into its chord. + ## Parse and store bindings `parseHotkey` returns either a logical `key` or a physical `code`, plus resolved modifier flags. Narrow the union before inspecting the identity: @@ -105,6 +111,6 @@ formatForDisplay(stored, { platform: 'windows' }) // 'Ctrl+S' Parsed modifiers are already resolved. To display a portable `Mod` binding on another platform, serialize with the original platform first, as above. `normalizeRegisterableHotkey` accepts strings or raw objects and preserves the logical/physical distinction. Do not store display labels or put a bracketed code in a logical `key` field. -Use `validateHotkey` when accepting strings from an external source. It returns `valid`, `errors`, and `warnings`; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). +Use `validateHotkey` when accepting a chord or sequence from an external source. It returns `valid`, `errors`, and `warnings`, and names the failing step of a sequence; it does not guarantee that a browser or operating system will deliver the shortcut. Recorder validation and live conflict checks are covered in the [recording guide](./hotkey-recording.md#validation-and-conflicts). Try these options together in the [vanilla formatter playground](../../../framework/vanilla/examples/formatForDisplay). diff --git a/docs/framework/vue/guides/hotkey-recording.md b/docs/framework/vue/guides/hotkey-recording.md index 62a8b7e5..2493ab55 100644 --- a/docs/framework/vue/guides/hotkey-recording.md +++ b/docs/framework/vue/guides/hotkey-recording.md @@ -3,119 +3,138 @@ title: Hotkey Recording Guide id: hotkey-recording --- -TanStack Hotkeys provides the `useHotkeyRecorder` composable for building shortcut customization UIs in Vue. +`useHotkeyRecorder` is for building keyboard shortcut customization UIs. Users record their own shortcuts by pressing the key combination they want, the same way system preferences or IDE shortcut editors work. By default it records one chord; set `maxSteps` to let users record a [sequence](./sequences) too. Recorders default to physical codes: recording a shortcut stores a string such as `Mod+[KeyS]`. Pass it directly to your hotkey registration and use `formatForDisplay` for the label. Set `recordBy: 'key'` when you intentionally want the produced character instead. -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Allow the framework to run its effect or watcher before relying on the update. +TanStack Hotkeys automatically suppresses registered hotkey callbacks while any recorder is active. You do not need to disable hotkeys while recording. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. ## Basic usage ```vue ``` -## Return value +## Recorder state and actions + +| Member | Description | +| --- | --- | +| `isRecording` | Whether the recorder is currently listening for key presses | +| `steps` | Chords captured so far in a multi-step recording | +| `recordedHotkey` | The last recorded hotkey, or `null` if nothing recorded | +| `startRecording()` | Start listening for key presses | +| `stopRecording()` | Stop listening without calling `onRecord` or `onCancel` | +| `cancelRecording()` | Stop listening and call `onCancel` | +| `commitRecording()` | Commit the current `steps` now (multi-step recordings) | -- `isRecording`: reactive ref-like value indicating whether recording is active -- `recordedHotkey`: reactive ref-like value with the most recently recorded hotkey -- `startRecording()`: start listening for key presses -- `stopRecording()`: stop listening and reset recorder state without calling `onRecord` -- `cancelRecording()`: stop listening and discard the in-progress recording +State members are readonly refs. Destructure them at the top level of ` +### `onCancel` - -``` +Called when recording is cancelled, by pressing Escape or calling `cancelRecording()`. + +### `onClear` + +Called when the recording is cleared: Backspace or Delete with no modifiers and no recorded steps. Clearing calls only `onClear`; your app decides whether that removes a hotkey or restores a default. ### `ignoreInputs` -The `HotkeyRecorderOptions` supports an `ignoreInputs` option (defaults to `true`). When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Pressing Escape still cancels recording even when an input has focus. Set `ignoreInputs: false` if you want the recorder to capture keys from within input elements. +Defaults to `true`. The recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Escape still cancels recording even while an input is focused. Set `ignoreInputs: false` to record from inside input elements. + +Options can also be a ref or getter, such as `useHotkeyRecorder(() => ({ maxSteps: 3, recordBy: mode.value, onRecord }))`, so recording policy can follow reactive state. + +### Default options + +Set defaults for every recorder with `HotkeysProvider` (or `provideHotkeysContext` in a root component's setup). Per-recorder options override them. + +```vue + + + +``` + +## Recording sequences + +Set `maxSteps` above 1 to record up to that many chords. Each chord adds a step, and the recording commits when the user presses Enter, when it reaches `maxSteps`, or after `idleTimeoutMs` without a new step: ```ts -useHotkeyRecorder({ - ignoreInputs: false, // record even from inside inputs - onRecord: (hotkey) => console.log(hotkey), +const recorder = useHotkeyRecorder({ + maxSteps: 3, + onRecord: (hotkey) => console.log('Recorded:', hotkey), // 'Mod+[KeyS]' or ['[KeyG]', '[KeyG]'] }) + +// Live preview while recording +const label = computed(() => + recorder.isRecording.value + ? formatForDisplay(recorder.steps.value) || 'Press chords, then Enter…' + : formatForDisplay(recorder.recordedHotkey.value ?? []), +) ``` +A one-step recording commits as a chord, so the result can always be passed straight to your hotkey registration. Use `maxSteps: Infinity` for no limit. + +| Option | Description | +| --- | --- | +| `maxSteps` | Maximum chords to record. Reaching it commits. Default `1`. | +| `commitKeys` | `'enter'` (default) commits on plain Enter. With `'none'`, plain Enter can be recorded as a step, and only `commitRecording()`, `idleTimeoutMs`, or `maxSteps` commit. | +| `idleTimeoutMs` | Commit automatically after this many milliseconds without a new step. The timer does not run before the first chord. | + +`commitKeys` and `idleTimeoutMs` are only accepted when `maxSteps` is set. With `maxSteps` above 1, `onRecord` and `validate` receive `Hotkey` (a chord or a sequence) instead of a chord. + ## 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. +| Key | Behavior | +| --- | --- | +| Modifier only (Shift, Ctrl, etc.) | Waits for a non-modifier key; modifier-only presses don't complete a step | +| Modifier + key (e.g., Ctrl+S) | Records the full combination | +| Single key (e.g., Escape, F1) | Records the single key | +| Enter (multi-step, `commitKeys: 'enter'`) | Commits the recorded steps | +| Escape | Cancels the recording | +| Backspace / Delete | Removes the last step; with no steps, clears and calls `onClear` | + +Repeats and IME composition never add a step. Recorded hotkeys use the portable `Mod` format: Command+S on macOS records `Mod+[KeyS]` rather than `Meta+[KeyS]`, so the primary modifier becomes Control if the hotkey is later used on Windows. ## Validation and conflicts -Both recorder APIs accept `detectConflicts`, `validate`, and `onReject`. For a single-hotkey recorder, these options can be supplied alongside `onRecord`: +The recorder accepts `detectConflicts`, `validate`, and `onReject` alongside `onRecord`: ```ts const options = { detectConflicts: { - // Use registration IDs to exclude the binding being edited: + // Use registration IDs to exclude the hotkey being edited: excludeIds: [registrationId], target: document, eventType: 'keydown', @@ -129,8 +148,63 @@ const options = { } ``` -`validate` returns true to accept, or false/a message to reject. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the candidate when available, and conflicting registration views for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. +`validate(hotkey, context)` returns true to accept, or false/a message to reject. The context holds the last chord's `event` and `parsedHotkey`, plus `events` and `parsedSequence` for every step. `onReject` receives `reason` (`missing-code`, `alt-graph`, `invalid`, `validation`, or `conflict`), a message, the rejected `hotkey` when available, and conflicting registrations for conflict rejections. Validation runs before commit; rejection never falls back to another key identity. A rejected multi-step recording keeps its steps so the user can edit them with Backspace. + +`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a hotkey. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Chords and sequence prefixes are checked: a recorded chord conflicts with a sequence that starts with it, and a recorded sequence conflicts with a registered chord equal to its first step. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute. + +For checks outside recording, use `findHotkeyConflicts(hotkey, options)`. Without source `events`, it compares hotkey identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. + +## Building a shortcut settings UI + +For an app-wide settings screen, declare the hotkeys with [app hotkeys](./app-hotkeys) and save the recorder's result with `setCustomHotkey`. That keeps cheat sheets, conflicts, and persistence in one place. + +For a single component, ordinary state is enough: + +```vue + + + +``` -`detectConflicts: true` checks enabled live registrations with the intended event type (default keydown) and overlapping targets (default document). Document/nested scopes can conflict; disjoint widgets can reuse a binding. The options object also supports `scope: 'all'`, `includeDisabled`, and an `exclude(registration)` predicate. Both single bindings and sequence prefixes are checked. Source events detect physical/logical overlap on the recorded layout. This is a conservative collision check, not a promise that two callbacks will execute: propagation, input filtering, match priority, external listeners, unmounted routes, and other layouts can affect actual dispatch. +## Under the hood -For checks outside recording, use `findHotkeyConflicts(bindingOrSequence, options)`. Without source `events`, it compares binding identity and sequence prefixes; it does not guess equivalence between a logical character and a physical position. +`useHotkeyRecorder` creates a `HotkeyRecorder` from `@tanstack/hotkeys`, exposes its state as refs, and destroys it when the effect scope is disposed. diff --git a/docs/framework/vue/guides/hotkeys.md b/docs/framework/vue/guides/hotkeys.md index 87e178c9..b60854af 100644 --- a/docs/framework/vue/guides/hotkeys.md +++ b/docs/framework/vue/guides/hotkeys.md @@ -5,6 +5,18 @@ id: hotkeys The `useHotkey` composable is the primary way to register keyboard shortcuts in Vue applications. It wraps the singleton `HotkeyManager` with automatic cleanup, support for template refs, and reactive option syncing. +## Chords and sequences + +A hotkey is either a **chord**, keys pressed together such as `'Mod+S'`, or a **sequence**, chords pressed one after another such as `['G', 'G']`. Every API in this guide accepts both, and a one-step array such as `['Mod+S']` is the chord `'Mod+S'`. + +```ts +useHotkey('Mod+S', () => save()) // chord +useHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 }) // sequence +useHotkey('Escape', () => close(), { requireReset: true }) // chord-only option +``` + +Most options apply to both kinds. `requireReset` applies only to chords and `timeout` only to sequences, and TypeScript rejects options that don't match the hotkey. When the kind isn't known statically, such as a `Hotkey` loaded from settings, only the shared options are accepted. Defaults apply only to the kinds that support them. See the [Sequences Guide](./sequences) for timing and matching, and the [App Hotkeys Guide](./app-hotkeys) to declare hotkeys up front and let users customize them. + ## Logical keys and physical positions Use a logical binding when the shortcut should follow the character on the active layout. Use a physical binding when it should follow a keyboard position: @@ -252,39 +264,31 @@ Group is descriptive metadata, not an execution scope. A shortcuts panel can gro ## Introspecting registrations -Use the `useHotkeyRegistrations` composable to get a live view of all hotkey and sequence registrations. It works well for shortcut palettes, help dialogs, and devtools. +Use the `useHotkeyRegistrations` composable to get a live view of every registration, chords and sequences together. It's the building block for shortcut palettes and help dialogs. ```vue ``` -The returned `hotkeys` array contains registration objects with the hotkey string, options (including `meta`), and enabled state. The `sequences` array contains sequence registrations with the same structure. +The returned ref holds one array of registration views. Each view has an `id`, the `hotkey`, its `options` (including `meta`), its `target`, and a `triggerCount`. Narrow with `kind`: chord views add `parsedHotkey`; sequence views add `parsedSequence` and `matchedStepCount`, the steps matched so far in the current attempt. Disabled entries stay listed; unmounted registrations disappear. Format any hotkey with `formatForDisplay`, and group entries by `options.meta?.group` when building a help panel. + +Registrations only exist while their components are mounted. To list hotkeys from routes that have not rendered, declare them with [app hotkeys](./app-hotkeys). ## The hotkey manager diff --git a/docs/framework/vue/guides/key-state-tracking.md b/docs/framework/vue/guides/key-state-tracking.md index 8dd69fe3..3f2c44fe 100644 --- a/docs/framework/vue/guides/key-state-tracking.md +++ b/docs/framework/vue/guides/key-state-tracking.md @@ -105,6 +105,6 @@ tracker.isKeyHeld('Shift') const visible = useHotkeyHint(() => binding.value) // computed ref: visible.value ``` -For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. +For `Alt+Shift+[KeyK]`, holding Alt, Shift, or both reveals the hint. An extra Control hides it; releasing all modifiers or blurring the window hides it. Nonmodifier keys are ignored. AltGraph does not reveal hints. Pass `{ exact: true }` to require all binding modifiers, or `{ platform: 'mac' }` to resolve Mod explicitly. Supply the same platform used by the registration when overriding detection. A sequence reveals its hint from its first step, and an app hotkey uses its effective hotkey (no hint while unbound). Combine the boolean with the action's enabled state. The helper does not register a shortcut or determine whether its target is focused. The core equivalent is `matchesHeldModifiers(binding, heldKeys, options)`. diff --git a/docs/framework/vue/guides/sequence-recording.md b/docs/framework/vue/guides/sequence-recording.md deleted file mode 100644 index 5815ebfa..00000000 --- a/docs/framework/vue/guides/sequence-recording.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: Sequence Recording Guide -id: sequence-recording ---- - -TanStack Hotkeys provides the `useHotkeySequenceRecorder` composable for building UIs where users record multi-chord sequences (Vim-style shortcuts). The recorder captures each step like a single hotkey chord. Users finish with Enter by default, or you can use manual commit and an optional idle timeout. - -Sequence recording uses `recordBy: 'code'` by default, preserving every step as a physical string such as `['[KeyG]', 'Alt+[KeyS]']`. Set `recordBy: 'key'` for logical characters. The shared rejection and conflict options follow the [hotkey recording guide](./hotkey-recording.md#validation-and-conflicts). - -TanStack Hotkeys automatically suppresses registered hotkey and sequence callbacks while any recorder is active. You do not need to set `enabled` from `isRecording`. Registrations remain available for conflict detection, and recorded keys stay suppressed through repeats and key release. - -## Reactive options - -Recorder options support [property getters and functions returning options](./hotkeys.md#property-getters). Updated callbacks, validation, and recording settings apply during an active session without restarting it. Allow the framework to run its effect or watcher before relying on the update. - -## Basic usage - -```vue - - - -``` - -## Return value - -| Property | Type | Description | -|----------|------|-------------| -| `isRecording` | `boolean` | Whether the recorder is listening | -| `steps` | `HotkeySequence` | Chords captured in the current session | -| `recordedSequence` | `HotkeySequence \| null` | Last committed sequence | -| `startRecording` | `() => void` | Start a new session | -| `stopRecording` | `() => void` | Stop without calling `onRecord` | -| `cancelRecording` | `() => void` | Stop and call `onCancel` | -| `commitRecording` | `() => void` | Commit current `steps` (no-op if empty) | - -## Options - -`recordBy` defaults to `'code'`; choose `'key'` for logical characters. Repeats and IME composition never append steps. Modifier-only presses wait for a complete chord. - -Core options live on `HotkeySequenceRecorderOptions` from `@tanstack/hotkeys`: - -- `onRecord(sequence)`: called when a nonempty sequence is committed. Clearing calls only `onClear`. -- `onCancel`, `onClear`: same intent as the hotkey recorder. -- `commitKeys`: `'enter'` (default) or `'none'`. With `'none'`, only `commitRecording()` (or `idleTimeoutMs`) finishes recording; plain Enter can be recorded as a chord. -- `commitOnEnter`: when `commitKeys` is `'enter'`, set to `false` to treat Enter as a normal chord (then use `commitRecording()` or idle timeout to finish). -- `idleTimeoutMs`: optional milliseconds of inactivity after the last completed chord to auto-commit. The timer does not run while waiting for the first chord. - -### Provider defaults - -```vue - - - -``` - -### `ignoreInputs` - -The `HotkeySequenceRecorderOptions` supports an `ignoreInputs` option (defaults to `true`). When `true`, the recorder doesn't intercept normal typing in text inputs, textareas, selects, or contentEditable elements; keystrokes pass through to the input as usual. Pressing Escape still cancels recording even when an input has focus. Set `ignoreInputs: false` if you want the recorder to capture keys from within input elements. - -## Validation and conflicts - -Sequence `validate(sequence, { events, parsedSequence })` runs when committing and returns true, false, or a rejection message. `detectConflicts` checks sequence prefixes as well as single bindings. Rejected commits keep recording active with the steps intact so the user can edit them with Backspace. Empty Backspace/Delete clears and emits `onClear` only; removing a nonempty step does not commit. Recorded steps, commit keys, and releases are isolated from application handlers. - -## Behavior - -| Input | Behavior | -|-------|----------| -| Valid chord | Appended to `steps`; listener stays active | -| Enter (no modifiers), `commitKeys: 'enter'`, `steps.length >= 1` | Commits and calls `onRecord` | -| Escape | Cancels; `onCancel` | -| Backspace / Delete (no modifiers) | Removes last step, or if empty runs only `onClear` and stops | - -Recorded chords use portable `Mod` format, same as `HotkeyRecorder`. - -## Under the hood - -`useHotkeySequenceRecorder` wraps the `HotkeySequenceRecorder` class and subscribes to its TanStack Store, same pattern as `useHotkeyRecorder`. diff --git a/docs/framework/vue/guides/sequences.md b/docs/framework/vue/guides/sequences.md index a176f839..0c53f195 100644 --- a/docs/framework/vue/guides/sequences.md +++ b/docs/framework/vue/guides/sequences.md @@ -3,148 +3,145 @@ title: Sequences Guide id: sequences --- -TanStack Hotkeys supports multi-key sequences in Vue, where keys are pressed one after another rather than simultaneously. +A sequence is a hotkey made of chords pressed one after another rather than together. Think Vim-style navigation, cheat codes, or multi-step commands. Sequences are ordinary hotkeys: register them with `useHotkey` and `useHotkeys`, list them with `useHotkeyRegistrations`, record them with `useHotkeyRecorder`, and format them with `formatForDisplay`. -Sequence steps use the same string syntax as single hotkeys. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. Display steps with `sequence.map((step) => formatForDisplay(step)).join(' → ')`. - -## Reactive options - -Sequence options support the same [property getter patterns](./hotkeys.md#property-getters) as hotkey options, including `enabled`, `timeout`, and `target`. Updates preserve the registration when its sequence, target, and platform stay the same. +Sequence steps use the same syntax as chords. For example, `['[KeyG]', '[KeyG]']` follows a physical position, while `['G', 'G']` follows the logical letter. A sequence can mix forms, such as `['Mod+[KeyK]', 'C']`. ## Basic usage +Pass an array of chords wherever you pass a hotkey: + ```vue ``` +The user must press the steps in order, each within the timeout window. A one-step array such as `['Escape']` is the chord `'Escape'`. + ## Many sequences at once -When you need several sequences, or a reactive list whose length changes, use `useHotkeySequences` instead of many `useHotkeySequence` calls. One composable registers every sequence safely. +`useHotkeys` registers chords and sequences together, including dynamic lists: -```vue - ``` -Options merge like `useHotkeys`: `HotkeysProvider` defaults, then `commonOptions`, then each definition's `options`. - -## Matching steps - -Both `SequenceManager` and `createSequenceMatcher` ignore modifier-only events, IME composition, and automatic keydown repeats. These events neither advance the sequence nor refresh its timeout: holding G does not complete a two-press G sequence. The manager prefers exact matches over weaker logical-key fallbacks while preserving equally strong matches. - ## Sequence options -```ts -useHotkeySequence(['G', 'G'], callback, { - timeout: 1000, - enabled: true, -}) -``` +Sequences accept the same options as chords, except `requireReset`, plus `timeout`. -### Reactive `enabled` +### `timeout` -When disabled, the sequence stays registered (visible in devtools); only execution is suppressed. - -```vue - +// Slow sequence - user has more time between keys +useHotkey(['Shift+Z', 'Shift+Z'], () => forceQuit(), { timeout: 2000 }) ``` -## Global defaults via provider +`timeout` is only accepted when the hotkey is a sequence. Set a default for every sequence with `HotkeysProvider` (or `provideHotkeysContext` in a root component's setup): ```vue - - ``` -### `meta` +### `enabled` and `meta` -Sequences support the same `meta` option as hotkeys. Attach a `name` and `description` to use in shortcut palettes and devtools. +These work exactly as they do for chords. Disabled sequences stay registered and visible in devtools, and `meta` flows to registrations, devtools, and cheat sheets. See the [Hotkeys Guide](./hotkeys). ```ts -useHotkeySequence(['G', 'G'], () => scrollToTop(), { - meta: { name: 'Go to Top', description: 'Scroll to the top of the page' }, -}) +useHotkey(['G', 'G'], () => scrollToTop(), () => ({ + enabled: isVimMode.value, + meta: { name: 'Go to top', description: 'Scroll to the top of the page' }, +})) ``` -See the [Hotkeys Guide](./hotkeys.md#metadata-name--description) for details on declaration merging and introspecting registrations. - -## Chained modifier chords +## Sequences with modifiers -This example follows physical R and T positions. Brackets retain those positions even when the keys produce different letters. Other sequences can continue using logical characters. - -Each step can use modifiers (for example `Mod+K` then `Mod+C`). You can use the same modifier on consecutive steps: +Each step in a sequence can include modifiers, and consecutive steps can repeat the same modifier: ```ts -useHotkeySequence(['Shift+[KeyR]', 'Shift+[KeyT]'], () => doNextAction()) -``` +// Ctrl+K followed by Ctrl+C (VS Code-style comment) +useHotkey(['Mod+K', 'Mod+C'], () => commentSelection()) -While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored: they do not advance the sequence and do not reset progress. A user can press Shift alone between `Shift+R` and `Shift+T` without breaking the sequence. +// Physical R then T positions, both with Shift +useHotkey(['Shift+[KeyR]', 'Shift+[KeyT]'], () => doNextAction()) +``` -## Common patterns +Brackets retain physical positions even when the keys produce different letters. While a sequence is in progress, modifier-only keydown events (Shift, Control, Alt, or Meta pressed alone) are ignored. They neither advance the sequence nor reset progress, so a user can tap or hold Shift between `Shift+R` and `Shift+T`. -### Vim-style navigation +## Vim-style navigation ```ts -useHotkeySequence(['G', 'G'], () => scrollToTop()) -useHotkeySequence(['G', 'Shift+G'], () => scrollToBottom()) -useHotkeySequence(['D', 'D'], () => deleteLine()) -useHotkeySequence(['D', 'W'], () => deleteWord()) -useHotkeySequence(['C', 'I', 'W'], () => changeInnerWord()) +useHotkeys([ + { hotkey: ['G', 'G'], callback: () => scrollToTop() }, + { hotkey: ['G', 'Shift+G'], callback: () => scrollToBottom() }, + { hotkey: ['D', 'D'], callback: () => deleteLine() }, + { hotkey: ['D', 'W'], callback: () => deleteWord() }, + { hotkey: ['C', 'I', 'W'], callback: () => changeInnerWord() }, +]) ``` -### Konami Code +## Displaying sequences + +`formatForDisplay` formats each step and joins them with `sequenceSeparator`, a space by default: ```ts -useHotkeySequence( - ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'B', 'A'], - () => enableEasterEgg(), - { timeout: 2000 }, -) +formatForDisplay(['Mod+K', 'Mod+C'], { platform: 'mac' }) // '⌘ K ⌘ C' +formatForDisplay(['G', 'G'], { sequenceSeparator: ' then ' }) // 'G then G' ``` -## Under the hood +Registration views report each sequence's progress in `matchedStepCount`, so a help panel can show how far the user has typed. See the [Hotkeys Guide](./hotkeys). -`useHotkeySequence` uses the singleton `SequenceManager`. You can also access it directly: +## How sequences work -```ts -import { createSequenceMatcher, getSequenceManager } from '@tanstack/vue-hotkeys' +The `HotkeyManager` tracks progress for every sequence registration. When a key is pressed: -const manager = getSequenceManager() -const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 }) -``` +1. It checks whether the key matches the next expected step of each sequence +2. If it matches, that sequence advances +3. If the timeout expires between steps, the sequence resets +4. When all steps are completed, the callback fires + +Modifier-only events, IME composition, and automatic keydown repeats neither advance a sequence nor refresh its timeout: holding G does not complete a two-press G sequence. Exact matches take priority over weaker logical-key fallbacks. + +Multiple sequences can share a prefix, such as `['D', 'D']`, `['D', 'W']`, and `['D', 'I', 'W']`. Progress is tracked for each sequence independently, so after pressing `D` the manager waits for the next key to determine which sequence completes. ### Conflicting registrations -For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate bindings. `conflictBehavior` applies to equivalent sequences without rewriting their stored strings. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection also checks prefixes and observed physical/logical overlap. +For the same target, duplicate detection compares resolved steps: modifier aliases, modifier order, and logical key casing do not create separate hotkeys. Physical and logical identities remain distinct, and a shared prefix alone is not a duplicate registration. Recorder conflict detection and app hotkey conflicts are stricter: there, a sequence also conflicts with a chord equal to its first step and with any sequence it starts. + +## Standalone matching + +For sequence matching without the singleton manager, use `createSequenceMatcher`: + +```ts +import { createSequenceMatcher } from '@tanstack/vue-hotkeys' + +const matcher = createSequenceMatcher(['G', 'G'], { timeout: 1000 }) + +document.addEventListener('keydown', (e) => { + if (matcher.match(e)) { + console.log('Sequence completed!') + } + console.log('Progress:', matcher.getProgress()) // e.g., 1 +}) +``` diff --git a/docs/framework/vue/quick-start.md b/docs/framework/vue/quick-start.md index 2e53388f..597955b9 100644 --- a/docs/framework/vue/quick-start.md +++ b/docs/framework/vue/quick-start.md @@ -81,14 +81,15 @@ useHotkey('Escape', () => { ```vue ``` -For several sequences or a list that changes at runtime, prefer a single `useHotkeySequences([...])` call (see the [Sequences guide](./guides/sequences.md#many-sequences-at-once)). +For several sequences or a list that changes at runtime, prefer a single `useHotkeys([...])` call (see the [Sequences guide](./guides/sequences.md#many-sequences-at-once)). ### Tracking held keys @@ -136,8 +137,7 @@ import { HotkeysProvider } from '@tanstack/vue-hotkeys' ``` +## Declaring app hotkeys + +To list every hotkey in a cheat sheet, including hotkeys whose components haven't mounted, and to let users customize them, declare them once with `defineAppHotkeys` and provide them with `HotkeysProvider`. See the [App Hotkeys Guide](./guides/app-hotkeys). + ## Next steps - [Hotkeys Guide](./guides/hotkeys) - [Sequences Guide](./guides/sequences) - [Hotkey Recording Guide](./guides/hotkey-recording) -- [Sequence Recording Guide](./guides/sequence-recording) +- [App Hotkeys Guide](./guides/app-hotkeys) - [Key State Tracking Guide](./guides/key-state-tracking) - [Formatting & Display Guide](./guides/formatting-display) diff --git a/docs/framework/vue/reference/functions/provideHotkeysContext.md b/docs/framework/vue/reference/functions/provideHotkeysContext.md index 43d888ed..66e33512 100644 --- a/docs/framework/vue/reference/functions/provideHotkeysContext.md +++ b/docs/framework/vue/reference/functions/provideHotkeysContext.md @@ -4,17 +4,32 @@ title: provideHotkeysContext --- ```ts -function provideHotkeysContext(defaultOptions?): void; +function provideHotkeysContext(options?): void; ``` -Defined in: [HotkeysProviderContext.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L26) +Defined in: [HotkeysProviderContext.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L69) + +Provides default options, app hotkeys, and the user's custom hotkeys to +descendants. `HotkeysProvider` calls this; call it directly in a root +component's setup instead of rendering the provider. ## Parameters -### defaultOptions? +### options? -`MaybeRefOrGetter`\<[`HotkeysProviderOptions`](../interfaces/HotkeysProviderOptions.md)\> +[`ProvideHotkeysContextOptions`](../interfaces/ProvideHotkeysContextOptions.md) = `{}` ## Returns `void` + +## Example + +```ts +provideHotkeysContext({ + appHotkeys, + customHotkeys: JSON.parse(localStorage.getItem('hotkeys') ?? '{}'), + onCustomHotkeysChange: (custom) => + localStorage.setItem('hotkeys', JSON.stringify(custom)), +}) +``` diff --git a/docs/framework/vue/reference/functions/useAppHotkeyList.md b/docs/framework/vue/reference/functions/useAppHotkeyList.md new file mode 100644 index 00000000..b330917f --- /dev/null +++ b/docs/framework/vue/reference/functions/useAppHotkeyList.md @@ -0,0 +1,18 @@ +--- +id: useAppHotkeyList +title: useAppHotkeyList +--- + +```ts +function useAppHotkeyList(): ComputedRef>[]>; +``` + +Defined in: useAppHotkeys.ts:48 + +Lists every app hotkey with its live status, in declaration order, for +cheat sheets and settings screens. App hotkeys whose components have not +mounted are `'inactive'`; those without a hotkey are `'unbound'`. + +## Returns + +`ComputedRef`\<`AppHotkeyListItem`\<`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>[]\> diff --git a/docs/framework/vue/reference/functions/useAppHotkeys.md b/docs/framework/vue/reference/functions/useAppHotkeys.md new file mode 100644 index 00000000..33f52678 --- /dev/null +++ b/docs/framework/vue/reference/functions/useAppHotkeys.md @@ -0,0 +1,24 @@ +--- +id: useAppHotkeys +title: useAppHotkeys +--- + +```ts +function useAppHotkeys(): ComputedRef>>>; +``` + +Defined in: useAppHotkeys.ts:35 + +Returns a computed ref of the effective app hotkeys from `HotkeysProvider`, +typed by `Register`: each declaration with the user's custom hotkey applied. + +## Returns + +`ComputedRef`\<`Readonly`\<`Record`\<`string`, `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>\>\>\> + +## Example + +```ts +const hotkeys = useAppHotkeys() +useHotkey(() => hotkeys.value['doc.save'], () => save()) +``` diff --git a/docs/framework/vue/reference/functions/useCustomHotkeys.md b/docs/framework/vue/reference/functions/useCustomHotkeys.md new file mode 100644 index 00000000..661688f6 --- /dev/null +++ b/docs/framework/vue/reference/functions/useCustomHotkeys.md @@ -0,0 +1,18 @@ +--- +id: useCustomHotkeys +title: useCustomHotkeys +--- + +```ts +function useCustomHotkeys(): UseCustomHotkeysResult; +``` + +Defined in: useAppHotkeys.ts:87 + +Reads and changes the user's custom hotkeys. Changes apply to every +registered app hotkey immediately and are emitted by the provider as +`custom-hotkeys-change`, ready to save. + +## Returns + +[`UseCustomHotkeysResult`](../interfaces/UseCustomHotkeysResult.md) diff --git a/docs/framework/vue/reference/functions/useDefaultHotkeysOptions.md b/docs/framework/vue/reference/functions/useDefaultHotkeysOptions.md index 1f316c09..8e184ce5 100644 --- a/docs/framework/vue/reference/functions/useDefaultHotkeysOptions.md +++ b/docs/framework/vue/reference/functions/useDefaultHotkeysOptions.md @@ -7,7 +7,7 @@ title: useDefaultHotkeysOptions function useDefaultHotkeysOptions(): HotkeysProviderOptions; ``` -Defined in: [HotkeysProviderContext.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L42) +Defined in: [HotkeysProviderContext.ts:109](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L109) ## Returns diff --git a/docs/framework/vue/reference/functions/useHotkey.md b/docs/framework/vue/reference/functions/useHotkey.md index e48c0333..0b6a8e5b 100644 --- a/docs/framework/vue/reference/functions/useHotkey.md +++ b/docs/framework/vue/reference/functions/useHotkey.md @@ -3,93 +3,106 @@ id: useHotkey title: useHotkey --- +## Call Signature + ```ts -function useHotkey( +function useHotkey( hotkey, callback, options?): void; ``` -Defined in: [useHotkey.ts:99](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L99) - -Vue composable for registering a keyboard hotkey. +Defined in: [useHotkey.ts:75](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L75) -Uses the singleton HotkeyManager for efficient event handling. -The callback receives both the keyboard event and a context object -containing the hotkey string and parsed hotkey. +Vue composable for registering a hotkey: a chord such as `'Mod+S'`, a +sequence such as `['G', 'G']`, or an app hotkey from `defineAppHotkeys`. +The hotkey and options may be refs or getters. -This composable automatically tracks reactive dependencies and updates -the registration when options or the callback change. +Chords accept `requireReset`; sequences accept `timeout`. A one-step +sequence registers as its chord. An app hotkey uses its effective hotkey +from `HotkeysProvider` (including the user's custom hotkey) and its +declared meta and options, and registers nothing while unbound. -## Parameters +### Type Parameters -### hotkey +#### T -`MaybeRefOrGetter`\<`RegisterableHotkey`\> +`T` *extends* `AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\> -The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object (supports `mod` for cross-platform) +### Parameters -### callback +#### hotkey -`HotkeyCallback` +`MaybeRefOrGetter`\<`T`\> -The function to call when the hotkey is pressed +#### callback -### options? +`HotkeyCallback` -`MaybeRefOrGetter`\<[`UseHotkeyOptions`](../interfaces/UseHotkeyOptions.md)\> = `{}` +#### options? -Options for the hotkey behavior +`MaybeRefOrGetter`\<[`UseAppHotkeyOptions`](../type-aliases/UseAppHotkeyOptions.md)\<`T`\>\> -## Returns +### Returns `void` -## Examples +### Example -```vue - +```ts +function useHotkey( + hotkey, + callback, + options?): void; ``` -```vue - -``` +Chords accept `requireReset`; sequences accept `timeout`. A one-step +sequence registers as its chord. An app hotkey uses its effective hotkey +from `HotkeysProvider` (including the user's custom hotkey) and its +declared meta and options, and registers nothing while unbound. + +### Type Parameters -```vue - +### Parameters - +#### hotkey + +`MaybeRefOrGetter`\<`T`\> + +#### callback + +`HotkeyCallback` + +#### options? + +`MaybeRefOrGetter`\<[`UseHotkeyOptionsFor`](../type-aliases/UseHotkeyOptionsFor.md)\<`T`\>\> + +### Returns + +`void` + +### Example + +```ts +useHotkey('Mod+S', () => save()) +useHotkey(['G', 'G'], () => scrollToTop(), { timeout: 500 }) +useHotkey('Escape', () => close(), { enabled: isOpen }) ``` diff --git a/docs/framework/vue/reference/functions/useHotkeyHint.md b/docs/framework/vue/reference/functions/useHotkeyHint.md index 5d982332..dda117d5 100644 --- a/docs/framework/vue/reference/functions/useHotkeyHint.md +++ b/docs/framework/vue/reference/functions/useHotkeyHint.md @@ -7,19 +7,21 @@ title: useHotkeyHint function useHotkeyHint(hotkey, options?): ComputedRef; ``` -Defined in: [useHotkeyHint.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyHint.ts#L14) +Defined in: [useHotkeyHint.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyHint.ts#L24) Returns a computed ref indicating whether held modifiers reveal this shortcut. -Pass refs or getters to track changing bindings and options. -Uses the nonempty-subset/AltGr rules of `matchesHeldModifiers`; `exact` requires -every modifier. Combine with the action's enabled state before showing a badge. -This helper does not register a shortcut or check whether its target is focused. +Pass refs or getters to track changing hotkeys and options. A sequence +reveals its hint from its first step, and an app hotkey uses its effective +hotkey (false while unbound). This helper does not register a shortcut or +check whether its target is focused. ## Parameters ### hotkey -`MaybeRefOrGetter`\<`RegisterableHotkey`\> +`MaybeRefOrGetter`\< + \| `AppHotkey`\<`string`, HotkeyChord \| HotkeySequence \| null\> + \| `RegisterableHotkey`\> ### options? diff --git a/docs/framework/vue/reference/functions/useHotkeyRecorder.md b/docs/framework/vue/reference/functions/useHotkeyRecorder.md index bdd20bf6..566ee7eb 100644 --- a/docs/framework/vue/reference/functions/useHotkeyRecorder.md +++ b/docs/framework/vue/reference/functions/useHotkeyRecorder.md @@ -3,60 +3,162 @@ id: useHotkeyRecorder title: useHotkeyRecorder --- +## Call Signature + +```ts +function useHotkeyRecorder(options): VueHotkeyRecorder; +``` + +Defined in: [useHotkeyRecorder.ts:46](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L46) + +Vue composable for recording keyboard shortcuts. Options may be a ref or getter. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### options + +`HotkeyChordRecorderOptions` + +### Returns + +[`VueHotkeyRecorder`](../interfaces/VueHotkeyRecorder.md)\<`HotkeyChord`\> + +### Example + +```ts +const recorder = useHotkeyRecorder({ onRecord: (hotkey) => (shortcut.value = hotkey) }) +const sequenceRecorder = useHotkeyRecorder({ maxSteps: 3, onRecord: save }) +``` + +## Call Signature + +```ts +function useHotkeyRecorder(options): VueHotkeyRecorder; +``` + +Defined in: [useHotkeyRecorder.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L49) + +Vue composable for recording keyboard shortcuts. Options may be a ref or getter. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### options + +`HotkeySequenceRecorderOptions` + +### Returns + +[`VueHotkeyRecorder`](../interfaces/VueHotkeyRecorder.md) + +### Example + +```ts +const recorder = useHotkeyRecorder({ onRecord: (hotkey) => (shortcut.value = hotkey) }) +const sequenceRecorder = useHotkeyRecorder({ maxSteps: 3, onRecord: save }) +``` + +## Call Signature + ```ts function useHotkeyRecorder(options): VueHotkeyRecorder; ``` -Defined in: [useHotkeyRecorder.ts:62](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L62) +Defined in: [useHotkeyRecorder.ts:54](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L54) -Vue composable for recording keyboard shortcuts. +Vue composable for recording keyboard shortcuts. Options may be a ref or getter. -This composable 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. +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. -## Parameters +### Parameters -### options +#### options -`MaybeRefOrGetter`\<`HotkeyRecorderOptions`\> +() => `HotkeySequenceRecorderOptions` -Configuration options for the recorder +### Returns -## Returns +[`VueHotkeyRecorder`](../interfaces/VueHotkeyRecorder.md) + +### Example + +```ts +const recorder = useHotkeyRecorder({ onRecord: (hotkey) => (shortcut.value = hotkey) }) +const sequenceRecorder = useHotkeyRecorder({ maxSteps: 3, onRecord: save }) +``` + +## Call Signature + +```ts +function useHotkeyRecorder(options): VueHotkeyRecorder; +``` + +Defined in: [useHotkeyRecorder.ts:57](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L57) + +Vue composable for recording keyboard shortcuts. Options may be a ref or getter. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### options + +() => `HotkeyChordRecorderOptions` + +### Returns + +[`VueHotkeyRecorder`](../interfaces/VueHotkeyRecorder.md)\<`HotkeyChord`\> + +### Example + +```ts +const recorder = useHotkeyRecorder({ onRecord: (hotkey) => (shortcut.value = hotkey) }) +const sequenceRecorder = useHotkeyRecorder({ maxSteps: 3, onRecord: save }) +``` + +## Call Signature + +```ts +function useHotkeyRecorder(options): VueHotkeyRecorder; +``` + +Defined in: [useHotkeyRecorder.ts:60](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L60) + +Vue composable for recording keyboard shortcuts. Options may be a ref or getter. + +With the default `maxSteps: 1`, the first valid chord is recorded. With a +larger `maxSteps`, each chord adds a step; the recording commits on Enter +(unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and +`onRecord` receives a chord (one step) or a sequence. + +### Parameters + +#### options + +`Ref`\<`HotkeyRecorderOptions`\> + +### Returns [`VueHotkeyRecorder`](../interfaces/VueHotkeyRecorder.md) -An object with recording state and control functions - -## Example - -```vue - - - +### Example + +```ts +const recorder = useHotkeyRecorder({ onRecord: (hotkey) => (shortcut.value = hotkey) }) +const sequenceRecorder = useHotkeyRecorder({ maxSteps: 3, onRecord: save }) ``` diff --git a/docs/framework/vue/reference/functions/useHotkeyRegistrations.md b/docs/framework/vue/reference/functions/useHotkeyRegistrations.md index 202549f4..a4c0a020 100644 --- a/docs/framework/vue/reference/functions/useHotkeyRegistrations.md +++ b/docs/framework/vue/reference/functions/useHotkeyRegistrations.md @@ -4,20 +4,31 @@ title: useHotkeyRegistrations --- ```ts -function useHotkeyRegistrations(): HotkeyRegistrationsResult; +function useHotkeyRegistrations(): Readonly>; ``` -Defined in: [useHotkeyRegistrations.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRegistrations.ts#L33) +Defined in: [useHotkeyRegistrations.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRegistrations.ts#L26) -Vue composable that reactively reads all hotkey and sequence registrations -from the singleton managers. +Vue composable that reactively reads every hotkey registration, chords and +sequences, from the singleton HotkeyManager. Narrow each view with `kind`. -This is a standalone composable that does NOT require the HotkeysProvider. -It subscribes to both HotkeyManager and SequenceManager stores and -triggers reactivity when registrations change. +This composable does NOT require the HotkeysProvider. ## Returns -[`HotkeyRegistrationsResult`](../interfaces/HotkeyRegistrationsResult.md) +`Readonly`\<`Ref`\<`HotkeyRegistrationView`[]\>\> -Object with `hotkeys` and `sequences` refs +A ref of all registrations, without callbacks + +## Example + +```vue + + +``` diff --git a/docs/framework/vue/reference/functions/useHotkeySequence.md b/docs/framework/vue/reference/functions/useHotkeySequence.md deleted file mode 100644 index d8848b38..00000000 --- a/docs/framework/vue/reference/functions/useHotkeySequence.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -id: useHotkeySequence -title: useHotkeySequence ---- - -```ts -function useHotkeySequence( - sequence, - callback, - options?): void; -``` - -Defined in: [useHotkeySequence.ts:76](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequence.ts#L76) - -Vue composable for registering a keyboard shortcut sequence (Vim-style). - -This composable allows you to register multi-key sequences like 'g g' or 'd d' -that trigger when the full sequence is pressed within a timeout. - -Each step may include modifiers. You can chain the same modifier across -steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift, -Control, Alt, or Meta pressed alone) are ignored while matching—they do not -advance the sequence or reset progress. - -## Parameters - -### sequence - -`MaybeRefOrGetter`\<`HotkeySequence`\> - -Array of hotkey strings that form the sequence - -### callback - -`HotkeyCallback` - -Function to call when the sequence is completed - -### options? - -`MaybeRefOrGetter`\<[`UseHotkeySequenceOptions`](../interfaces/UseHotkeySequenceOptions.md)\> = `{}` - -Options for the sequence behavior - -## Returns - -`void` - -## Example - -```vue - - - -``` diff --git a/docs/framework/vue/reference/functions/useHotkeySequenceRecorder.md b/docs/framework/vue/reference/functions/useHotkeySequenceRecorder.md deleted file mode 100644 index 4a37a79c..00000000 --- a/docs/framework/vue/reference/functions/useHotkeySequenceRecorder.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: useHotkeySequenceRecorder -title: useHotkeySequenceRecorder ---- - -```ts -function useHotkeySequenceRecorder(options): VueHotkeySequenceRecorder; -``` - -Defined in: [useHotkeySequenceRecorder.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L24) - -Vue composable for recording multi-chord sequences (Vim-style shortcuts). - -## Parameters - -### options - -`MaybeRefOrGetter`\<`HotkeySequenceRecorderOptions`\> - -## Returns - -[`VueHotkeySequenceRecorder`](../interfaces/VueHotkeySequenceRecorder.md) diff --git a/docs/framework/vue/reference/functions/useHotkeySequences.md b/docs/framework/vue/reference/functions/useHotkeySequences.md deleted file mode 100644 index cbae9270..00000000 --- a/docs/framework/vue/reference/functions/useHotkeySequences.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -id: useHotkeySequences -title: useHotkeySequences ---- - -```ts -function useHotkeySequences(definitions, commonOptions?): void; -``` - -Defined in: [useHotkeySequences.ts:68](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequences.ts#L68) - -Vue composable for registering multiple keyboard shortcut sequences at once (Vim-style). - -Uses the singleton SequenceManager. Accepts a dynamic array of definitions, or a getter/ref -that returns one, so you can register variable-length lists safely. - -Options are merged in this order: -HotkeysProvider defaults < commonOptions < per-definition options - -Definitions with an empty `sequence` are skipped (no registration). - -## Parameters - -### definitions - -`MaybeRefOrGetter`\<[`UseHotkeySequenceDefinition`](../interfaces/UseHotkeySequenceDefinition.md)[]\> - -Array of sequence definitions, or a getter/ref - -### commonOptions? - -`MaybeRefOrGetter`\<[`UseHotkeySequenceOptions`](../interfaces/UseHotkeySequenceOptions.md)\> = `{}` - -Shared options applied to all sequences (overridden by per-definition options) - -## Returns - -`void` - -## Examples - -```vue - -``` - -```vue - -``` diff --git a/docs/framework/vue/reference/functions/useHotkeys.md b/docs/framework/vue/reference/functions/useHotkeys.md index 030f4d44..673d359d 100644 --- a/docs/framework/vue/reference/functions/useHotkeys.md +++ b/docs/framework/vue/reference/functions/useHotkeys.md @@ -7,63 +7,35 @@ title: useHotkeys function useHotkeys(hotkeys, commonOptions?): void; ``` -Defined in: [useHotkeys.ts:73](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L73) +Defined in: [useHotkeys.ts:90](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L90) -Vue composable for registering multiple keyboard hotkeys at once. +Vue composable for registering several hotkeys at once: chords, sequences, +and app hotkeys. The list, each hotkey, and options may be refs or getters. -Uses the singleton HotkeyManager for efficient event handling. -Accepts a dynamic array of hotkey definitions, making it safe to use -with variable-length lists. - -Options are merged in this order: -HotkeysProvider defaults < commonOptions < per-definition options +Options merge in this order: provider defaults, app hotkey declaration, +commonOptions, per-hotkey options. Each option applies only to the kinds +that support it. Unbound app hotkeys and empty sequences register nothing. +A target that resolves to `null` skips registration until it is set. ## Parameters ### hotkeys -`MaybeRefOrGetter`\<[`UseHotkeyDefinition`](../interfaces/UseHotkeyDefinition.md)[]\> - -Array of hotkey definitions to register, or a getter/ref +`MaybeRefOrGetter`\ ### commonOptions? -`MaybeRefOrGetter`\<[`UseHotkeyOptions`](../interfaces/UseHotkeyOptions.md)\> = `{}` - -Shared options applied to all hotkeys (overridden by per-definition options) +`MaybeRefOrGetter`\<[`UseHotkeyCommonOptions`](../type-aliases/UseHotkeyCommonOptions.md)\> = `{}` ## Returns `void` -## Examples - -```vue - -``` - -```vue - ``` diff --git a/docs/framework/vue/reference/functions/useHotkeysContext.md b/docs/framework/vue/reference/functions/useHotkeysContext.md index b853d9e3..9f8fa8f2 100644 --- a/docs/framework/vue/reference/functions/useHotkeysContext.md +++ b/docs/framework/vue/reference/functions/useHotkeysContext.md @@ -7,7 +7,7 @@ title: useHotkeysContext function useHotkeysContext(): HotkeysContextValue | null; ``` -Defined in: [HotkeysProviderContext.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L38) +Defined in: [HotkeysProviderContext.ts:105](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L105) ## Returns diff --git a/docs/framework/vue/reference/index.md b/docs/framework/vue/reference/index.md index 78df6d3f..991b6187 100644 --- a/docs/framework/vue/reference/index.md +++ b/docs/framework/vue/reference/index.md @@ -5,14 +5,21 @@ title: "@tanstack/vue-hotkeys" ## Interfaces -- [HotkeyRegistrationsResult](interfaces/HotkeyRegistrationsResult.md) - [HotkeysProviderOptions](interfaces/HotkeysProviderOptions.md) -- [UseHotkeyDefinition](interfaces/UseHotkeyDefinition.md) -- [UseHotkeyOptions](interfaces/UseHotkeyOptions.md) -- [UseHotkeySequenceDefinition](interfaces/UseHotkeySequenceDefinition.md) -- [UseHotkeySequenceOptions](interfaces/UseHotkeySequenceOptions.md) +- [ProvideHotkeysContextOptions](interfaces/ProvideHotkeysContextOptions.md) +- [UseCustomHotkeysResult](interfaces/UseCustomHotkeysResult.md) - [VueHotkeyRecorder](interfaces/VueHotkeyRecorder.md) -- [VueHotkeySequenceRecorder](interfaces/VueHotkeySequenceRecorder.md) + +## Type Aliases + +- [UseAppHotkeyOptions](type-aliases/UseAppHotkeyOptions.md) +- [UseHotkeyChordOptions](type-aliases/UseHotkeyChordOptions.md) +- [UseHotkeyCommonOptions](type-aliases/UseHotkeyCommonOptions.md) +- [UseHotkeyDefinition](type-aliases/UseHotkeyDefinition.md) +- [UseHotkeyOptions](type-aliases/UseHotkeyOptions.md) +- [UseHotkeyOptionsFor](type-aliases/UseHotkeyOptionsFor.md) +- [UseHotkeySequenceOptions](type-aliases/UseHotkeySequenceOptions.md) +- [UseHotkeyTarget](type-aliases/UseHotkeyTarget.md) ## Variables @@ -21,6 +28,9 @@ title: "@tanstack/vue-hotkeys" ## Functions - [provideHotkeysContext](functions/provideHotkeysContext.md) +- [useAppHotkeyList](functions/useAppHotkeyList.md) +- [useAppHotkeys](functions/useAppHotkeys.md) +- [useCustomHotkeys](functions/useCustomHotkeys.md) - [useDefaultHotkeysOptions](functions/useDefaultHotkeysOptions.md) - [useHeldKeyCodes](functions/useHeldKeyCodes.md) - [useHeldKeys](functions/useHeldKeys.md) @@ -30,7 +40,4 @@ title: "@tanstack/vue-hotkeys" - [useHotkeyRegistrations](functions/useHotkeyRegistrations.md) - [useHotkeys](functions/useHotkeys.md) - [useHotkeysContext](functions/useHotkeysContext.md) -- [useHotkeySequence](functions/useHotkeySequence.md) -- [useHotkeySequenceRecorder](functions/useHotkeySequenceRecorder.md) -- [useHotkeySequences](functions/useHotkeySequences.md) - [useKeyHold](functions/useKeyHold.md) diff --git a/docs/framework/vue/reference/interfaces/HotkeyRegistrationsResult.md b/docs/framework/vue/reference/interfaces/HotkeyRegistrationsResult.md deleted file mode 100644 index 4f34bb59..00000000 --- a/docs/framework/vue/reference/interfaces/HotkeyRegistrationsResult.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -id: HotkeyRegistrationsResult -title: HotkeyRegistrationsResult ---- - -Defined in: [useHotkeyRegistrations.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRegistrations.ts#L16) - -Return type for useHotkeyRegistrations. - -## Properties - -### hotkeys - -```ts -hotkeys: Ref; -``` - -Defined in: [useHotkeyRegistrations.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRegistrations.ts#L18) - -All registered hotkeys (public view, no callbacks) - -*** - -### sequences - -```ts -sequences: Ref; -``` - -Defined in: [useHotkeyRegistrations.ts:20](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRegistrations.ts#L20) - -All registered sequences diff --git a/docs/framework/vue/reference/interfaces/HotkeysProviderOptions.md b/docs/framework/vue/reference/interfaces/HotkeysProviderOptions.md index ef313fd5..87b23a43 100644 --- a/docs/framework/vue/reference/interfaces/HotkeysProviderOptions.md +++ b/docs/framework/vue/reference/interfaces/HotkeysProviderOptions.md @@ -3,44 +3,29 @@ id: HotkeysProviderOptions title: HotkeysProviderOptions --- -Defined in: [HotkeysProviderContext.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L10) +Defined in: [HotkeysProviderContext.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L12) ## Properties ### hotkey? ```ts -optional hotkey?: Partial; +optional hotkey?: UseHotkeyCommonOptions; ``` -Defined in: [HotkeysProviderContext.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L11) - -*** - -### hotkeyRecorder? - -```ts -optional hotkeyRecorder?: Partial; -``` +Defined in: [HotkeysProviderContext.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L17) -Defined in: [HotkeysProviderContext.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L12) +Defaults for every hotkey registered by `useHotkey` and `useHotkeys`. +`requireReset` applies to chords and `timeout` applies to sequences. *** -### hotkeySequence? +### hotkeyRecorder? ```ts -optional hotkeySequence?: Partial; +optional hotkeyRecorder?: Partial>; ``` -Defined in: [HotkeysProviderContext.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L14) - -*** - -### hotkeySequenceRecorder? - -```ts -optional hotkeySequenceRecorder?: Partial; -``` +Defined in: [HotkeysProviderContext.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L19) -Defined in: [HotkeysProviderContext.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L13) +Defaults for every `useHotkeyRecorder`. diff --git a/docs/framework/vue/reference/interfaces/ProvideHotkeysContextOptions.md b/docs/framework/vue/reference/interfaces/ProvideHotkeysContextOptions.md new file mode 100644 index 00000000..73b8db0c --- /dev/null +++ b/docs/framework/vue/reference/interfaces/ProvideHotkeysContextOptions.md @@ -0,0 +1,70 @@ +--- +id: ProvideHotkeysContextOptions +title: ProvideHotkeysContextOptions +--- + +Defined in: [HotkeysProviderContext.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L38) + +Options for `provideHotkeysContext`. + +## Properties + +### appHotkeys? + +```ts +optional appHotkeys?: MaybeRefOrGetter< + | Readonly>> +| undefined>; +``` + +Defined in: [HotkeysProviderContext.ts:44](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L44) + +Every app hotkey, from `defineAppHotkeys`. Read when the context is +provided; later changes update the declarations. + +*** + +### customHotkeys? + +```ts +optional customHotkeys?: + | CustomHotkeys>>> + | null; +``` + +Defined in: [HotkeysProviderContext.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L49) + +Starting custom hotkeys, such as saved data. Read once; the context owns +the state afterwards. Invalid entries are ignored with a warning. + +*** + +### defaultOptions? + +```ts +optional defaultOptions?: MaybeRefOrGetter; +``` + +Defined in: [HotkeysProviderContext.ts:39](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L39) + +*** + +### onCustomHotkeysChange? + +```ts +optional onCustomHotkeysChange?: (customHotkeys) => void; +``` + +Defined in: [HotkeysProviderContext.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProviderContext.ts#L51) + +Called with the custom hotkeys after every change, ready to save. + +#### Parameters + +##### customHotkeys + +`CustomHotkeys` + +#### Returns + +`void` diff --git a/docs/framework/vue/reference/interfaces/UseCustomHotkeysResult.md b/docs/framework/vue/reference/interfaces/UseCustomHotkeysResult.md new file mode 100644 index 00000000..ba3d29c4 --- /dev/null +++ b/docs/framework/vue/reference/interfaces/UseCustomHotkeysResult.md @@ -0,0 +1,111 @@ +--- +id: UseCustomHotkeysResult +title: UseCustomHotkeysResult +--- + +Defined in: useAppHotkeys.ts:60 + +Custom hotkey state and actions returned by `useCustomHotkeys`. + +## Properties + +### customHotkeys + +```ts +customHotkeys: ComputedRef>>>>; +``` + +Defined in: useAppHotkeys.ts:62 + +The user's hotkeys that differ from the defaults. + +*** + +### findConflicts + +```ts +findConflicts: (id, hotkey) => AppHotkey[]; +``` + +Defined in: useAppHotkeys.ts:76 + +Other app hotkeys that would collide if `id` used `hotkey`. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] + +*** + +### resetCustomHotkey + +```ts +resetCustomHotkey: (id) => void; +``` + +Defined in: useAppHotkeys.ts:72 + +Restores the default hotkey for an app hotkey. + +#### Parameters + +##### id + +`string` + +#### Returns + +`void` + +*** + +### resetCustomHotkeys + +```ts +resetCustomHotkeys: () => void; +``` + +Defined in: useAppHotkeys.ts:74 + +Restores every default hotkey. + +#### Returns + +`void` + +*** + +### setCustomHotkey + +```ts +setCustomHotkey: (id, hotkey) => AppHotkey[]; +``` + +Defined in: useAppHotkeys.ts:67 + +Sets the user's hotkey for an app hotkey. `null` unbinds it. +Returns conflicting app hotkeys; when any exist, nothing changes. + +#### Parameters + +##### id + +`string` + +##### hotkey + +`Hotkey` \| `null` + +#### Returns + +`AppHotkey`\<`string`, `HotkeyChord` \| `HotkeySequence` \| `null`\>[] diff --git a/docs/framework/vue/reference/interfaces/UseHotkeyDefinition.md b/docs/framework/vue/reference/interfaces/UseHotkeyDefinition.md deleted file mode 100644 index 718f35af..00000000 --- a/docs/framework/vue/reference/interfaces/UseHotkeyDefinition.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: UseHotkeyDefinition -title: UseHotkeyDefinition ---- - -Defined in: [useHotkeys.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L19) - -A single hotkey definition for use with `useHotkeys`. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [useHotkeys.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L23) - -The function to call when the hotkey is pressed - -*** - -### hotkey - -```ts -hotkey: MaybeRefOrGetter; -``` - -Defined in: [useHotkeys.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L21) - -The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object - -*** - -### options? - -```ts -optional options?: MaybeRefOrGetter; -``` - -Defined in: [useHotkeys.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L25) - -Per-hotkey options (merged on top of commonOptions) diff --git a/docs/framework/vue/reference/interfaces/UseHotkeyOptions.md b/docs/framework/vue/reference/interfaces/UseHotkeyOptions.md deleted file mode 100644 index b668007d..00000000 --- a/docs/framework/vue/reference/interfaces/UseHotkeyOptions.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -id: UseHotkeyOptions -title: UseHotkeyOptions ---- - -Defined in: [useHotkey.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L17) - -## Extends - -- `Omit`\<`HotkeyOptions`, `"enabled"` \| `"target"`\> - -## Properties - -### enabled? - -```ts -optional enabled?: MaybeRefOrGetter; -``` - -Defined in: [useHotkey.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L26) - -Whether the hotkey is active. -Can be a Ref, a getter function, or a boolean value. -Defaults to true. - -*** - -### target? - -```ts -optional target?: MaybeRefOrGetter; -``` - -Defined in: [useHotkey.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L32) - -The DOM element to attach the event listener to. -Can be a Ref, a getter function, direct DOM element, or null. -Defaults to document. diff --git a/docs/framework/vue/reference/interfaces/UseHotkeySequenceDefinition.md b/docs/framework/vue/reference/interfaces/UseHotkeySequenceDefinition.md deleted file mode 100644 index 6cd5de1a..00000000 --- a/docs/framework/vue/reference/interfaces/UseHotkeySequenceDefinition.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: UseHotkeySequenceDefinition -title: UseHotkeySequenceDefinition ---- - -Defined in: [useHotkeySequences.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequences.ts#L15) - -A single sequence definition for use with `useHotkeySequences`. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [useHotkeySequences.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequences.ts#L19) - -The function to call when the sequence is completed - -*** - -### options? - -```ts -optional options?: MaybeRefOrGetter; -``` - -Defined in: [useHotkeySequences.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequences.ts#L21) - -Per-sequence options (merged on top of commonOptions) - -*** - -### sequence - -```ts -sequence: MaybeRefOrGetter; -``` - -Defined in: [useHotkeySequences.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequences.ts#L17) - -Array of hotkey strings that form the sequence diff --git a/docs/framework/vue/reference/interfaces/UseHotkeySequenceOptions.md b/docs/framework/vue/reference/interfaces/UseHotkeySequenceOptions.md deleted file mode 100644 index 26c2cc58..00000000 --- a/docs/framework/vue/reference/interfaces/UseHotkeySequenceOptions.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -id: UseHotkeySequenceOptions -title: UseHotkeySequenceOptions ---- - -Defined in: [useHotkeySequence.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequence.ts#L12) - -## Extends - -- `Omit`\<`SequenceOptions`, `"enabled"` \| `"target"`\> - -## Properties - -### enabled? - -```ts -optional enabled?: MaybeRefOrGetter; -``` - -Defined in: [useHotkeySequence.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequence.ts#L21) - -Whether the sequence is active. -Can be a Ref, a getter function, or a boolean value. -Defaults to true. - -*** - -### target? - -```ts -optional target?: MaybeRefOrGetter; -``` - -Defined in: [useHotkeySequence.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequence.ts#L27) - -The DOM element to attach the event listener to. -Can be a Ref, a getter function, direct DOM element, or null. -Defaults to document. diff --git a/docs/framework/vue/reference/interfaces/VueHotkeyRecorder.md b/docs/framework/vue/reference/interfaces/VueHotkeyRecorder.md index bcc26a30..54b63f95 100644 --- a/docs/framework/vue/reference/interfaces/VueHotkeyRecorder.md +++ b/docs/framework/vue/reference/interfaces/VueHotkeyRecorder.md @@ -3,7 +3,13 @@ id: VueHotkeyRecorder title: VueHotkeyRecorder --- -Defined in: [useHotkeyRecorder.ts:8](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L8) +Defined in: [useHotkeyRecorder.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L15) + +## Type Parameters + +### THotkey + +`THotkey` *extends* `Hotkey` = `Hotkey` ## Properties @@ -13,9 +19,25 @@ Defined in: [useHotkeyRecorder.ts:8](https://github.com/TanStack/hotkeys/blob/ma cancelRecording: () => void; ``` -Defined in: [useHotkeyRecorder.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L18) +Defined in: [useHotkeyRecorder.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L27) -Cancel recording without saving +Cancel recording and call onCancel + +#### Returns + +`void` + +*** + +### commitRecording + +```ts +commitRecording: () => void; +``` + +Defined in: [useHotkeyRecorder.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L29) + +Commit the recorded steps now (multi-step recordings) #### Returns @@ -26,10 +48,10 @@ Cancel recording without saving ### isRecording ```ts -isRecording: Ref; +isRecording: Readonly>; ``` -Defined in: [useHotkeyRecorder.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L10) +Defined in: [useHotkeyRecorder.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L17) Whether recording is currently active @@ -38,12 +60,12 @@ Whether recording is currently active ### recordedHotkey ```ts -recordedHotkey: Ref; +recordedHotkey: Readonly>; ``` -Defined in: [useHotkeyRecorder.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L12) +Defined in: [useHotkeyRecorder.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L21) -The currently recorded hotkey (for live preview) +The last committed hotkey *** @@ -53,7 +75,7 @@ The currently recorded hotkey (for live preview) startRecording: () => void; ``` -Defined in: [useHotkeyRecorder.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L14) +Defined in: [useHotkeyRecorder.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L23) Start recording a new hotkey @@ -63,15 +85,27 @@ Start recording a new hotkey *** +### steps + +```ts +steps: Readonly>; +``` + +Defined in: [useHotkeyRecorder.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L19) + +Chords recorded so far in a multi-step recording (for live preview) + +*** + ### stopRecording ```ts stopRecording: () => void; ``` -Defined in: [useHotkeyRecorder.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L16) +Defined in: [useHotkeyRecorder.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeyRecorder.ts#L25) -Stop recording (same as cancel) +Stop recording without calling onCancel #### Returns diff --git a/docs/framework/vue/reference/interfaces/VueHotkeySequenceRecorder.md b/docs/framework/vue/reference/interfaces/VueHotkeySequenceRecorder.md deleted file mode 100644 index 5ee65750..00000000 --- a/docs/framework/vue/reference/interfaces/VueHotkeySequenceRecorder.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -id: VueHotkeySequenceRecorder -title: VueHotkeySequenceRecorder ---- - -Defined in: [useHotkeySequenceRecorder.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L11) - -## Properties - -### cancelRecording - -```ts -cancelRecording: () => void; -``` - -Defined in: [useHotkeySequenceRecorder.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L17) - -#### Returns - -`void` - -*** - -### commitRecording - -```ts -commitRecording: () => void; -``` - -Defined in: [useHotkeySequenceRecorder.ts:18](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L18) - -#### Returns - -`void` - -*** - -### isRecording - -```ts -isRecording: Ref; -``` - -Defined in: [useHotkeySequenceRecorder.ts:12](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L12) - -*** - -### recordedSequence - -```ts -recordedSequence: Ref; -``` - -Defined in: [useHotkeySequenceRecorder.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L14) - -*** - -### startRecording - -```ts -startRecording: () => void; -``` - -Defined in: [useHotkeySequenceRecorder.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L15) - -#### Returns - -`void` - -*** - -### steps - -```ts -steps: Ref; -``` - -Defined in: [useHotkeySequenceRecorder.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L13) - -*** - -### stopRecording - -```ts -stopRecording: () => void; -``` - -Defined in: [useHotkeySequenceRecorder.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeySequenceRecorder.ts#L16) - -#### Returns - -`void` diff --git a/docs/framework/vue/reference/type-aliases/UseAppHotkeyOptions.md b/docs/framework/vue/reference/type-aliases/UseAppHotkeyOptions.md new file mode 100644 index 00000000..0fe6ce72 --- /dev/null +++ b/docs/framework/vue/reference/type-aliases/UseAppHotkeyOptions.md @@ -0,0 +1,18 @@ +--- +id: UseAppHotkeyOptions +title: UseAppHotkeyOptions +--- + +```ts +type UseAppHotkeyOptions = WithReactiveEnabled>; +``` + +Defined in: [useHotkey.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L55) + +Call-site options for an app hotkey, typed by its declared default kind. + +## Type Parameters + +### T + +`T` *extends* `AppHotkey` = `AppHotkey` diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeyChordOptions.md b/docs/framework/vue/reference/type-aliases/UseHotkeyChordOptions.md new file mode 100644 index 00000000..cac8d354 --- /dev/null +++ b/docs/framework/vue/reference/type-aliases/UseHotkeyChordOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeyChordOptions +title: UseHotkeyChordOptions +--- + +```ts +type UseHotkeyChordOptions = WithReactiveEnabled>; +``` + +Defined in: [useHotkey.ts:36](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L36) + +Options for a chord such as `'Mod+S'`. diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeyCommonOptions.md b/docs/framework/vue/reference/type-aliases/UseHotkeyCommonOptions.md new file mode 100644 index 00000000..08b4d665 --- /dev/null +++ b/docs/framework/vue/reference/type-aliases/UseHotkeyCommonOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeyCommonOptions +title: UseHotkeyCommonOptions +--- + +```ts +type UseHotkeyCommonOptions = WithReactiveEnabled>; +``` + +Defined in: [useHotkey.ts:46](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L46) + +Options applied to every hotkey, such as provider defaults. Each applies to the kinds that support it. diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeyDefinition.md b/docs/framework/vue/reference/type-aliases/UseHotkeyDefinition.md new file mode 100644 index 00000000..98663b10 --- /dev/null +++ b/docs/framework/vue/reference/type-aliases/UseHotkeyDefinition.md @@ -0,0 +1,166 @@ +--- +id: UseHotkeyDefinition +title: UseHotkeyDefinition +--- + +```ts +type UseHotkeyDefinition = + | { + callback: HotkeyCallback; + hotkey: MaybeRefOrGetter; + options?: MaybeRefOrGetter; +} + | { + callback: HotkeyCallback; + hotkey: MaybeRefOrGetter; + options?: MaybeRefOrGetter; +} + | { + callback: HotkeyCallback; + hotkey: MaybeRefOrGetter; + options?: MaybeRefOrGetter; +} + | { + callback: HotkeyCallback; + hotkey: MaybeRefOrGetter; + options?: MaybeRefOrGetter; +}; +``` + +Defined in: [useHotkeys.ts:36](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkeys.ts#L36) + +One hotkey for `useHotkeys`. The hotkey and options may be refs or getters. +Chords accept `requireReset`; sequences accept `timeout`; hotkeys of unknown +kind accept only shared options. + +## Union Members + +### Type Literal + +```ts +{ + callback: HotkeyCallback; + hotkey: MaybeRefOrGetter; + options?: MaybeRefOrGetter; +} +``` + +#### callback + +```ts +callback: HotkeyCallback; +``` + +The function to call when the hotkey is pressed + +#### hotkey + +```ts +hotkey: MaybeRefOrGetter; +``` + +A chord string such as `'Mod+S'`, or a RawHotkey object + +#### options? + +```ts +optional options?: MaybeRefOrGetter; +``` + +Per-hotkey options (merged on top of commonOptions) + +*** + +### Type Literal + +```ts +{ + callback: HotkeyCallback; + hotkey: MaybeRefOrGetter; + options?: MaybeRefOrGetter; +} +``` + +#### callback + +```ts +callback: HotkeyCallback; +``` + +#### hotkey + +```ts +hotkey: MaybeRefOrGetter; +``` + +A sequence such as `['G', 'G']` + +#### options? + +```ts +optional options?: MaybeRefOrGetter; +``` + +*** + +### Type Literal + +```ts +{ + callback: HotkeyCallback; + hotkey: MaybeRefOrGetter; + options?: MaybeRefOrGetter; +} +``` + +#### callback + +```ts +callback: HotkeyCallback; +``` + +#### hotkey + +```ts +hotkey: MaybeRefOrGetter; +``` + +An app hotkey from `defineAppHotkeys` + +#### options? + +```ts +optional options?: MaybeRefOrGetter; +``` + +*** + +### Type Literal + +```ts +{ + callback: HotkeyCallback; + hotkey: MaybeRefOrGetter; + options?: MaybeRefOrGetter; +} +``` + +#### callback + +```ts +callback: HotkeyCallback; +``` + +#### hotkey + +```ts +hotkey: MaybeRefOrGetter; +``` + +A hotkey whose kind is not known statically. Accepts only shared options. + +#### options? + +```ts +optional options?: MaybeRefOrGetter; +``` diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeyOptions.md b/docs/framework/vue/reference/type-aliases/UseHotkeyOptions.md new file mode 100644 index 00000000..8286af72 --- /dev/null +++ b/docs/framework/vue/reference/type-aliases/UseHotkeyOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeyOptions +title: UseHotkeyOptions +--- + +```ts +type UseHotkeyOptions = WithReactiveEnabled>; +``` + +Defined in: [useHotkey.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L31) + +Options shared by chords and sequences. diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeyOptionsFor.md b/docs/framework/vue/reference/type-aliases/UseHotkeyOptionsFor.md new file mode 100644 index 00000000..a9eb279f --- /dev/null +++ b/docs/framework/vue/reference/type-aliases/UseHotkeyOptionsFor.md @@ -0,0 +1,18 @@ +--- +id: UseHotkeyOptionsFor +title: UseHotkeyOptionsFor +--- + +```ts +type UseHotkeyOptionsFor = WithReactiveEnabled>; +``` + +Defined in: [useHotkey.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L51) + +The options type for a hotkey: chord options for chords, sequence options for sequences. + +## Type Parameters + +### T + +`T` *extends* `RegisterableHotkey` diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeySequenceOptions.md b/docs/framework/vue/reference/type-aliases/UseHotkeySequenceOptions.md new file mode 100644 index 00000000..a3ff5388 --- /dev/null +++ b/docs/framework/vue/reference/type-aliases/UseHotkeySequenceOptions.md @@ -0,0 +1,12 @@ +--- +id: UseHotkeySequenceOptions +title: UseHotkeySequenceOptions +--- + +```ts +type UseHotkeySequenceOptions = WithReactiveEnabled>; +``` + +Defined in: [useHotkey.ts:41](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L41) + +Options for a sequence such as `['G', 'G']`. diff --git a/docs/framework/vue/reference/type-aliases/UseHotkeyTarget.md b/docs/framework/vue/reference/type-aliases/UseHotkeyTarget.md new file mode 100644 index 00000000..c987db81 --- /dev/null +++ b/docs/framework/vue/reference/type-aliases/UseHotkeyTarget.md @@ -0,0 +1,13 @@ +--- +id: UseHotkeyTarget +title: UseHotkeyTarget +--- + +```ts +type UseHotkeyTarget = MaybeRefOrGetter; +``` + +Defined in: [useHotkey.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/useHotkey.ts#L22) + +The element to listen on: a ref, getter, element, or null. A target that +resolves to `null` skips registration until it is set. Defaults to document. diff --git a/docs/framework/vue/reference/variables/HotkeysProvider.md b/docs/framework/vue/reference/variables/HotkeysProvider.md index 4468f772..27e2668f 100644 --- a/docs/framework/vue/reference/variables/HotkeysProvider.md +++ b/docs/framework/vue/reference/variables/HotkeysProvider.md @@ -5,9 +5,19 @@ title: HotkeysProvider ```ts const HotkeysProvider: DefineComponent>>>; + }; + customHotkeys: { + default: undefined; + type: PropType< + | CustomHotkeys>>> + | null>; + }; defaultOptions: { default: undefined; - type: () => HotkeysProviderOptions; + type: PropType; }; }>, () => | VNode true; }, string, PublicProps, ToResolvedProps>>>; + }; + customHotkeys: { + default: undefined; + type: PropType< + | CustomHotkeys>>> + | null>; + }; defaultOptions: { default: undefined; - type: () => HotkeysProviderOptions; + type: PropType; }; }>, { + customHotkeysChange: (_customHotkeys) => true; }>, { + appHotkeys: Readonly>>; + customHotkeys: | CustomHotkeys>>> + | null; defaultOptions: HotkeysProviderOptions; }, { }, { @@ -32,15 +57,21 @@ const HotkeysProvider: DefineComponent; ``` -Defined in: [HotkeysProvider.tsx:17](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProvider.tsx#L17) +Defined in: [HotkeysProvider.tsx:25](https://github.com/TanStack/hotkeys/blob/main/packages/vue-hotkeys/src/HotkeysProvider.tsx#L25) -Vue component that provides default options for hotkeys context. +Vue component that provides default options, app hotkeys, and the user's +custom hotkeys. `customHotkeys` is read once; listen to +`custom-hotkeys-change` to save changes. ## Example ```vue diff --git a/docs/overview.md b/docs/overview.md index 61779a19..a046e567 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -3,70 +3,66 @@ title: Overview id: overview --- -TanStack Hotkeys is a type-safe, headless library for keyboard shortcuts, sequences, recording, and key state tracking. Use its framework adapters for registration and cleanup, and build shortcut interfaces with your own components and application state. +TanStack Hotkeys is a type-safe, framework-agnostic library for keyboard shortcuts. It includes: -## Runtime and package requirements +- **Hotkey registration** for shortcuts like `Mod+S` and Vim-style sequences like `G G` +- **App hotkeys** for global cheat-sheets and user-customizable shortcuts +- **Hotkey recording** for shortcut settings screens +- **Key state tracking** for held keys and modifier hints +- **Display formatting** for platform-appropriate labels like `⌘ S` and `Ctrl+S` +- **Devtools** for inspecting shortcuts while you develop -Hotkeys packages ship ES2022 JavaScript as ESM only and require Node.js 20 or newer when used in Node.js, except the Octane adapter, which requires Node.js 22.22.2 or newer. Browser applications need an ES2022-compatible runtime or a build pipeline that transforms the library for their supported browsers. +> [!IMPORTANT] +> TanStack Hotkeys is currently in **alpha** and its API is still subject to change. Early adopters are encouraged to help us solve edge cases across multiple keyboard layouts, locales, and operating systems. -Use ESM imports, such as `import { parseHotkey } from '@tanstack/hotkeys'`. CommonJS builds and `require` export conditions are no longer provided. CommonJS applications can use dynamic `import()` or migrate their consuming modules to ESM. +## Motivation -Published packages contain the runtime files and TypeScript declarations in `dist`, plus package metadata, README, and license files. The repository's `src` directory and source maps are no longer included. Inspect `dist/index.d.ts` and its referenced declarations for the installed API; consult the matching repository version when you need the original implementation. +On the surface, keyboard shortcuts are a simple concept, and you would think that it should just take a couple of lines of code to implement them. And sometimes, it can be that simple. However, there are enough small "gotchas" that can eventually add up to an annoying amount of complexity when you need to consider multiple keyboard layouts, operating systems, custom shortcuts, conflicting hotkey scopes, properly ignoring input elements, and more. Browser keyboard events have their own edge cases too: key repeat, IME composition, and keys that look stuck after a missed key-up or a window switch. -## Choose what a shortcut follows +As an app grows, its shortcuts also end up scattered across components. Cheat sheets and settings screens are maintained by hand, drift out of sync with the actual hotkeys, and miss shortcuts on pages that haven't loaded yet. -A binding can follow a logical character or a physical keyboard position. Both forms use the same registration APIs: +Surprisingly, in our experience, even AI often struggles to get hotkey management fully correct. We believe that providing a library that brings type-safety and well thought out cross-platform compatibility to hotkey management is a valuable contribution to the community. -```ts -// React; the other adapters accept the same binding forms. -useHotkey('Mod+S', save) // Logical S on the active layout -useHotkey('Alt+[KeyW]', moveForward) // Physical KeyW position -useHotkey({ code: 'NumpadAdd', mod: true }, zoomIn) -``` +## Features -`Mod` means Command on macOS and Control on Windows/Linux. Brackets identify physical `event.code` names in strings, including `[Enter]` and `[F13]`. Object bindings use either `key` or `code`, never both. Both logical names and physical codes have type-safe autocomplete. +### Hotkey registration -Logical bindings prefer `event.key`. ASCII letter output remains authoritative on layouts such as Dvorak and AZERTY; conservative code fallback helps when Option, dead keys, or non-Latin output transforms the character. Physical bindings match `event.code` exactly. Among eligible registrations on a target, exact matches take priority over weaker fallbacks. +Register shortcuts such as `Mod+S` to save a document or `Escape` to close a dialog. `HotkeyManager` handles event listeners, conflict detection, and input filtering. Shortcuts can be enabled conditionally or scoped to a specific element, so a dialog's shortcuts can follow the dialog's lifetime. -Logical and physical bindings both support F1–F24 and shared named keys such as `CapsLock`, `MediaPlayPause`, and `BrowserBack`. Some browser names differ: logical `LaunchApplication1` corresponds to physical `[LaunchApp1]`. Logical `Enter` includes numpad Enter, while `[Enter]` and `[NumpadEnter]` distinguish their positions. +By default, TanStack Hotkeys prevents the browser's default action, stops event propagation, and filters shortcuts in input elements. These options are configurable when your application needs different behavior. -## Register actions and sequences +### Multi-key sequences -Single shortcuts and sequence steps use the same binding syntax. For example, `['G', 'G']` follows the logical letter, while `['[KeyG]', '[KeyG]']` follows a position. Sequences may mix logical and physical steps. Modifier-only events, IME composition, and automatic repeats do not advance sequences or extend their timeout. +Some shortcuts are a series of keystrokes instead of a single combination. Pass an array of chords anywhere a hotkey is accepted to register a Vim-style sequence such as `['G', 'G']` or `['D', 'I', 'W']`, with a configurable timeout between steps. Use sequences for navigation or commands when you want more shortcuts without adding more modifier keys. -Registrations support enabled state, element targets, input handling, callback metadata, and conflict policies. Framework adapters update bindings from normal reactive state and remove them on unmount. `meta.name`, `meta.description`, and `meta.group` describe registrations for menus and help panels; they do not change matching or scope. +### App hotkeys -## Record a replacement binding +Hotkeys registered by components only exist while those components are mounted. Declare every hotkey up front with `defineAppHotkeys` and pass the declarations to your framework's provider to list them in cheat sheets and settings screens, even on routes that have not loaded. Users can rebind or unbind any of them, conflicts are checked across every declaration, and your app decides where to save their custom hotkeys. -Recorders default to `recordBy: 'code'`. On macOS, Option+S producing `ß` records `Alt+[KeyS]`, so replay follows the same physical combination. Set `recordBy: 'key'` to intentionally record the produced logical character. The recorder never silently switches between these modes. +### Hotkey recording -Pass recorded strings directly into registrations and store them in ordinary application state. Both single and sequence recorders support validation, structured rejection, and live-registry conflict checks. Rejected candidates leave recording active. Clearing calls only `onClear`; your app decides whether that removes a binding or restores an initial value. +`HotkeyRecorder` captures a key combination so users can customize shortcuts in a settings UI. Set `maxSteps` to let users record a sequence too. It supports validation and conflict checks before you save a hotkey. You can register the recorded hotkey directly, store it in your application's state, or save it as an app hotkey's custom hotkey. -## Display shortcuts and hints +### Key state tracking -Use `formatForDisplay` at render time. Both `Mod+S` and `Mod+[KeyS]` display as `⌘ S` on macOS, but their stored identities remain distinct. `parts: true` returns individual keycap labels, and symbols can be configured separately for modifiers and keys. +`KeyStateTracker` tracks held keys and modifiers. Framework APIs such as `useHeldKeys`, `useHeldKeyCodes`, and `useKeyHold` let you respond while a key is down. Use them for keyboard controls, visual feedback, or hints that appear while a modifier is held. -Physical labels use readable defaults such as `S`, `2`, and numpad labels. For a known layout, pass an already-resolved `layoutMap`; use `keyLabels` for explicit overrides. Formatting stays synchronous and never loads a keyboard layout. Labels do not change registration or recording behavior. +### Type-safe, cross-platform bindings -Key-state primitives expose held logical keys and physical codes. `matchesHeldModifiers` and framework hint helpers reveal relevant shortcuts while modifiers are held. Live registration views and devtools expose current shortcuts and sequence progress, making it possible to build grouped help panels without a separate action catalog. +TypeScript autocomplete helps you write valid shortcut strings such as `Control+A`, `Alt+S`, or `Mod+Shift+G`. You can also use a `RawHotkey` object, such as `{ key: 'S', mod: true }`. -## Inspect a binding +`Mod` resolves to Command on macOS and Control on Windows and Linux, so the same binding works across operating systems. Logical shortcuts follow the character that was pressed, while physical shortcuts follow a keyboard position. This lets you choose between a letter-based command and a position-based control, such as movement in a game. -`ParsedHotkey` preserves identity as a union: logical bindings have `key`, physical bindings have `code`. Narrow with `parsed.code !== undefined` before reading it. Shared resolved flags and the ordered modifier list live in `ParsedModifiers`. `parseKeyboardEvent` produces logical identity; code recording constructs physical identity explicitly. +### Display formatting -## Framework guides +Format chords and sequences for menus, buttons, and cheat sheets with platform-appropriate labels, such as `⌘ S` on macOS and `Ctrl+S` on Windows. You can display a shortcut as text or individual keycaps while keeping its stored hotkey separate from its label. -| Framework | Start here | -| --- | --- | -| Alpine | [Quick start](./framework/alpine/quick-start) | -| Angular | [Quick start](./framework/angular/quick-start) | -| Ember | [Quick start](./framework/ember/quick-start) | -| Lit | [Quick start](./framework/lit/quick-start) | -| Octane | [Quick start](./framework/octane/quick-start) | -| Preact | [Hotkeys guide](./framework/preact/guides/hotkeys) | -| React | [Quick start](./framework/react/quick-start) | -| Solid | [Hotkeys guide](./framework/solid/guides/hotkeys) | -| Svelte | [Quick start](./framework/svelte/quick-start) | -| Vue | [Quick start](./framework/vue/quick-start) | +### Framework adapters -Explore a kitchen sink for [Alpine](./framework/alpine/examples/kitchen-sink), [Ember](./framework/ember/examples/kitchen-sink), [Octane](./framework/octane/examples/kitchen-sink), or [React](./framework/react/examples/kitchen-sink) to see route lifetimes, recording, and hints. Try the [vanilla formatter playground](./framework/vanilla/examples/formatForDisplay) for display options. +Use React and Preact hooks, Solid primitives, Angular inject APIs, Vue composables, Lit controllers and decorators, Svelte reactive APIs, Alpine scopes, Ember helpers, or Octane hooks. The adapters connect hotkey registration and key state to your framework and clean up registrations when components unmount. You can also use the core utilities directly in vanilla JavaScript. + +### Devtools + +See registered hotkeys, declared app hotkeys, held keys, and sequence progress in real time. The [devtools](./devtools) help you inspect which shortcuts are active and how they are configured while developing your application. + +For a complete walkthrough, see the quick start for [React](framework/react/quick-start), [Preact](framework/preact/quick-start), [Solid](framework/solid/quick-start), [Angular](framework/angular/quick-start), [Vue](framework/vue/quick-start), [Svelte](framework/svelte/quick-start), [Lit](framework/lit/quick-start), [Alpine](framework/alpine/quick-start), [Ember](framework/ember/quick-start), or [Octane](framework/octane/quick-start). diff --git a/docs/reference/adapter/functions/createHotkeyBindings.md b/docs/reference/adapter/functions/createHotkeyBindings.md index bb0ba927..4fdf78d1 100644 --- a/docs/reference/adapter/functions/createHotkeyBindings.md +++ b/docs/reference/adapter/functions/createHotkeyBindings.md @@ -7,18 +7,16 @@ title: createHotkeyBindings function createHotkeyBindings(): object; ``` -Defined in: [adapter.ts:107](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L107) +Defined in: [adapter.ts:53](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L53) Reconciles adapter registrations while retaining handles for option updates. ## Returns -`object` - -### destroy +### destroy() ```ts -destroy: () => void = bindings.destroy; +destroy(): void; ``` #### Returns @@ -31,15 +29,18 @@ destroy: () => void = bindings.destroy; update(definitions, commonOptions?): void; ``` +Registers new definitions, updates existing ones in place, and removes +stale ones. Definitions with an empty sequence or a null target are skipped. + #### Parameters ##### definitions -[`HotkeyDefinition`](../interfaces/HotkeyDefinition.md)[] +readonly [`HotkeyDefinition`](../type-aliases/HotkeyDefinition.md)[] ##### commonOptions? -`HotkeyOptions` = `{}` +[`HotkeyBindingsCommonOptions`](../type-aliases/HotkeyBindingsCommonOptions.md) = `{}` #### Returns diff --git a/docs/reference/adapter/functions/createHotkeySequenceBindings.md b/docs/reference/adapter/functions/createHotkeySequenceBindings.md deleted file mode 100644 index f97db585..00000000 --- a/docs/reference/adapter/functions/createHotkeySequenceBindings.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -id: createHotkeySequenceBindings -title: createHotkeySequenceBindings ---- - -```ts -function createHotkeySequenceBindings(): object; -``` - -Defined in: [adapter.ts:143](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L143) - -Reconciles sequence bindings; empty sequences and explicit null targets are skipped. - -## Returns - -`object` - -### destroy - -```ts -destroy: () => void = bindings.destroy; -``` - -#### Returns - -`void` - -### update() - -```ts -update(definitions, commonOptions?): void; -``` - -#### Parameters - -##### definitions - -[`HotkeySequenceDefinition`](../interfaces/HotkeySequenceDefinition.md)[] - -##### commonOptions? - -`SequenceOptions` = `{}` - -#### Returns - -`void` diff --git a/docs/reference/adapter/functions/prepareHotkeyDefinitions.md b/docs/reference/adapter/functions/prepareHotkeyDefinitions.md new file mode 100644 index 00000000..a410cc1a --- /dev/null +++ b/docs/reference/adapter/functions/prepareHotkeyDefinitions.md @@ -0,0 +1,36 @@ +--- +id: prepareHotkeyDefinitions +title: prepareHotkeyDefinitions +--- + +```ts +function prepareHotkeyDefinitions(definitions, options?): HotkeyDefinition[]; +``` + +Defined in: [adapter.ts:280](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L280) + +Converts adapter definitions into `createHotkeyBindings` definitions. +Options merge in this order: defaults, app hotkey declaration, common +options, per-definition options. App hotkeys resolve their effective hotkey +from the store, and an unbound app hotkey keeps its position but registers +nothing. + +## Type Parameters + +### TTarget + +`TTarget` + +## Parameters + +### definitions + +readonly [`AdapterHotkeyDefinition`](../type-aliases/AdapterHotkeyDefinition.md)\<`TTarget`\>[] + +### options? + +[`PrepareHotkeyDefinitionsOptions`](../interfaces/PrepareHotkeyDefinitionsOptions.md)\<`TTarget`\> = `{}` + +## Returns + +[`HotkeyDefinition`](../type-aliases/HotkeyDefinition.md)[] diff --git a/docs/reference/adapter/functions/subscribePublishedAppHotkeys.md b/docs/reference/adapter/functions/subscribePublishedAppHotkeys.md new file mode 100644 index 00000000..778aa996 --- /dev/null +++ b/docs/reference/adapter/functions/subscribePublishedAppHotkeys.md @@ -0,0 +1,25 @@ +--- +id: subscribePublishedAppHotkeys +title: subscribePublishedAppHotkeys +--- + +```ts +function subscribePublishedAppHotkeys(onChange): () => void; +``` + +Defined in: [adapter.ts:319](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L319) + +Calls `onChange` when the store published with `publishAppHotkeysStore` or +`provideAppHotkeys` changes, or when its app hotkeys or custom hotkeys +change. For adapters that resolve app hotkeys without a provider. Returns an +unsubscribe function. + +## Parameters + +### onChange + +() => `void` + +## Returns + +() => `void` diff --git a/docs/reference/adapter/index.md b/docs/reference/adapter/index.md index fec56d9d..17967895 100644 --- a/docs/reference/adapter/index.md +++ b/docs/reference/adapter/index.md @@ -5,10 +5,23 @@ title: "@tanstack/hotkeys" ## Interfaces -- [HotkeyDefinition](interfaces/HotkeyDefinition.md) -- [HotkeySequenceDefinition](interfaces/HotkeySequenceDefinition.md) +- [PrepareHotkeyDefinitionsOptions](interfaces/PrepareHotkeyDefinitionsOptions.md) + +## Type Aliases + +- [AdapterAppHotkeyOptions](type-aliases/AdapterAppHotkeyOptions.md) +- [AdapterChordOptions](type-aliases/AdapterChordOptions.md) +- [AdapterCommonOptions](type-aliases/AdapterCommonOptions.md) +- [AdapterHotkeyDefinition](type-aliases/AdapterHotkeyDefinition.md) +- [AdapterHotkeyOptions](type-aliases/AdapterHotkeyOptions.md) +- [AdapterOptionsFor](type-aliases/AdapterOptionsFor.md) +- [AdapterSequenceOptions](type-aliases/AdapterSequenceOptions.md) +- [HotkeyBindingsCommonOptions](type-aliases/HotkeyBindingsCommonOptions.md) +- [HotkeyDefinition](type-aliases/HotkeyDefinition.md) +- [WithTarget](type-aliases/WithTarget.md) ## Functions - [createHotkeyBindings](functions/createHotkeyBindings.md) -- [createHotkeySequenceBindings](functions/createHotkeySequenceBindings.md) +- [prepareHotkeyDefinitions](functions/prepareHotkeyDefinitions.md) +- [subscribePublishedAppHotkeys](functions/subscribePublishedAppHotkeys.md) diff --git a/docs/reference/adapter/interfaces/HotkeyDefinition.md b/docs/reference/adapter/interfaces/HotkeyDefinition.md deleted file mode 100644 index 17cd68d5..00000000 --- a/docs/reference/adapter/interfaces/HotkeyDefinition.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -id: HotkeyDefinition -title: HotkeyDefinition ---- - -Defined in: [adapter.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L14) - -A hotkey and its callback, with optional per-binding options. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [adapter.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L16) - -*** - -### hotkey - -```ts -hotkey: RegisterableHotkey; -``` - -Defined in: [adapter.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L15) - -*** - -### options? - -```ts -optional options?: HotkeyOptions; -``` - -Defined in: [adapter.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L17) diff --git a/docs/reference/adapter/interfaces/HotkeySequenceDefinition.md b/docs/reference/adapter/interfaces/HotkeySequenceDefinition.md deleted file mode 100644 index d09fe726..00000000 --- a/docs/reference/adapter/interfaces/HotkeySequenceDefinition.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -id: HotkeySequenceDefinition -title: HotkeySequenceDefinition ---- - -Defined in: [adapter.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L21) - -A sequence and its callback, with optional per-binding options. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [adapter.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L23) - -*** - -### options? - -```ts -optional options?: SequenceOptions; -``` - -Defined in: [adapter.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L24) - -*** - -### sequence - -```ts -sequence: HotkeySequence; -``` - -Defined in: [adapter.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L22) diff --git a/docs/reference/adapter/interfaces/PrepareHotkeyDefinitionsOptions.md b/docs/reference/adapter/interfaces/PrepareHotkeyDefinitionsOptions.md new file mode 100644 index 00000000..8be14089 --- /dev/null +++ b/docs/reference/adapter/interfaces/PrepareHotkeyDefinitionsOptions.md @@ -0,0 +1,76 @@ +--- +id: PrepareHotkeyDefinitionsOptions +title: PrepareHotkeyDefinitionsOptions +--- + +Defined in: [adapter.ts:258](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L258) + +Context for `prepareHotkeyDefinitions`. + +## Type Parameters + +### TTarget + +`TTarget` + +## Properties + +### appHotkeysStore? + +```ts +optional appHotkeysStore?: + | AppHotkeysStore>>> + | null; +``` + +Defined in: [adapter.ts:260](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L260) + +The provider's app hotkeys store, so app hotkeys use custom hotkeys. + +*** + +### commonOptions? + +```ts +optional commonOptions?: AdapterCommonOptions; +``` + +Defined in: [adapter.ts:264](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L264) + +Options for every definition, applied after app hotkey declarations. + +*** + +### defaults? + +```ts +optional defaults?: AdapterCommonOptions; +``` + +Defined in: [adapter.ts:262](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L262) + +Provider defaults, applied first. + +*** + +### resolveTarget? + +```ts +optional resolveTarget?: (target) => Target | null | undefined; +``` + +Defined in: [adapter.ts:270](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L270) + +Resolves a framework target, such as a ref. Return `undefined` for the +document or `null` to skip registration until the target exists. +Without it, targets pass through and `null` means document. + +#### Parameters + +##### target + +`TTarget` + +#### Returns + +`Target` \| `null` \| `undefined` diff --git a/docs/reference/adapter/type-aliases/AdapterAppHotkeyOptions.md b/docs/reference/adapter/type-aliases/AdapterAppHotkeyOptions.md new file mode 100644 index 00000000..f48fcd23 --- /dev/null +++ b/docs/reference/adapter/type-aliases/AdapterAppHotkeyOptions.md @@ -0,0 +1,26 @@ +--- +id: AdapterAppHotkeyOptions +title: AdapterAppHotkeyOptions +--- + +```ts +type AdapterAppHotkeyOptions = Omit<[T["defaultHotkey"]] extends [HotkeySequence] ? AdapterSequenceOptions : [T["defaultHotkey"]] extends [HotkeyChord] ? AdapterChordOptions : AdapterCommonOptions, "meta">; +``` + +Defined in: [adapter.ts:209](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L209) + +Call-site options for an app hotkey. `meta` comes from the declaration. +Kind-specific options follow the declared default: `requireReset` for a +chord, `timeout` for a sequence, both for an app hotkey without a default. +If the user rebinds it to the other kind, options for the old kind no +longer apply. + +## Type Parameters + +### T + +`T` *extends* `AppHotkey` + +### TTarget + +`TTarget` diff --git a/docs/reference/adapter/type-aliases/AdapterChordOptions.md b/docs/reference/adapter/type-aliases/AdapterChordOptions.md new file mode 100644 index 00000000..a7cc7590 --- /dev/null +++ b/docs/reference/adapter/type-aliases/AdapterChordOptions.md @@ -0,0 +1,18 @@ +--- +id: AdapterChordOptions +title: AdapterChordOptions +--- + +```ts +type AdapterChordOptions = WithTarget; +``` + +Defined in: [adapter.ts:176](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L176) + +Options for a chord such as `'Mod+S'`, with a framework target. + +## Type Parameters + +### TTarget + +`TTarget` diff --git a/docs/reference/adapter/type-aliases/AdapterCommonOptions.md b/docs/reference/adapter/type-aliases/AdapterCommonOptions.md new file mode 100644 index 00000000..7f233152 --- /dev/null +++ b/docs/reference/adapter/type-aliases/AdapterCommonOptions.md @@ -0,0 +1,18 @@ +--- +id: AdapterCommonOptions +title: AdapterCommonOptions +--- + +```ts +type AdapterCommonOptions = AdapterChordOptions & AdapterSequenceOptions; +``` + +Defined in: [adapter.ts:188](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L188) + +Options applied to every hotkey, such as defaults. Each applies to the kinds that support it. + +## Type Parameters + +### TTarget + +`TTarget` diff --git a/docs/reference/adapter/type-aliases/AdapterHotkeyDefinition.md b/docs/reference/adapter/type-aliases/AdapterHotkeyDefinition.md new file mode 100644 index 00000000..84143253 --- /dev/null +++ b/docs/reference/adapter/type-aliases/AdapterHotkeyDefinition.md @@ -0,0 +1,174 @@ +--- +id: AdapterHotkeyDefinition +title: AdapterHotkeyDefinition +--- + +```ts +type AdapterHotkeyDefinition = + | { + callback: HotkeyCallback; + hotkey: RegisterableHotkeyChord; + options?: AdapterChordOptions; +} + | { + callback: HotkeyCallback; + hotkey: HotkeySequence; + options?: AdapterSequenceOptions; +} + | { + callback: HotkeyCallback; + hotkey: AppHotkey; + options?: Omit, "meta">; +} + | { + callback: HotkeyCallback; + hotkey: RegisterableHotkey; + options?: AdapterHotkeyOptions; +}; +``` + +Defined in: [adapter.ts:223](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L223) + +One hotkey and its callback for adapter APIs such as `useHotkeys`. Chords +accept `requireReset`; sequences accept `timeout`; hotkeys of unknown kind +accept only shared options. + +## Type Parameters + +### TTarget + +`TTarget` + +## Union Members + +### Type Literal + +```ts +{ + callback: HotkeyCallback; + hotkey: RegisterableHotkeyChord; + options?: AdapterChordOptions; +} +``` + +#### callback + +```ts +callback: HotkeyCallback; +``` + +The function to call when the hotkey is pressed + +#### hotkey + +```ts +hotkey: RegisterableHotkeyChord; +``` + +A chord string such as `'Mod+S'`, or a RawHotkey object + +#### options? + +```ts +optional options?: AdapterChordOptions; +``` + +Per-hotkey options (merged on top of common options) + +*** + +### Type Literal + +```ts +{ + callback: HotkeyCallback; + hotkey: HotkeySequence; + options?: AdapterSequenceOptions; +} +``` + +#### callback + +```ts +callback: HotkeyCallback; +``` + +#### hotkey + +```ts +hotkey: HotkeySequence; +``` + +A sequence such as `['G', 'G']` + +#### options? + +```ts +optional options?: AdapterSequenceOptions; +``` + +*** + +### Type Literal + +```ts +{ + callback: HotkeyCallback; + hotkey: AppHotkey; + options?: Omit, "meta">; +} +``` + +#### callback + +```ts +callback: HotkeyCallback; +``` + +#### hotkey + +```ts +hotkey: AppHotkey; +``` + +An app hotkey from `defineAppHotkeys`. Kind-specific options apply +only while its effective hotkey has that kind. + +#### options? + +```ts +optional options?: Omit, "meta">; +``` + +*** + +### Type Literal + +```ts +{ + callback: HotkeyCallback; + hotkey: RegisterableHotkey; + options?: AdapterHotkeyOptions; +} +``` + +#### callback + +```ts +callback: HotkeyCallback; +``` + +#### hotkey + +```ts +hotkey: RegisterableHotkey; +``` + +A hotkey whose kind is not known statically, such as user data typed +`Hotkey`. Accepts only shared options. + +#### options? + +```ts +optional options?: AdapterHotkeyOptions; +``` diff --git a/docs/reference/adapter/type-aliases/AdapterHotkeyOptions.md b/docs/reference/adapter/type-aliases/AdapterHotkeyOptions.md new file mode 100644 index 00000000..9a329287 --- /dev/null +++ b/docs/reference/adapter/type-aliases/AdapterHotkeyOptions.md @@ -0,0 +1,18 @@ +--- +id: AdapterHotkeyOptions +title: AdapterHotkeyOptions +--- + +```ts +type AdapterHotkeyOptions = WithTarget; +``` + +Defined in: [adapter.ts:173](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L173) + +Options shared by chords and sequences, with a framework target. + +## Type Parameters + +### TTarget + +`TTarget` diff --git a/docs/reference/adapter/type-aliases/AdapterOptionsFor.md b/docs/reference/adapter/type-aliases/AdapterOptionsFor.md new file mode 100644 index 00000000..ba2d9e60 --- /dev/null +++ b/docs/reference/adapter/type-aliases/AdapterOptionsFor.md @@ -0,0 +1,22 @@ +--- +id: AdapterOptionsFor +title: AdapterOptionsFor +--- + +```ts +type AdapterOptionsFor = [T] extends [never] ? AdapterCommonOptions : T extends readonly [unknown] ? AdapterChordOptions : T extends ReadonlyArray ? AdapterSequenceOptions : AdapterChordOptions; +``` + +Defined in: [adapter.ts:192](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L192) + +Chord options for chords, sequence options for sequences. + +## Type Parameters + +### T + +`T` *extends* `RegisterableHotkey` + +### TTarget + +`TTarget` diff --git a/docs/reference/adapter/type-aliases/AdapterSequenceOptions.md b/docs/reference/adapter/type-aliases/AdapterSequenceOptions.md new file mode 100644 index 00000000..275050f8 --- /dev/null +++ b/docs/reference/adapter/type-aliases/AdapterSequenceOptions.md @@ -0,0 +1,18 @@ +--- +id: AdapterSequenceOptions +title: AdapterSequenceOptions +--- + +```ts +type AdapterSequenceOptions = WithTarget; +``` + +Defined in: [adapter.ts:182](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L182) + +Options for a sequence such as `['G', 'G']`, with a framework target. + +## Type Parameters + +### TTarget + +`TTarget` diff --git a/docs/reference/adapter/type-aliases/HotkeyBindingsCommonOptions.md b/docs/reference/adapter/type-aliases/HotkeyBindingsCommonOptions.md new file mode 100644 index 00000000..f659186d --- /dev/null +++ b/docs/reference/adapter/type-aliases/HotkeyBindingsCommonOptions.md @@ -0,0 +1,12 @@ +--- +id: HotkeyBindingsCommonOptions +title: HotkeyBindingsCommonOptions +--- + +```ts +type HotkeyBindingsCommonOptions = HotkeyChordOptions & HotkeySequenceOptions; +``` + +Defined in: [adapter.ts:47](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L47) + +Options shared by every definition. Each option applies only to the kinds that support it. diff --git a/docs/reference/adapter/type-aliases/HotkeyDefinition.md b/docs/reference/adapter/type-aliases/HotkeyDefinition.md new file mode 100644 index 00000000..c3f5bf94 --- /dev/null +++ b/docs/reference/adapter/type-aliases/HotkeyDefinition.md @@ -0,0 +1,23 @@ +--- +id: HotkeyDefinition +title: HotkeyDefinition +--- + +```ts +type HotkeyDefinition = + | { + callback: HotkeyCallback; + hotkey: RegisterableHotkeyChord; + options?: HotkeyChordOptions; +} + | { + callback: HotkeyCallback; + hotkey: HotkeySequence; + options?: HotkeySequenceOptions; +}; +``` + +Defined in: [adapter.ts:34](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L34) + +A hotkey and its callback, with optional per-binding options. Chords accept +`requireReset`; sequences accept `timeout`. diff --git a/docs/reference/adapter/type-aliases/WithTarget.md b/docs/reference/adapter/type-aliases/WithTarget.md new file mode 100644 index 00000000..d1b87c21 --- /dev/null +++ b/docs/reference/adapter/type-aliases/WithTarget.md @@ -0,0 +1,32 @@ +--- +id: WithTarget +title: WithTarget +--- + +```ts +type WithTarget = Omit & object; +``` + +Defined in: [adapter.ts:164](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/adapter.ts#L164) + +Options with a framework-specific target, such as a ref. + +## Type Declaration + +### target? + +```ts +optional target?: TTarget; +``` + +The element to listen on. Defaults to document. + +## Type Parameters + +### TOptions + +`TOptions` + +### TTarget + +`TTarget` diff --git a/docs/reference/classes/AppHotkeysStore.md b/docs/reference/classes/AppHotkeysStore.md new file mode 100644 index 00000000..04a4720c --- /dev/null +++ b/docs/reference/classes/AppHotkeysStore.md @@ -0,0 +1,303 @@ +--- +id: AppHotkeysStore +title: AppHotkeysStore +--- + +Defined in: app-hotkeys.ts:349 + +Holds declared app hotkeys and the user's custom hotkeys. Framework +providers create one and expose it through hooks; devtools read it from +`HotkeyManager.appHotkeys`. + +## Example + +```ts +const store = new AppHotkeysStore(appHotkeys, { + customHotkeys: JSON.parse(localStorage.getItem('hotkeys') ?? '{}'), + onCustomHotkeysChange: (custom) => + localStorage.setItem('hotkeys', JSON.stringify(custom)), +}) +store.setCustomHotkey('doc.save', 'Mod+Shift+S') +``` + +## Type Parameters + +### T + +`T` *extends* [`AppHotkeys`](../type-aliases/AppHotkeys.md) = [`AppHotkeys`](../type-aliases/AppHotkeys.md) + +## Constructors + +### Constructor + +```ts +new AppHotkeysStore(appHotkeys, options?): AppHotkeysStore; +``` + +Defined in: app-hotkeys.ts:354 + +#### Parameters + +##### appHotkeys + +`T` + +##### options? + +[`AppHotkeysStoreOptions`](../interfaces/AppHotkeysStoreOptions.md)\<`T`\> = `{}` + +#### Returns + +`AppHotkeysStore`\<`T`\> + +## Properties + +### store + +```ts +readonly store: Store>; +``` + +Defined in: app-hotkeys.ts:350 + +## Accessors + +### appHotkeys + +#### Get Signature + +```ts +get appHotkeys(): T; +``` + +Defined in: app-hotkeys.ts:372 + +Effective app hotkeys. + +##### Returns + +`T` + +*** + +### customHotkeys + +#### Get Signature + +```ts +get customHotkeys(): CustomHotkeys; +``` + +Defined in: app-hotkeys.ts:377 + +The user's hotkeys that differ from the defaults. + +##### Returns + +[`CustomHotkeys`](../type-aliases/CustomHotkeys.md)\<`T`\> + +## Methods + +### findConflicts() + +```ts +findConflicts(id, hotkey): AppHotkey[]; +``` + +Defined in: app-hotkeys.ts:413 + +Other app hotkeys that would collide if `id` used `hotkey`. App hotkeys +collide when their contexts match or either has no context, and their +hotkeys are equal or one sequence starts with the other. + +#### Parameters + +##### id + +keyof `T` & `string` + +##### hotkey + +[`Hotkey`](../type-aliases/Hotkey.md) + +#### Returns + +[`AppHotkey`](../interfaces/AppHotkey.md)\<`string`, + \| [`HotkeyChord`](../type-aliases/HotkeyChord.md) + \| [`HotkeySequence`](../type-aliases/HotkeySequence.md) + \| `null`\>[] + +*** + +### get() + +```ts +get(id): + | AppHotkey + | undefined; +``` + +Defined in: app-hotkeys.ts:404 + +Effective app hotkey for an id, or `undefined` when it is not declared. + +#### Parameters + +##### id + +`string` + +#### Returns + + \| [`AppHotkey`](../interfaces/AppHotkey.md)\<`string`, + \| [`HotkeyChord`](../type-aliases/HotkeyChord.md) + \| [`HotkeySequence`](../type-aliases/HotkeySequence.md) + \| `null`\> + \| `undefined` + +*** + +### getList() + +```ts +getList(registrations): AppHotkeyListItem[]; +``` + +Defined in: app-hotkeys.ts:480 + +Joins app hotkeys with live registrations, in declaration order. +Adapters call this with the manager's registration views. + +#### Parameters + +##### registrations + +`Iterable`\<[`HotkeyRegistrationView`](../type-aliases/HotkeyRegistrationView.md)\> + +#### Returns + +[`AppHotkeyListItem`](../interfaces/AppHotkeyListItem.md)\<`T`\[keyof `T`\]\>[] + +*** + +### resetCustomHotkey() + +```ts +resetCustomHotkey(id): void; +``` + +Defined in: app-hotkeys.ts:457 + +Restores the default hotkey for an app hotkey. + +#### Parameters + +##### id + +keyof `T` & `string` + +#### Returns + +`void` + +*** + +### resetCustomHotkeys() + +```ts +resetCustomHotkeys(): void; +``` + +Defined in: app-hotkeys.ts:464 + +Restores every default hotkey. + +#### Returns + +`void` + +*** + +### setAppHotkeys() + +```ts +setAppHotkeys(appHotkeys): void; +``` + +Defined in: app-hotkeys.ts:390 + +Replaces the declarations, for example after hot module replacement. +Custom hotkeys for ids that still exist are kept. + +#### Parameters + +##### appHotkeys + +`T` + +#### Returns + +`void` + +*** + +### setCustomHotkey() + +```ts +setCustomHotkey(id, hotkey): AppHotkey[]; +``` + +Defined in: app-hotkeys.ts:432 + +Sets the user's hotkey for an app hotkey. `null` unbinds it. A hotkey +equal to the default removes the customization. + +#### Parameters + +##### id + +keyof `T` & `string` + +##### hotkey + +[`Hotkey`](../type-aliases/Hotkey.md) \| `null` + +#### Returns + +[`AppHotkey`](../interfaces/AppHotkey.md)\<`string`, + \| [`HotkeyChord`](../type-aliases/HotkeyChord.md) + \| [`HotkeySequence`](../type-aliases/HotkeySequence.md) + \| `null`\>[] + +Conflicting app hotkeys. When any exist, nothing changes. + +*** + +### setOptions() + +```ts +setOptions(options): void; +``` + +Defined in: app-hotkeys.ts:382 + +Updates callbacks and other options without resetting custom hotkeys. + +#### Parameters + +##### options + +`Omit`\<[`AppHotkeysStoreOptions`](../interfaces/AppHotkeysStoreOptions.md)\<`T`\>, `"customHotkeys"`\> + +#### Returns + +`void` diff --git a/docs/reference/classes/HotkeyManager.md b/docs/reference/classes/HotkeyManager.md index 23a15316..b05beabb 100644 --- a/docs/reference/classes/HotkeyManager.md +++ b/docs/reference/classes/HotkeyManager.md @@ -3,38 +3,63 @@ id: HotkeyManager title: HotkeyManager --- -Defined in: [hotkey-manager.ts:191](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L191) +Defined in: [hotkey-manager.ts:311](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L311) -Singleton manager for hotkey registrations. +Singleton manager for hotkey registrations, including sequences. -This class provides a centralized way to register and manage keyboard hotkeys. -It uses a single event listener for efficiency, regardless of how many hotkeys -are registered. +This class provides a centralized way to register and manage keyboard +hotkeys. It attaches one keydown and one keyup listener per target, +regardless of how many hotkeys are registered. + +Sequences (Vim-style hotkeys such as `['G', 'G']`) match when every step is +pressed within the timeout. Modifier-only events (Shift, Control, Alt, or +Meta alone) neither advance nor reset a sequence, so chained modifier chords +such as `['Shift+R', 'Shift+T']` work. ## Example ```ts const manager = HotkeyManager.getInstance() -const unregister = manager.register('Mod+S', (event, context) => { +const save = manager.register('Mod+S', (event, context) => { console.log('Save triggered!') }) +const top = manager.register(['G', 'G'], () => scrollToTop(), { timeout: 500 }) // Later, to unregister: -unregister() +save.unregister() +top.unregister() ``` ## Properties +### appHotkeys + +```ts +readonly appHotkeys: Store< + | AppHotkeysStore>>> +| null>; +``` + +Defined in: [hotkey-manager.ts:340](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L340) + +The app hotkeys store published by a framework provider, so tools such as +devtools can list declared hotkeys that are not registered. + +*** + ### registrations ```ts readonly registrations: Store>; ``` -Defined in: [hotkey-manager.ts:213](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L213) +Defined in: [hotkey-manager.ts:333](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L333) -The TanStack Store containing all hotkey registrations. +The TanStack Store containing all hotkey registrations, chords and sequences. Use this to subscribe to registration changes or access current registrations. #### Example @@ -61,7 +86,7 @@ for (const [id, reg] of manager.registrations.state) { destroy(): void; ``` -Defined in: [hotkey-manager.ts:796](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L796) +Defined in: [hotkey-manager.ts:1042](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L1042) Destroys the manager and removes all listeners. @@ -77,9 +102,9 @@ Destroys the manager and removes all listeners. getRegistrationCount(): number; ``` -Defined in: [hotkey-manager.ts:761](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L761) +Defined in: [hotkey-manager.ts:1012](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L1012) -Gets the number of registered hotkeys. +Gets the number of registered hotkeys, chords and sequences. #### Returns @@ -93,7 +118,7 @@ Gets the number of registered hotkeys. isRegistered(hotkey, target?): boolean; ``` -Defined in: [hotkey-manager.ts:772](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L772) +Defined in: [hotkey-manager.ts:1023](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L1023) Checks if a specific hotkey is registered. @@ -101,13 +126,13 @@ Checks if a specific hotkey is registered. ##### hotkey -[`Hotkey`](../type-aliases/Hotkey.md) +[`RegisterableHotkey`](../type-aliases/RegisterableHotkey.md) -The hotkey string to check +The chord or sequence to check ##### target? -`Document` \| `Window` \| `HTMLElement` +[`Target`](../type-aliases/Target.md) Optional target element to match (if provided, both hotkey and target must match) @@ -122,26 +147,36 @@ True if a matching registration exists ### register() ```ts -register( +register( hotkey, callback, - options?): HotkeyRegistrationHandle; +options?): HotkeyRegistrationHandle>; ``` -Defined in: [hotkey-manager.ts:276](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L276) +Defined in: [hotkey-manager.ts:410](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L410) Registers a hotkey handler and returns a handle for updating the registration. +Pass a chord (`'Mod+S'` or a RawHotkey object) or a sequence (`['G', 'G']`). +A one-step sequence registers as its chord. Chords accept `requireReset`; +sequences accept `timeout`. + The returned handle allows updating the callback and options without re-registering, which is useful for avoiding stale closures in React. +#### Type Parameters + +##### T + +`T` *extends* [`RegisterableHotkey`](../type-aliases/RegisterableHotkey.md) + #### Parameters ##### hotkey -[`RegisterableHotkey`](../type-aliases/RegisterableHotkey.md) +`T` -The hotkey string (e.g., 'Mod+S') or RawHotkey object +A chord string, RawHotkey object, or sequence ##### callback @@ -151,13 +186,13 @@ The function to call when the hotkey is pressed ##### options? -[`HotkeyOptions`](../interfaces/HotkeyOptions.md) = `{}` +[`HotkeyOptionsFor`](../type-aliases/HotkeyOptionsFor.md)\<`T`\> Options for the hotkey behavior #### Returns -[`HotkeyRegistrationHandle`](../interfaces/HotkeyRegistrationHandle.md) +[`HotkeyRegistrationHandle`](../interfaces/HotkeyRegistrationHandle.md)\<[`HotkeyOptionsFor`](../type-aliases/HotkeyOptionsFor.md)\<`T`\>\> A handle for managing the registration @@ -178,16 +213,33 @@ handle.unregister() *** +### resetSequenceProgress() + +```ts +resetSequenceProgress(): void; +``` + +Defined in: [hotkey-manager.ts:957](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L957) + +Resets the progress of every in-progress sequence. + +#### Returns + +`void` + +*** + ### triggerRegistration() ```ts triggerRegistration(id): boolean; ``` -Defined in: [hotkey-manager.ts:723](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L723) +Defined in: [hotkey-manager.ts:975](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L975) Triggers a registration's callback programmatically from devtools. -Creates a synthetic KeyboardEvent and invokes the callback. +Creates a synthetic KeyboardEvent from the chord (or a sequence's last step) +and invokes the callback. #### Parameters @@ -211,7 +263,7 @@ True if the registration was found and triggered static getInstance(): HotkeyManager; ``` -Defined in: [hotkey-manager.ts:234](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L234) +Defined in: [hotkey-manager.ts:359](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L359) Gets the singleton instance of HotkeyManager. @@ -227,7 +279,7 @@ Gets the singleton instance of HotkeyManager. static resetInstance(): void; ``` -Defined in: [hotkey-manager.ts:244](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L244) +Defined in: [hotkey-manager.ts:369](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L369) Resets the singleton instance. Useful for testing. diff --git a/docs/reference/classes/HotkeyRecorder.md b/docs/reference/classes/HotkeyRecorder.md index bacc0560..9c979847 100644 --- a/docs/reference/classes/HotkeyRecorder.md +++ b/docs/reference/classes/HotkeyRecorder.md @@ -3,45 +3,40 @@ id: HotkeyRecorder title: HotkeyRecorder --- -Defined in: [hotkey-recorder.ts:94](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L94) +Defined in: [hotkey-recorder.ts:144](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L144) Framework-agnostic class for recording keyboard shortcuts. -This class handles 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. +With the default `maxSteps: 1`, the first valid chord commits. With a larger +`maxSteps`, each chord adds a step; the recording commits on Enter (unless +`commitKeys: 'none'`), after `idleTimeoutMs`, or when it reaches `maxSteps`. +Backspace/Delete removes the last step, and on an empty recording calls +`onClear`. Escape cancels. A one-step result is a chord string. State Management: - Uses TanStack Store for reactive state management - State can be accessed via `recorder.store.state` when using the class directly -- When using framework adapters (React), use `useSelector` hooks for reactive state +- When using framework adapters, use the `useHotkeyRecorder` hook for reactive state ## Example ```ts const recorder = new HotkeyRecorder({ onRecord: (hotkey) => { - console.log('Recorded:', hotkey) - }, - onCancel: () => { - console.log('Recording cancelled') + console.log('Recorded:', hotkey) // 'Mod+S' }, }) -// Start recording -recorder.start() - -// Access state directly -console.log(recorder.store.state.isRecording) // true - -// Subscribe to changes with TanStack Store -const unsubscribe = recorder.store.subscribe(() => { - console.log('Recording:', recorder.store.state.isRecording) +const sequenceRecorder = new HotkeyRecorder({ + maxSteps: 3, + onRecord: (hotkey) => { + console.log('Recorded:', hotkey) // 'Mod+S' or ['G', 'H'] + }, }) -// Cleanup +recorder.start() +// ... later recorder.destroy() -unsubscribe() ``` ## Constructors @@ -52,16 +47,33 @@ unsubscribe() new HotkeyRecorder(options): HotkeyRecorder; ``` -Defined in: [hotkey-recorder.ts:119](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L119) +Defined in: [hotkey-recorder.ts:169](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L169) + +#### Parameters + +##### options -Options may be a getter to read current configuration during an active session. + \| [`HotkeyChordRecorderOptions`](../interfaces/HotkeyChordRecorderOptions.md) + \| (() => [`HotkeyChordRecorderOptions`](../interfaces/HotkeyChordRecorderOptions.md)) + +#### Returns + +`HotkeyRecorder` + +### Constructor + +```ts +new HotkeyRecorder(options): HotkeyRecorder; +``` + +Defined in: [hotkey-recorder.ts:172](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L172) #### Parameters ##### options - \| [`HotkeyRecorderOptions`](../interfaces/HotkeyRecorderOptions.md) - \| (() => [`HotkeyRecorderOptions`](../interfaces/HotkeyRecorderOptions.md)) + \| [`HotkeySequenceRecorderOptions`](../interfaces/HotkeySequenceRecorderOptions.md) + \| (() => [`HotkeySequenceRecorderOptions`](../interfaces/HotkeySequenceRecorderOptions.md)) #### Returns @@ -75,10 +87,7 @@ Options may be a getter to read current configuration during an active session. readonly store: Store; ``` -Defined in: [hotkey-recorder.ts:99](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L99) - -The TanStack Store instance containing the recorder state. -Use this to subscribe to state changes or access current state. +Defined in: [hotkey-recorder.ts:145](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L145) ## Methods @@ -88,7 +97,7 @@ Use this to subscribe to state changes or access current state. cancel(): void; ``` -Defined in: [hotkey-recorder.ts:302](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L302) +Defined in: [hotkey-recorder.ts:417](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L417) Cancel recording without saving. @@ -101,13 +110,31 @@ the onCancel callback if provided. *** +### commit() + +```ts +commit(): void; +``` + +Defined in: [hotkey-recorder.ts:326](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L326) + +Commits the recorded steps after validation and conflict checks. +Called automatically at `maxSteps`, on Enter, or after `idleTimeoutMs`. +A rejected commit leaves recording active with its steps intact. + +#### Returns + +`void` + +*** + ### destroy() ```ts destroy(): void; ``` -Defined in: [hotkey-recorder.ts:348](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L348) +Defined in: [hotkey-recorder.ts:444](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L444) Clean up event listeners and reset state. @@ -126,7 +153,7 @@ all event listeners are properly removed. setOptions(options): void; ``` -Defined in: [hotkey-recorder.ts:128](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L128) +Defined in: [hotkey-recorder.ts:185](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L185) Updates the recorder options, including callbacks. This allows framework adapters to sync callback changes without recreating the recorder. @@ -135,7 +162,7 @@ This allows framework adapters to sync callback changes without recreating the r ##### options -`Partial`\<[`HotkeyRecorderOptions`](../interfaces/HotkeyRecorderOptions.md)\> +`Partial`\<[`HotkeyRecorderOptions`](../type-aliases/HotkeyRecorderOptions.md)\> #### Returns @@ -149,13 +176,12 @@ This allows framework adapters to sync callback changes without recreating the r start(): void; ``` -Defined in: [hotkey-recorder.ts:139](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L139) +Defined in: [hotkey-recorder.ts:219](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L219) Start recording a new hotkey. Sets up a keydown event listener that captures keyboard events -and converts them to hotkey strings. Recording continues until -a valid hotkey is recorded, Escape is pressed, or stop/cancel is called. +and records chords according to `maxSteps`. #### Returns @@ -169,7 +195,7 @@ a valid hotkey is recorded, Escape is pressed, or stop/cancel is called. stop(): void; ``` -Defined in: [hotkey-recorder.ts:281](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L281) +Defined in: [hotkey-recorder.ts:407](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L407) Stop recording (same as cancel, but doesn't call onCancel). diff --git a/docs/reference/classes/HotkeySequenceRecorder.md b/docs/reference/classes/HotkeySequenceRecorder.md deleted file mode 100644 index 7b9944dc..00000000 --- a/docs/reference/classes/HotkeySequenceRecorder.md +++ /dev/null @@ -1,145 +0,0 @@ ---- -id: HotkeySequenceRecorder -title: HotkeySequenceRecorder ---- - -Defined in: [hotkey-sequence-recorder.ts:104](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L104) - -Framework-agnostic class for recording multi-chord sequences (Vim-style shortcuts). - -Each step is captured like a single hotkey chord. Press **Enter** (no modifiers) to commit -when [HotkeySequenceRecorderOptions.commitKeys](../interfaces/HotkeySequenceRecorderOptions.md#commitkeys) is `'enter'` (default), **Escape** to cancel, -**Backspace/Delete** to remove the last step or clear when empty. - -## Constructors - -### Constructor - -```ts -new HotkeySequenceRecorder(options): HotkeySequenceRecorder; -``` - -Defined in: [hotkey-sequence-recorder.ts:131](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L131) - -Options may be a getter to read current configuration during an active session. - -#### Parameters - -##### options - - \| [`HotkeySequenceRecorderOptions`](../interfaces/HotkeySequenceRecorderOptions.md) - \| (() => [`HotkeySequenceRecorderOptions`](../interfaces/HotkeySequenceRecorderOptions.md)) - -#### Returns - -`HotkeySequenceRecorder` - -## Properties - -### store - -```ts -readonly store: Store; -``` - -Defined in: [hotkey-sequence-recorder.ts:105](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L105) - -## Methods - -### cancel() - -```ts -cancel(): void; -``` - -Defined in: [hotkey-sequence-recorder.ts:379](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L379) - -Stops, discards in-progress steps, and notifies onCancel. - -#### Returns - -`void` - -*** - -### commit() - -```ts -commit(): void; -``` - -Defined in: [hotkey-sequence-recorder.ts:302](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L302) - -Commit the current steps as a sequence. No-op if fewer than one step. - -#### Returns - -`void` - -*** - -### destroy() - -```ts -destroy(): void; -``` - -Defined in: [hotkey-sequence-recorder.ts:411](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L411) - -#### Returns - -`void` - -*** - -### setOptions() - -```ts -setOptions(options): void; -``` - -Defined in: [hotkey-sequence-recorder.ts:140](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L140) - -Merges current callbacks and options without discarding recorded steps. - -#### Parameters - -##### options - -`Partial`\<[`HotkeySequenceRecorderOptions`](../interfaces/HotkeySequenceRecorderOptions.md)\> - -#### Returns - -`void` - -*** - -### start() - -```ts -start(): void; -``` - -Defined in: [hotkey-sequence-recorder.ts:183](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L183) - -Starts a fresh recording; repeated starts during an active session are ignored. - -#### Returns - -`void` - -*** - -### stop() - -```ts -stop(): void; -``` - -Defined in: [hotkey-sequence-recorder.ts:363](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L363) - -Stops and discards in-progress steps without invoking onCancel. - -#### Returns - -`void` diff --git a/docs/reference/classes/SequenceManager.md b/docs/reference/classes/SequenceManager.md deleted file mode 100644 index b157ccaf..00000000 --- a/docs/reference/classes/SequenceManager.md +++ /dev/null @@ -1,167 +0,0 @@ ---- -id: SequenceManager -title: SequenceManager ---- - -Defined in: [sequence-manager.ts:169](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L169) - -## Properties - -### registrations - -```ts -readonly registrations: Store>; -``` - -Defined in: [sequence-manager.ts:176](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L176) - -The TanStack Store containing sequence registration views for devtools. -Subscribe to this to observe registration changes. - -## Methods - -### destroy() - -```ts -destroy(): void; -``` - -Defined in: [sequence-manager.ts:665](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L665) - -Destroys the manager and removes all listeners. - -#### Returns - -`void` - -*** - -### getRegistrationCount() - -```ts -getRegistrationCount(): number; -``` - -Defined in: [sequence-manager.ts:658](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L658) - -Gets the number of registered sequences. - -#### Returns - -`number` - -*** - -### register() - -```ts -register( - sequence, - callback, - options?): SequenceRegistrationHandle; -``` - -Defined in: [sequence-manager.ts:228](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L228) - -Registers a hotkey sequence handler. - -#### Parameters - -##### sequence - -[`HotkeySequence`](../type-aliases/HotkeySequence.md) - -Array of hotkey strings that form the sequence - -##### callback - -[`HotkeyCallback`](../type-aliases/HotkeyCallback.md) - -Function to call when the sequence is completed - -##### options? - -[`SequenceOptions`](../interfaces/SequenceOptions.md) = `{}` - -Options for the sequence behavior - -#### Returns - -[`SequenceRegistrationHandle`](../interfaces/SequenceRegistrationHandle.md) - -A handle to update or unregister the sequence - -*** - -### resetAll() - -```ts -resetAll(): void; -``` - -Defined in: [sequence-manager.ts:598](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L598) - -Resets all sequence progress. - -#### Returns - -`void` - -*** - -### triggerSequence() - -```ts -triggerSequence(id): boolean; -``` - -Defined in: [sequence-manager.ts:613](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L613) - -Triggers a sequence's callback programmatically from devtools. -Creates a synthetic KeyboardEvent from the last key in the sequence. - -#### Parameters - -##### id - -`string` - -The registration ID to trigger - -#### Returns - -`boolean` - -True if the registration was found and triggered - -*** - -### getInstance() - -```ts -static getInstance(): SequenceManager; -``` - -Defined in: [sequence-manager.ts:197](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L197) - -Gets the singleton instance of SequenceManager. - -#### Returns - -`SequenceManager` - -*** - -### resetInstance() - -```ts -static resetInstance(): void; -``` - -Defined in: [sequence-manager.ts:207](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L207) - -Resets the singleton instance. Useful for testing. - -#### Returns - -`void` diff --git a/docs/reference/functions/areHotkeysEqual.md b/docs/reference/functions/areHotkeysEqual.md index e63a32d3..e0a8f415 100644 --- a/docs/reference/functions/areHotkeysEqual.md +++ b/docs/reference/functions/areHotkeysEqual.md @@ -10,9 +10,10 @@ function areHotkeysEqual( platform?): boolean; ``` -Defined in: [match.ts:194](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L194) +Defined in: [match.ts:203](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L203) -Compares normalized binding identity, including all four modifiers. +Compares normalized hotkey identity, including all four modifiers. +Sequences are equal when every step is equal; a one-step sequence equals its chord. Aliases resolve on the supplied platform. A physical code and a logical key remain distinct even when one keyboard event could match both. diff --git a/docs/reference/functions/assertValidHotkey.md b/docs/reference/functions/assertValidHotkey.md index 372605cd..40f44c7b 100644 --- a/docs/reference/functions/assertValidHotkey.md +++ b/docs/reference/functions/assertValidHotkey.md @@ -7,7 +7,7 @@ title: assertValidHotkey function assertValidHotkey(hotkey): void; ``` -Defined in: [validate.ts:134](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L134) +Defined in: [validate.ts:163](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L163) Validates a hotkey and throws an error if invalid. Useful for development-time validation. @@ -16,7 +16,7 @@ Useful for development-time validation. ### hotkey -[`Hotkey`](../type-aliases/Hotkey.md) \| `string` & `object` +[`ValidatableHotkey`](../type-aliases/ValidatableHotkey.md) The hotkey string to validate diff --git a/docs/reference/functions/checkHotkey.md b/docs/reference/functions/checkHotkey.md index 7a6d0f15..79cf8d1d 100644 --- a/docs/reference/functions/checkHotkey.md +++ b/docs/reference/functions/checkHotkey.md @@ -7,7 +7,7 @@ title: checkHotkey function checkHotkey(hotkey): boolean; ``` -Defined in: [validate.ts:155](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L155) +Defined in: [validate.ts:186](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L186) Validates a hotkey and logs warnings to the console. Useful for development-time feedback. @@ -16,7 +16,7 @@ Useful for development-time feedback. ### hotkey -[`Hotkey`](../type-aliases/Hotkey.md) \| `string` & `object` +[`ValidatableHotkey`](../type-aliases/ValidatableHotkey.md) The hotkey string to validate diff --git a/docs/reference/functions/createHotkeyHandler.md b/docs/reference/functions/createHotkeyHandler.md index ba00e63c..5d5370b1 100644 --- a/docs/reference/functions/createHotkeyHandler.md +++ b/docs/reference/functions/createHotkeyHandler.md @@ -10,7 +10,7 @@ function createHotkeyHandler( options?): (event) => void; ``` -Defined in: [match.ts:87](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L87) +Defined in: [match.ts:95](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L95) Creates a keyboard event handler that calls the callback when the hotkey matches. @@ -18,7 +18,7 @@ Creates a keyboard event handler that calls the callback when the hotkey matches ### hotkey - \| [`Hotkey`](../type-aliases/Hotkey.md) + \| [`HotkeyChord`](../type-aliases/HotkeyChord.md) \| [`ParsedHotkey`](../type-aliases/ParsedHotkey.md) The hotkey string or ParsedHotkey to match diff --git a/docs/reference/functions/createMultiHotkeyHandler.md b/docs/reference/functions/createMultiHotkeyHandler.md index 6a57ecc7..c64cb432 100644 --- a/docs/reference/functions/createMultiHotkeyHandler.md +++ b/docs/reference/functions/createMultiHotkeyHandler.md @@ -7,7 +7,7 @@ title: createMultiHotkeyHandler function createMultiHotkeyHandler(handlers, options?): (event) => void; ``` -Defined in: [match.ts:138](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L138) +Defined in: [match.ts:146](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L146) Creates a handler that matches multiple hotkeys. diff --git a/docs/reference/functions/createSequenceMatcher.md b/docs/reference/functions/createSequenceMatcher.md index 13937243..e46cfb8d 100644 --- a/docs/reference/functions/createSequenceMatcher.md +++ b/docs/reference/functions/createSequenceMatcher.md @@ -7,7 +7,7 @@ title: createSequenceMatcher function createSequenceMatcher(sequence, options?): object; ``` -Defined in: [sequence-manager.ts:704](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L704) +Defined in: [match.ts:259](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L259) Creates a simple sequence matcher for one-off use. diff --git a/docs/reference/functions/defineAppHotkeys.md b/docs/reference/functions/defineAppHotkeys.md new file mode 100644 index 00000000..7ac0f32b --- /dev/null +++ b/docs/reference/functions/defineAppHotkeys.md @@ -0,0 +1,40 @@ +--- +id: defineAppHotkeys +title: defineAppHotkeys +--- + +```ts +function defineAppHotkeys(definitions): DefinedAppHotkeys; +``` + +Defined in: app-hotkeys.ts:175 + +Declares every app hotkey up front, without registering any, so cheat +sheets and settings screens can list hotkeys whose components have not +mounted. Pass the result to `HotkeysProvider` as `appHotkeys`. + +## Type Parameters + +### T + +`T` *extends* `Record`\<`string`, [`AppHotkeyInput`](../interfaces/AppHotkeyInput.md)\> + +## Parameters + +### definitions + +`T` & `ValidatedAppHotkeyInputs`\<`T`\> + +## Returns + +[`DefinedAppHotkeys`](../type-aliases/DefinedAppHotkeys.md)\<`T`\> + +## Example + +```ts +export const appHotkeys = defineAppHotkeys({ + 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save', group: 'Document' } }, + 'nav.home': { hotkey: ['G', 'H'], meta: { name: 'Go home' }, options: { timeout: 800 } }, + 'palette.open': { hotkey: null, meta: { name: 'Command palette' } }, +}) +``` diff --git a/docs/reference/functions/findHotkeyConflicts.md b/docs/reference/functions/findHotkeyConflicts.md index 46ece2f0..344e532c 100644 --- a/docs/reference/functions/findHotkeyConflicts.md +++ b/docs/reference/functions/findHotkeyConflicts.md @@ -17,8 +17,7 @@ discover unmounted handlers, external listeners, or other keyboard layouts. ### candidate - \| [`RegisterableHotkey`](../type-aliases/RegisterableHotkey.md) - \| readonly [`RegisterableHotkey`](../type-aliases/RegisterableHotkey.md)[] +[`RegisterableHotkey`](../type-aliases/RegisterableHotkey.md) ### options? @@ -26,4 +25,4 @@ discover unmounted handlers, external listeners, or other keyboard layouts. ## Returns -[`HotkeyConflict`](../type-aliases/HotkeyConflict.md)[] +[`HotkeyConflict`](../interfaces/HotkeyConflict.md)[] diff --git a/docs/reference/functions/formatForDisplay.md b/docs/reference/functions/formatForDisplay.md index 88c1aef8..c05cd3a2 100644 --- a/docs/reference/functions/formatForDisplay.md +++ b/docs/reference/functions/formatForDisplay.md @@ -5,11 +5,68 @@ title: formatForDisplay ## Call Signature +```ts +function formatForDisplay(hotkey, options): string[][]; +``` + +Defined in: [format.ts:95](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L95) + +Formats a hotkey for display in a user interface. + +On macOS, uses symbols (⇧⌘S) in Control, Option, Shift, Command order. +Display ordering does not change the normalized hotkey used for registration. +On Windows/Linux, uses text (Ctrl+Shift+S) with `+` separators. +The separator can be customized with `separatorToken`. +Physical codes use conventional display labels: `[KeyS]` becomes `S` and +`[Digit2]` becomes `2`. These labels do not infer the active keyboard layout +or change matching. Supply a resolved `layoutMap` for layout-aware labels; +`keyLabels` overrides both layout entries and fallback labels. +Sequence steps are formatted individually and joined with `sequenceSeparator` +(a space by default). + +### Parameters + +#### hotkey + +readonly [`DisplayHotkeyChord`](../type-aliases/DisplayHotkeyChord.md)[] + +A logical or physical string, raw object, ParsedHotkey, or sequence + +#### options + +[`FormatDisplayOptions`](../interfaces/FormatDisplayOptions.md) & `object` + +Formatting options + +### Returns + +`string`[][] + +Display text, or individual key labels when parts is true (one array per step for sequences) + +### Example + +```ts +formatForDisplay('Mod+Shift+S', { platform: 'mac' }) +// Returns: '⇧ ⌘ S' (symbols separated by spaces on macOS) + +formatForDisplay('Mod+Shift+S', { platform: 'windows' }) +// Returns: 'Ctrl+Shift+S' + +formatForDisplay('Escape') +// Returns: 'Esc' (on all platforms) + +formatForDisplay(['G', 'H']) +// Returns: 'G H' +``` + +## Call Signature + ```ts function formatForDisplay(hotkey, options): string[]; ``` -Defined in: [format.ts:101](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L101) +Defined in: [format.ts:99](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L99) Formats a hotkey for display in a user interface. @@ -21,14 +78,16 @@ Physical codes use conventional display labels: `[KeyS]` becomes `S` and `[Digit2]` becomes `2`. These labels do not infer the active keyboard layout or change matching. Supply a resolved `layoutMap` for layout-aware labels; `keyLabels` overrides both layout entries and fallback labels. +Sequence steps are formatted individually and joined with `sequenceSeparator` +(a space by default). ### Parameters #### hotkey -[`DisplayHotkey`](../type-aliases/DisplayHotkey.md) +[`DisplayHotkeyChord`](../type-aliases/DisplayHotkeyChord.md) -A logical or physical string, raw binding, or ParsedHotkey +A logical or physical string, raw object, ParsedHotkey, or sequence #### options @@ -40,7 +99,7 @@ Formatting options `string`[] -Display text, or individual key labels when parts is true +Display text, or individual key labels when parts is true (one array per step for sequences) ### Example @@ -53,6 +112,9 @@ formatForDisplay('Mod+Shift+S', { platform: 'windows' }) formatForDisplay('Escape') // Returns: 'Esc' (on all platforms) + +formatForDisplay(['G', 'H']) +// Returns: 'G H' ``` ## Call Signature @@ -61,7 +123,7 @@ formatForDisplay('Escape') function formatForDisplay(hotkey, options?): string; ``` -Defined in: [format.ts:105](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L105) +Defined in: [format.ts:103](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L103) Formats a hotkey for display in a user interface. @@ -73,6 +135,8 @@ Physical codes use conventional display labels: `[KeyS]` becomes `S` and `[Digit2]` becomes `2`. These labels do not infer the active keyboard layout or change matching. Supply a resolved `layoutMap` for layout-aware labels; `keyLabels` overrides both layout entries and fallback labels. +Sequence steps are formatted individually and joined with `sequenceSeparator` +(a space by default). ### Parameters @@ -80,7 +144,7 @@ or change matching. Supply a resolved `layoutMap` for layout-aware labels; [`DisplayHotkey`](../type-aliases/DisplayHotkey.md) -A logical or physical string, raw binding, or ParsedHotkey +A logical or physical string, raw object, ParsedHotkey, or sequence #### options? @@ -92,7 +156,7 @@ Formatting options `string` -Display text, or individual key labels when parts is true +Display text, or individual key labels when parts is true (one array per step for sequences) ### Example @@ -105,15 +169,18 @@ formatForDisplay('Mod+Shift+S', { platform: 'windows' }) formatForDisplay('Escape') // Returns: 'Esc' (on all platforms) + +formatForDisplay(['G', 'H']) +// Returns: 'G H' ``` ## Call Signature ```ts -function formatForDisplay(hotkey, options): string | string[]; +function formatForDisplay(hotkey, options): string | string[] | string[][]; ``` -Defined in: [format.ts:109](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L109) +Defined in: [format.ts:107](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L107) Formats a hotkey for display in a user interface. @@ -125,6 +192,8 @@ Physical codes use conventional display labels: `[KeyS]` becomes `S` and `[Digit2]` becomes `2`. These labels do not infer the active keyboard layout or change matching. Supply a resolved `layoutMap` for layout-aware labels; `keyLabels` overrides both layout entries and fallback labels. +Sequence steps are formatted individually and joined with `sequenceSeparator` +(a space by default). ### Parameters @@ -132,7 +201,7 @@ or change matching. Supply a resolved `layoutMap` for layout-aware labels; [`DisplayHotkey`](../type-aliases/DisplayHotkey.md) -A logical or physical string, raw binding, or ParsedHotkey +A logical or physical string, raw object, ParsedHotkey, or sequence #### options @@ -142,9 +211,9 @@ Formatting options ### Returns -`string` \| `string`[] +`string` \| `string`[] \| `string`[][] -Display text, or individual key labels when parts is true +Display text, or individual key labels when parts is true (one array per step for sequences) ### Example @@ -157,4 +226,7 @@ formatForDisplay('Mod+Shift+S', { platform: 'windows' }) formatForDisplay('Escape') // Returns: 'Esc' (on all platforms) + +formatForDisplay(['G', 'H']) +// Returns: 'G H' ``` diff --git a/docs/reference/functions/formatHotkey.md b/docs/reference/functions/formatHotkey.md index e76c1d3e..0f33fa89 100644 --- a/docs/reference/functions/formatHotkey.md +++ b/docs/reference/functions/formatHotkey.md @@ -7,7 +7,7 @@ title: formatHotkey function formatHotkey(parsed): string; ``` -Defined in: [format.ts:54](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L54) +Defined in: [format.ts:37](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L37) Converts a ParsedHotkey back to a hotkey string. diff --git a/docs/reference/functions/formatHotkeySequence.md b/docs/reference/functions/formatHotkeySequence.md deleted file mode 100644 index cafc3383..00000000 --- a/docs/reference/functions/formatHotkeySequence.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -id: formatHotkeySequence -title: formatHotkeySequence ---- - -```ts -function formatHotkeySequence(sequence): string; -``` - -Defined in: [format.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L38) - -Joins stored sequence steps with spaces, preserving physical code tokens. -For user-facing labels, call formatForDisplay on each step instead. - -## Parameters - -### sequence - -[`Hotkey`](../type-aliases/Hotkey.md)[] - -Array of hotkey strings that form the sequence - -## Returns - -`string` - -A space-separated string (e.g. ['G','G'] → 'G G') - -## Example - -```ts -formatHotkeySequence(['G', 'G']) // 'G G' -formatHotkeySequence(['D', 'I', 'W']) // 'D I W' -``` diff --git a/docs/reference/functions/formatWithLabels.md b/docs/reference/functions/formatWithLabels.md deleted file mode 100644 index 9e76b4fc..00000000 --- a/docs/reference/functions/formatWithLabels.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -id: formatWithLabels -title: formatWithLabels ---- - -```ts -function formatWithLabels(hotkey, options?): string; -``` - -Defined in: [format.ts:185](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L185) - -## Parameters - -### hotkey - -[`RegisterableHotkey`](../type-aliases/RegisterableHotkey.md) - -### options? - -`Omit`\<[`FormatDisplayOptions`](../interfaces/FormatDisplayOptions.md), `"useSymbols"` \| `"parts"`\> = `{}` - -## Returns - -`string` - -## Deprecated - -Use [formatForDisplay](formatForDisplay.md) instead with `useSymbols: false` option. diff --git a/docs/reference/functions/getAppHotkeyRegistrationOptions.md b/docs/reference/functions/getAppHotkeyRegistrationOptions.md new file mode 100644 index 00000000..ca83a441 --- /dev/null +++ b/docs/reference/functions/getAppHotkeyRegistrationOptions.md @@ -0,0 +1,24 @@ +--- +id: getAppHotkeyRegistrationOptions +title: getAppHotkeyRegistrationOptions +--- + +```ts +function getAppHotkeyRegistrationOptions(appHotkey): AppHotkeyChordOptions & AppHotkeySequenceOptions & object; +``` + +Defined in: app-hotkeys.ts:528 + +Options that adapters pass to the manager when registering an app hotkey: +declared options, meta, and the app hotkey id. The manager drops options +that do not apply to the effective hotkey's kind. + +## Parameters + +### appHotkey + +[`AppHotkey`](../interfaces/AppHotkey.md) + +## Returns + +[`AppHotkeyChordOptions`](../type-aliases/AppHotkeyChordOptions.md) & [`AppHotkeySequenceOptions`](../type-aliases/AppHotkeySequenceOptions.md) & `object` diff --git a/docs/reference/functions/getHotkeyManager.md b/docs/reference/functions/getHotkeyManager.md index 722451ae..4eda621b 100644 --- a/docs/reference/functions/getHotkeyManager.md +++ b/docs/reference/functions/getHotkeyManager.md @@ -7,7 +7,7 @@ title: getHotkeyManager function getHotkeyManager(): HotkeyManager; ``` -Defined in: [hotkey-manager.ts:812](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L812) +Defined in: [hotkey-manager.ts:1059](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L1059) Gets the singleton HotkeyManager instance. Convenience function for accessing the manager. diff --git a/docs/reference/functions/getSequenceManager.md b/docs/reference/functions/getSequenceManager.md deleted file mode 100644 index 78ca51b9..00000000 --- a/docs/reference/functions/getSequenceManager.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -id: getSequenceManager -title: getSequenceManager ---- - -```ts -function getSequenceManager(): SequenceManager; -``` - -Defined in: [sequence-manager.ts:678](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L678) - -Gets the singleton SequenceManager instance. -Convenience function for accessing the manager. - -## Returns - -[`SequenceManager`](../classes/SequenceManager.md) diff --git a/docs/reference/functions/hasNonModifierKey.md b/docs/reference/functions/hasNonModifierKey.md index 94197a1b..5d904583 100644 --- a/docs/reference/functions/hasNonModifierKey.md +++ b/docs/reference/functions/hasNonModifierKey.md @@ -7,7 +7,7 @@ title: hasNonModifierKey function hasNonModifierKey(hotkey, platform?): boolean; ``` -Defined in: [parse.ts:336](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L336) +Defined in: [parse.ts:373](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L373) Checks if a hotkey or ParsedHotkey contains at least one non-modifier key. @@ -18,7 +18,7 @@ just a combination of modifiers without an action key. ### hotkey - \| [`Hotkey`](../type-aliases/Hotkey.md) + \| [`HotkeyChord`](../type-aliases/HotkeyChord.md) \| `string` & `object` \| [`ParsedHotkey`](../type-aliases/ParsedHotkey.md) diff --git a/docs/reference/functions/isAppHotkey.md b/docs/reference/functions/isAppHotkey.md new file mode 100644 index 00000000..06b34c92 --- /dev/null +++ b/docs/reference/functions/isAppHotkey.md @@ -0,0 +1,22 @@ +--- +id: isAppHotkey +title: isAppHotkey +--- + +```ts +function isAppHotkey(value): value is AppHotkey; +``` + +Defined in: app-hotkeys.ts:195 + +Whether a value is an app hotkey created by `defineAppHotkeys`. + +## Parameters + +### value + +`unknown` + +## Returns + +value is AppHotkey\ diff --git a/docs/reference/functions/isCustomHotkey.md b/docs/reference/functions/isCustomHotkey.md new file mode 100644 index 00000000..f97dca9d --- /dev/null +++ b/docs/reference/functions/isCustomHotkey.md @@ -0,0 +1,26 @@ +--- +id: isCustomHotkey +title: isCustomHotkey +--- + +```ts +function isCustomHotkey(appHotkey, platform?): boolean; +``` + +Defined in: app-hotkeys.ts:213 + +Whether an app hotkey's effective hotkey differs from its default. + +## Parameters + +### appHotkey + +[`AppHotkey`](../interfaces/AppHotkey.md) + +### platform? + +`Platform` = `...` + +## Returns + +`boolean` diff --git a/docs/reference/functions/isHotkeySequence.md b/docs/reference/functions/isHotkeySequence.md new file mode 100644 index 00000000..79619c46 --- /dev/null +++ b/docs/reference/functions/isHotkeySequence.md @@ -0,0 +1,23 @@ +--- +id: isHotkeySequence +title: isHotkeySequence +--- + +```ts +function isHotkeySequence(hotkey): hotkey is HotkeySequence; +``` + +Defined in: [parse.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L23) + +Whether a hotkey is a sequence. Use this instead of `Array.isArray`, which +does not narrow readonly arrays. + +## Parameters + +### hotkey + +[`RegisterableHotkey`](../type-aliases/RegisterableHotkey.md) + +## Returns + +`hotkey is HotkeySequence` diff --git a/docs/reference/functions/isModifierKey.md b/docs/reference/functions/isModifierKey.md index c7c49d7b..f7cdd584 100644 --- a/docs/reference/functions/isModifierKey.md +++ b/docs/reference/functions/isModifierKey.md @@ -7,7 +7,7 @@ title: isModifierKey function isModifierKey(key): key is string; ``` -Defined in: [parse.ts:250](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L250) +Defined in: [parse.ts:287](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L287) Checks if a string is a recognized modifier token (including aliases). diff --git a/docs/reference/functions/matchesHeldModifiers.md b/docs/reference/functions/matchesHeldModifiers.md index de844b2e..4f016e87 100644 --- a/docs/reference/functions/matchesHeldModifiers.md +++ b/docs/reference/functions/matchesHeldModifiers.md @@ -10,12 +10,13 @@ function matchesHeldModifiers( options?): boolean; ``` -Defined in: [hint.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hint.ts#L23) +Defined in: [hint.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hint.ts#L24) Returns whether held modifiers should reveal a shortcut hint. By default, any nonempty subset of the binding's modifiers qualifies; an unrelated modifier hides the hint. `exact` requires every binding modifier. Nonmodifier keys are ignored. This does not check registration or focus. +A sequence reveals its hint from its first step. ## Parameters diff --git a/docs/reference/functions/matchesKeyboardEvent.md b/docs/reference/functions/matchesKeyboardEvent.md index da398c24..aa013f48 100644 --- a/docs/reference/functions/matchesKeyboardEvent.md +++ b/docs/reference/functions/matchesKeyboardEvent.md @@ -10,7 +10,7 @@ function matchesKeyboardEvent( platform?): boolean; ``` -Defined in: [match.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L49) +Defined in: [match.ts:57](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L57) Checks if a KeyboardEvent matches a hotkey. @@ -35,7 +35,7 @@ The KeyboardEvent to check ### hotkey - \| [`Hotkey`](../type-aliases/Hotkey.md) + \| [`HotkeyChord`](../type-aliases/HotkeyChord.md) \| [`ParsedHotkey`](../type-aliases/ParsedHotkey.md) The hotkey string or ParsedHotkey to match against diff --git a/docs/reference/functions/normalizeHotkey.md b/docs/reference/functions/normalizeHotkey.md index 4de4410a..c8dd748e 100644 --- a/docs/reference/functions/normalizeHotkey.md +++ b/docs/reference/functions/normalizeHotkey.md @@ -4,10 +4,10 @@ title: normalizeHotkey --- ```ts -function normalizeHotkey(hotkey, platform?): Hotkey; +function normalizeHotkey(hotkey, platform?): HotkeyChord; ``` -Defined in: [parse.ts:205](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L205) +Defined in: [parse.ts:229](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L229) Normalizes a hotkey string to its canonical form. @@ -33,7 +33,7 @@ The target platform for resolving `Mod` (defaults to auto-detection) ## Returns -[`Hotkey`](../type-aliases/Hotkey.md) +[`HotkeyChord`](../type-aliases/HotkeyChord.md) The normalized hotkey string diff --git a/docs/reference/functions/normalizeHotkeyFromEvent.md b/docs/reference/functions/normalizeHotkeyFromEvent.md index c10afff5..f7768cc6 100644 --- a/docs/reference/functions/normalizeHotkeyFromEvent.md +++ b/docs/reference/functions/normalizeHotkeyFromEvent.md @@ -4,10 +4,10 @@ title: normalizeHotkeyFromEvent --- ```ts -function normalizeHotkeyFromEvent(event, platform?): Hotkey; +function normalizeHotkeyFromEvent(event, platform?): HotkeyChord; ``` -Defined in: [parse.ts:309](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L309) +Defined in: [parse.ts:346](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L346) Normalizes a keyboard event to the same canonical hotkey string as [normalizeHotkey](normalizeHotkey.md). @@ -27,4 +27,4 @@ Target platform for `Mod` eligibility ## Returns -[`Hotkey`](../type-aliases/Hotkey.md) +[`HotkeyChord`](../type-aliases/HotkeyChord.md) diff --git a/docs/reference/functions/normalizeHotkeyFromParsed.md b/docs/reference/functions/normalizeHotkeyFromParsed.md index 55d4551c..8a58660e 100644 --- a/docs/reference/functions/normalizeHotkeyFromParsed.md +++ b/docs/reference/functions/normalizeHotkeyFromParsed.md @@ -4,10 +4,10 @@ title: normalizeHotkeyFromParsed --- ```ts -function normalizeHotkeyFromParsed(parsed, platform?): Hotkey; +function normalizeHotkeyFromParsed(parsed, platform?): HotkeyChord; ``` -Defined in: [parse.ts:218](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L218) +Defined in: [parse.ts:242](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L242) Same canonical string as [normalizeHotkey](normalizeHotkey.md), but from an already-parsed hotkey. @@ -23,4 +23,4 @@ Same canonical string as [normalizeHotkey](normalizeHotkey.md), but from an alre ## Returns -[`Hotkey`](../type-aliases/Hotkey.md) +[`HotkeyChord`](../type-aliases/HotkeyChord.md) diff --git a/docs/reference/functions/normalizeRegisterableHotkey.md b/docs/reference/functions/normalizeRegisterableHotkey.md index 44338ec7..c70df2dd 100644 --- a/docs/reference/functions/normalizeRegisterableHotkey.md +++ b/docs/reference/functions/normalizeRegisterableHotkey.md @@ -3,25 +3,54 @@ id: normalizeRegisterableHotkey title: normalizeRegisterableHotkey --- +## Call Signature + +```ts +function normalizeRegisterableHotkey(hotkey, platform?): HotkeyChord; +``` + +Defined in: [parse.ts:254](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L254) + +Normalizes a chord (string or [RawHotkey](../type-aliases/RawHotkey.md) object) or a sequence to canonical form. +Each sequence step is normalized, and a one-step sequence becomes its chord. +Use this in framework adapters instead of branching on `formatHotkey(rawHotkeyToParsedHotkey(...))`. + +### Parameters + +#### hotkey + +[`RegisterableHotkeyChord`](../type-aliases/RegisterableHotkeyChord.md) + +#### platform? + +`"mac"` \| `"windows"` \| `"linux"` + +### Returns + +[`HotkeyChord`](../type-aliases/HotkeyChord.md) + +## Call Signature + ```ts function normalizeRegisterableHotkey(hotkey, platform?): Hotkey; ``` -Defined in: [parse.ts:229](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L229) +Defined in: [parse.ts:258](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L258) -Normalizes a string or [RawHotkey](../type-aliases/RawHotkey.md) object to the same canonical hotkey string. +Normalizes a chord (string or [RawHotkey](../type-aliases/RawHotkey.md) object) or a sequence to canonical form. +Each sequence step is normalized, and a one-step sequence becomes its chord. Use this in framework adapters instead of branching on `formatHotkey(rawHotkeyToParsedHotkey(...))`. -## Parameters +### Parameters -### hotkey +#### hotkey [`RegisterableHotkey`](../type-aliases/RegisterableHotkey.md) -### platform? +#### platform? `"mac"` \| `"windows"` \| `"linux"` -## Returns +### Returns [`Hotkey`](../type-aliases/Hotkey.md) diff --git a/docs/reference/functions/parseHotkey.md b/docs/reference/functions/parseHotkey.md index ffdee9e7..005eb739 100644 --- a/docs/reference/functions/parseHotkey.md +++ b/docs/reference/functions/parseHotkey.md @@ -7,7 +7,7 @@ title: parseHotkey function parseHotkey(hotkey, platform?): ParsedHotkey; ``` -Defined in: [parse.ts:30](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L30) +Defined in: [parse.ts:54](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L54) Parses a hotkey string into its component parts. @@ -15,7 +15,7 @@ Parses a hotkey string into its component parts. ### hotkey -[`Hotkey`](../type-aliases/Hotkey.md) \| `string` & `object` +[`HotkeyChord`](../type-aliases/HotkeyChord.md) \| `string` & `object` The hotkey string to parse (e.g., 'Mod+Shift+S') diff --git a/docs/reference/functions/parseKeyboardEvent.md b/docs/reference/functions/parseKeyboardEvent.md index d407d384..167f0870 100644 --- a/docs/reference/functions/parseKeyboardEvent.md +++ b/docs/reference/functions/parseKeyboardEvent.md @@ -7,7 +7,7 @@ title: parseKeyboardEvent function parseKeyboardEvent(event, platform?): ParsedHotkey; ``` -Defined in: [parse.ts:274](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L274) +Defined in: [parse.ts:311](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L311) Parses a KeyboardEvent into a ParsedHotkey object. diff --git a/docs/reference/functions/parseRegisterableHotkey.md b/docs/reference/functions/parseRegisterableHotkey.md index 1ce0eb12..5109735b 100644 --- a/docs/reference/functions/parseRegisterableHotkey.md +++ b/docs/reference/functions/parseRegisterableHotkey.md @@ -7,7 +7,7 @@ title: parseRegisterableHotkey function parseRegisterableHotkey(hotkey, platform?): ParsedHotkey; ``` -Defined in: [parse.ts:362](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L362) +Defined in: [parse.ts:399](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L399) Parses a string or raw binding, resolving Mod for the supplied platform. Physical tokens such as `[KeyQ]` retain `code`; they never become logical Q. @@ -16,7 +16,7 @@ Physical tokens such as `[KeyQ]` retain `code`; they never become logical Q. ### hotkey -[`RegisterableHotkey`](../type-aliases/RegisterableHotkey.md) +[`RegisterableHotkeyChord`](../type-aliases/RegisterableHotkeyChord.md) ### platform? diff --git a/docs/reference/functions/provideAppHotkeys.md b/docs/reference/functions/provideAppHotkeys.md new file mode 100644 index 00000000..8abb8e59 --- /dev/null +++ b/docs/reference/functions/provideAppHotkeys.md @@ -0,0 +1,65 @@ +--- +id: provideAppHotkeys +title: provideAppHotkeys +--- + +```ts +function provideAppHotkeys(appHotkeys, options?): object; +``` + +Defined in: app-hotkeys.ts:569 + +Creates an app hotkeys store and publishes it on the HotkeyManager, for +adapters without a provider component (such as Lit and Alpine). Hotkey +APIs in those adapters resolve app hotkeys and custom hotkeys from the +published store. Call it once at startup. Returns the store and a function +that unpublishes it. + +## Type Parameters + +### T + +`T` *extends* `Readonly`\<`Record`\<`string`, [`AppHotkey`](../interfaces/AppHotkey.md)\<`string`, + \| [`HotkeyChord`](../type-aliases/HotkeyChord.md) + \| [`HotkeySequence`](../type-aliases/HotkeySequence.md) + \| `null`\>\>\> + +## Parameters + +### appHotkeys + +`T` + +### options? + +[`AppHotkeysStoreOptions`](../interfaces/AppHotkeysStoreOptions.md)\<`T`\> = `{}` + +## Returns + +`object` + +### dispose + +```ts +dispose: () => void; +``` + +#### Returns + +`void` + +### store + +```ts +store: AppHotkeysStore; +``` + +## Example + +```ts +provideAppHotkeys(appHotkeys, { + customHotkeys: JSON.parse(localStorage.getItem('hotkeys') ?? '{}'), + onCustomHotkeysChange: (custom) => + localStorage.setItem('hotkeys', JSON.stringify(custom)), +}) +``` diff --git a/docs/reference/functions/publishAppHotkeysStore.md b/docs/reference/functions/publishAppHotkeysStore.md new file mode 100644 index 00000000..282c26a3 --- /dev/null +++ b/docs/reference/functions/publishAppHotkeysStore.md @@ -0,0 +1,24 @@ +--- +id: publishAppHotkeysStore +title: publishAppHotkeysStore +--- + +```ts +function publishAppHotkeysStore(store): () => void; +``` + +Defined in: app-hotkeys.ts:543 + +Publishes a provider's store on the HotkeyManager so devtools can list +declared hotkeys. Returns a cleanup function that unpublishes it if it is +still the published store. + +## Parameters + +### store + +[`AppHotkeysStore`](../classes/AppHotkeysStore.md) + +## Returns + +() => `void` diff --git a/docs/reference/functions/rawHotkeyToParsedHotkey.md b/docs/reference/functions/rawHotkeyToParsedHotkey.md index d070c009..b68d8489 100644 --- a/docs/reference/functions/rawHotkeyToParsedHotkey.md +++ b/docs/reference/functions/rawHotkeyToParsedHotkey.md @@ -7,7 +7,7 @@ title: rawHotkeyToParsedHotkey function rawHotkeyToParsedHotkey(raw, platform?): ParsedHotkey; ``` -Defined in: [parse.ts:99](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L99) +Defined in: [parse.ts:123](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L123) Converts a RawHotkey object to a ParsedHotkey. Optional modifier booleans default to false; modifiers array is derived from them. diff --git a/docs/reference/functions/resolveAppHotkey.md b/docs/reference/functions/resolveAppHotkey.md new file mode 100644 index 00000000..45b09fb7 --- /dev/null +++ b/docs/reference/functions/resolveAppHotkey.md @@ -0,0 +1,33 @@ +--- +id: resolveAppHotkey +title: resolveAppHotkey +--- + +```ts +function resolveAppHotkey(appHotkey, store): AppHotkey; +``` + +Defined in: app-hotkeys.ts:516 + +Returns the provider's version of an app hotkey, so custom hotkeys apply +even when a component imports the declaration directly. Falls back to the +given app hotkey when no store declares it. + +## Parameters + +### appHotkey + +[`AppHotkey`](../interfaces/AppHotkey.md) + +### store + + \| [`AppHotkeysStore`](../classes/AppHotkeysStore.md)\<`Readonly`\<`Record`\<`string`, [`AppHotkey`](../interfaces/AppHotkey.md)\<`string`, + \| [`HotkeyChord`](../type-aliases/HotkeyChord.md) + \| [`HotkeySequence`](../type-aliases/HotkeySequence.md) + \| `null`\>\>\>\> + \| `null` + \| `undefined` + +## Returns + +[`AppHotkey`](../interfaces/AppHotkey.md) diff --git a/docs/reference/functions/simplifyHotkey.md b/docs/reference/functions/simplifyHotkey.md new file mode 100644 index 00000000..5588bb31 --- /dev/null +++ b/docs/reference/functions/simplifyHotkey.md @@ -0,0 +1,29 @@ +--- +id: simplifyHotkey +title: simplifyHotkey +--- + +```ts +function simplifyHotkey(hotkey): T extends HotkeySequence ? Hotkey : T; +``` + +Defined in: [parse.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/parse.ts#L33) + +Collapses a one-step sequence to its chord without otherwise changing the hotkey. +Registration APIs treat `['Mod+S']` exactly like `'Mod+S'`. + +## Type Parameters + +### T + +`T` *extends* [`RegisterableHotkey`](../type-aliases/RegisterableHotkey.md) + +## Parameters + +### hotkey + +`T` + +## Returns + +`T` *extends* [`HotkeySequence`](../type-aliases/HotkeySequence.md) ? [`Hotkey`](../type-aliases/Hotkey.md) : `T` diff --git a/docs/reference/functions/toHotkeyRegistrationView.md b/docs/reference/functions/toHotkeyRegistrationView.md index ea7cb501..375c020d 100644 --- a/docs/reference/functions/toHotkeyRegistrationView.md +++ b/docs/reference/functions/toHotkeyRegistrationView.md @@ -7,17 +7,16 @@ title: toHotkeyRegistrationView function toHotkeyRegistrationView(reg): HotkeyRegistrationView; ``` -Defined in: [hotkey-manager.ts:107](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L107) +Defined in: [hotkey-manager.ts:170](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L170) -Creates a public view from an internal registration, -stripping the callback function. +Creates a public view from an internal registration, stripping the callback. ## Parameters ### reg -[`HotkeyRegistration`](../interfaces/HotkeyRegistration.md) +[`HotkeyRegistration`](../type-aliases/HotkeyRegistration.md) ## Returns -[`HotkeyRegistrationView`](../interfaces/HotkeyRegistrationView.md) +[`HotkeyRegistrationView`](../type-aliases/HotkeyRegistrationView.md) diff --git a/docs/reference/functions/validateHotkey.md b/docs/reference/functions/validateHotkey.md index d7c5effc..bdb19da4 100644 --- a/docs/reference/functions/validateHotkey.md +++ b/docs/reference/functions/validateHotkey.md @@ -7,9 +7,10 @@ title: validateHotkey function validateHotkey(hotkey): ValidationResult; ``` -Defined in: [validate.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L25) +Defined in: [validate.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L32) -Validates a hotkey string and returns any warnings or errors. +Validates a hotkey and returns any warnings or errors. +Sequences are validated step by step, and messages name the failing step. Checks for: - Valid syntax (modifier+...+key format) @@ -20,7 +21,7 @@ Checks for: ### hotkey -[`Hotkey`](../type-aliases/Hotkey.md) \| `string` & `object` +[`ValidatableHotkey`](../type-aliases/ValidatableHotkey.md) The hotkey string to validate @@ -38,4 +39,7 @@ validateHotkey('Mod+S') validateHotkey('') // { valid: false, warnings: [], errors: ['Hotkey cannot be empty'] } + +validateHotkey(['G', 'Foo+H']) +// { valid: false, warnings: [], errors: ["Step 2: Unknown modifier: 'Foo'"] } ``` diff --git a/docs/reference/index.md b/docs/reference/index.md index 630487b0..e57e87c6 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -5,30 +5,37 @@ title: "@tanstack/hotkeys" ## Classes +- [AppHotkeysStore](classes/AppHotkeysStore.md) - [HotkeyManager](classes/HotkeyManager.md) - [HotkeyRecorder](classes/HotkeyRecorder.md) -- [HotkeySequenceRecorder](classes/HotkeySequenceRecorder.md) - [KeyStateTracker](classes/KeyStateTracker.md) -- [SequenceManager](classes/SequenceManager.md) ## Interfaces +- [AppHotkey](interfaces/AppHotkey.md) +- [AppHotkeyInput](interfaces/AppHotkeyInput.md) +- [AppHotkeyListItem](interfaces/AppHotkeyListItem.md) +- [AppHotkeysState](interfaces/AppHotkeysState.md) +- [AppHotkeysStoreOptions](interfaces/AppHotkeysStoreOptions.md) - [CreateHotkeyHandlerOptions](interfaces/CreateHotkeyHandlerOptions.md) - [FormatDisplayOptions](interfaces/FormatDisplayOptions.md) - [HeldModifierOptions](interfaces/HeldModifierOptions.md) - [HotkeyCallbackContext](interfaces/HotkeyCallbackContext.md) +- [HotkeyChordOptions](interfaces/HotkeyChordOptions.md) +- [HotkeyChordRecorderOptions](interfaces/HotkeyChordRecorderOptions.md) +- [HotkeyChordRegistration](interfaces/HotkeyChordRegistration.md) +- [HotkeyChordRegistrationView](interfaces/HotkeyChordRegistrationView.md) +- [HotkeyConflict](interfaces/HotkeyConflict.md) - [HotkeyConflictOptions](interfaces/HotkeyConflictOptions.md) - [HotkeyMeta](interfaces/HotkeyMeta.md) - [HotkeyOptions](interfaces/HotkeyOptions.md) -- [HotkeyRecorderOptions](interfaces/HotkeyRecorderOptions.md) - [HotkeyRecorderState](interfaces/HotkeyRecorderState.md) - [HotkeyRecorderValidationContext](interfaces/HotkeyRecorderValidationContext.md) -- [HotkeyRegistration](interfaces/HotkeyRegistration.md) - [HotkeyRegistrationHandle](interfaces/HotkeyRegistrationHandle.md) -- [HotkeyRegistrationView](interfaces/HotkeyRegistrationView.md) +- [HotkeySequenceOptions](interfaces/HotkeySequenceOptions.md) - [HotkeySequenceRecorderOptions](interfaces/HotkeySequenceRecorderOptions.md) -- [HotkeySequenceRecorderState](interfaces/HotkeySequenceRecorderState.md) -- [HotkeySequenceRecorderValidationContext](interfaces/HotkeySequenceRecorderValidationContext.md) +- [HotkeySequenceRegistration](interfaces/HotkeySequenceRegistration.md) +- [HotkeySequenceRegistrationView](interfaces/HotkeySequenceRegistrationView.md) - [KeyboardEventMatch](interfaces/KeyboardEventMatch.md) - [KeyStateTrackerState](interfaces/KeyStateTrackerState.md) - [NormalizedKeyboardEvent](interfaces/NormalizedKeyboardEvent.md) @@ -36,25 +43,36 @@ title: "@tanstack/hotkeys" - [RawModifiers](interfaces/RawModifiers.md) - [RecorderOptions](interfaces/RecorderOptions.md) - [RecorderRejection](interfaces/RecorderRejection.md) -- [SequenceOptions](interfaces/SequenceOptions.md) -- [SequenceRegistration](interfaces/SequenceRegistration.md) -- [SequenceRegistrationHandle](interfaces/SequenceRegistrationHandle.md) -- [SequenceRegistrationView](interfaces/SequenceRegistrationView.md) +- [Register](interfaces/Register.md) - [ValidationResult](interfaces/ValidationResult.md) ## Type Aliases +- [AppHotkeyChordOptions](type-aliases/AppHotkeyChordOptions.md) +- [AppHotkeyDefaultKind](type-aliases/AppHotkeyDefaultKind.md) +- [AppHotkeyId](type-aliases/AppHotkeyId.md) +- [AppHotkeyOptions](type-aliases/AppHotkeyOptions.md) +- [AppHotkeys](type-aliases/AppHotkeys.md) +- [AppHotkeySequenceOptions](type-aliases/AppHotkeySequenceOptions.md) +- [AppHotkeyStatus](type-aliases/AppHotkeyStatus.md) - [CanonicalModifier](type-aliases/CanonicalModifier.md) - [ConflictBehavior](type-aliases/ConflictBehavior.md) +- [CustomHotkeys](type-aliases/CustomHotkeys.md) +- [DefinedAppHotkeys](type-aliases/DefinedAppHotkeys.md) - [DisplayHotkey](type-aliases/DisplayHotkey.md) +- [DisplayHotkeyChord](type-aliases/DisplayHotkeyChord.md) - [EditingKey](type-aliases/EditingKey.md) - [FourModifierHotkey](type-aliases/FourModifierHotkey.md) - [FunctionKey](type-aliases/FunctionKey.md) - [Hotkey](type-aliases/Hotkey.md) - [HotkeyCallback](type-aliases/HotkeyCallback.md) -- [HotkeyConflict](type-aliases/HotkeyConflict.md) +- [HotkeyChord](type-aliases/HotkeyChord.md) +- [HotkeyOptionsFor](type-aliases/HotkeyOptionsFor.md) +- [HotkeyRecorderCommitKeys](type-aliases/HotkeyRecorderCommitKeys.md) +- [HotkeyRecorderOptions](type-aliases/HotkeyRecorderOptions.md) +- [HotkeyRegistration](type-aliases/HotkeyRegistration.md) +- [HotkeyRegistrationView](type-aliases/HotkeyRegistrationView.md) - [HotkeySequence](type-aliases/HotkeySequence.md) -- [HotkeySequenceRecorderCommitKeys](type-aliases/HotkeySequenceRecorderCommitKeys.md) - [IndividualKey](type-aliases/IndividualKey.md) - [Key](type-aliases/Key.md) - [LetterKey](type-aliases/LetterKey.md) @@ -72,10 +90,13 @@ title: "@tanstack/hotkeys" - [RawHotkey](type-aliases/RawHotkey.md) - [RecorderKeyMode](type-aliases/RecorderKeyMode.md) - [RegisterableHotkey](type-aliases/RegisterableHotkey.md) +- [RegisterableHotkeyChord](type-aliases/RegisterableHotkeyChord.md) +- [RegisteredAppHotkeys](type-aliases/RegisteredAppHotkeys.md) - [SingleModifierHotkey](type-aliases/SingleModifierHotkey.md) - [Target](type-aliases/Target.md) - [ThreeModifierHotkey](type-aliases/ThreeModifierHotkey.md) - [TwoModifierHotkey](type-aliases/TwoModifierHotkey.md) +- [ValidatableHotkey](type-aliases/ValidatableHotkey.md) ## Variables @@ -106,16 +127,18 @@ title: "@tanstack/hotkeys" - [createHotkeyHandler](functions/createHotkeyHandler.md) - [createMultiHotkeyHandler](functions/createMultiHotkeyHandler.md) - [createSequenceMatcher](functions/createSequenceMatcher.md) +- [defineAppHotkeys](functions/defineAppHotkeys.md) - [detectPlatform](functions/detectPlatform.md) - [findHotkeyConflicts](functions/findHotkeyConflicts.md) - [formatForDisplay](functions/formatForDisplay.md) - [formatHotkey](functions/formatHotkey.md) -- [formatHotkeySequence](functions/formatHotkeySequence.md) -- [~~formatWithLabels~~](functions/formatWithLabels.md) +- [getAppHotkeyRegistrationOptions](functions/getAppHotkeyRegistrationOptions.md) - [getHotkeyManager](functions/getHotkeyManager.md) - [getKeyStateTracker](functions/getKeyStateTracker.md) -- [getSequenceManager](functions/getSequenceManager.md) - [hasNonModifierKey](functions/hasNonModifierKey.md) +- [isAppHotkey](functions/isAppHotkey.md) +- [isCustomHotkey](functions/isCustomHotkey.md) +- [isHotkeySequence](functions/isHotkeySequence.md) - [isModifierKey](functions/isModifierKey.md) - [isSingleLetterKey](functions/isSingleLetterKey.md) - [matchesHeldModifiers](functions/matchesHeldModifiers.md) @@ -128,7 +151,11 @@ title: "@tanstack/hotkeys" - [parseHotkey](functions/parseHotkey.md) - [parseKeyboardEvent](functions/parseKeyboardEvent.md) - [parseRegisterableHotkey](functions/parseRegisterableHotkey.md) +- [provideAppHotkeys](functions/provideAppHotkeys.md) +- [publishAppHotkeysStore](functions/publishAppHotkeysStore.md) - [rawHotkeyToParsedHotkey](functions/rawHotkeyToParsedHotkey.md) +- [resolveAppHotkey](functions/resolveAppHotkey.md) - [resolveModifier](functions/resolveModifier.md) +- [simplifyHotkey](functions/simplifyHotkey.md) - [toHotkeyRegistrationView](functions/toHotkeyRegistrationView.md) - [validateHotkey](functions/validateHotkey.md) diff --git a/docs/reference/interfaces/AppHotkey.md b/docs/reference/interfaces/AppHotkey.md new file mode 100644 index 00000000..d1cbf0f9 --- /dev/null +++ b/docs/reference/interfaces/AppHotkey.md @@ -0,0 +1,106 @@ +--- +id: AppHotkey +title: AppHotkey +--- + +Defined in: app-hotkeys.ts:69 + +A declared app hotkey. It never holds a callback: register it with +`useHotkey(appHotkey, callback)` or the equivalent adapter API. + +`TDefault` is the kind of the declared default (`HotkeyChord`, +`HotkeySequence`, or `null`). Adapters use it to type kind-specific options. + +## Type Parameters + +### TId + +`TId` *extends* `string` = `string` + +### TDefault + +`TDefault` *extends* + \| [`HotkeyChord`](../type-aliases/HotkeyChord.md) + \| [`HotkeySequence`](../type-aliases/HotkeySequence.md) + \| `null` = + \| [`HotkeyChord`](../type-aliases/HotkeyChord.md) + \| [`HotkeySequence`](../type-aliases/HotkeySequence.md) + \| `null` + +## Properties + +### ~appHotkey + +```ts +readonly ~appHotkey: true; +``` + +Defined in: app-hotkeys.ts:75 + +Brand that distinguishes app hotkeys from raw hotkey objects. + +*** + +### context? + +```ts +readonly optional context?: string; +``` + +Defined in: app-hotkeys.ts:83 + +*** + +### defaultHotkey + +```ts +readonly defaultHotkey: TDefault; +``` + +Defined in: app-hotkeys.ts:81 + +Hotkey declared in `defineAppHotkeys`. + +*** + +### hotkey + +```ts +readonly hotkey: Hotkey | null; +``` + +Defined in: app-hotkeys.ts:79 + +Effective hotkey: the user's custom hotkey, or the default. `null` means unbound. + +*** + +### id + +```ts +readonly id: TId; +``` + +Defined in: app-hotkeys.ts:77 + +Stable id. Saved custom hotkeys are keyed by this value. + +*** + +### meta? + +```ts +readonly optional meta?: HotkeyMeta; +``` + +Defined in: app-hotkeys.ts:82 + +*** + +### options? + +```ts +readonly optional options?: AppHotkeyOptions; +``` + +Defined in: app-hotkeys.ts:84 diff --git a/docs/reference/interfaces/AppHotkeyInput.md b/docs/reference/interfaces/AppHotkeyInput.md new file mode 100644 index 00000000..f250c8f9 --- /dev/null +++ b/docs/reference/interfaces/AppHotkeyInput.md @@ -0,0 +1,59 @@ +--- +id: AppHotkeyInput +title: AppHotkeyInput +--- + +Defined in: app-hotkeys.ts:44 + +One declaration passed to `defineAppHotkeys`. + +## Properties + +### context? + +```ts +optional context?: string; +``` + +Defined in: app-hotkeys.ts:54 + +Label for where this hotkey applies, such as `'editor'`. App hotkeys with +different contexts may share a hotkey; one without a context conflicts with +any app hotkey that shares its hotkey. It does not affect matching. + +*** + +### hotkey + +```ts +hotkey: Hotkey | null; +``` + +Defined in: app-hotkeys.ts:46 + +Default hotkey: a chord, a sequence, or `null` for an action without a default. + +*** + +### meta? + +```ts +optional meta?: HotkeyMeta; +``` + +Defined in: app-hotkeys.ts:48 + +Display metadata for cheat sheets and settings screens. + +*** + +### options? + +```ts +optional options?: AppHotkeyOptions; +``` + +Defined in: app-hotkeys.ts:59 + +Options that describe the hotkey itself. Chord defaults accept +`requireReset`; sequence defaults accept `timeout`. diff --git a/docs/reference/interfaces/AppHotkeyListItem.md b/docs/reference/interfaces/AppHotkeyListItem.md new file mode 100644 index 00000000..c75b7fb5 --- /dev/null +++ b/docs/reference/interfaces/AppHotkeyListItem.md @@ -0,0 +1,58 @@ +--- +id: AppHotkeyListItem +title: AppHotkeyListItem +--- + +Defined in: app-hotkeys.ts:307 + +One row of an app hotkey cheat sheet. + +## Type Parameters + +### T + +`T` *extends* [`AppHotkey`](AppHotkey.md) = [`AppHotkey`](AppHotkey.md) + +## Properties + +### appHotkey + +```ts +appHotkey: T; +``` + +Defined in: app-hotkeys.ts:308 + +*** + +### isCustom + +```ts +isCustom: boolean; +``` + +Defined in: app-hotkeys.ts:311 + +Whether the effective hotkey differs from the declared default. + +*** + +### registrations + +```ts +registrations: HotkeyRegistrationView[]; +``` + +Defined in: app-hotkeys.ts:313 + +Live registrations of this app hotkey (for example, one per list row). + +*** + +### status + +```ts +status: AppHotkeyStatus; +``` + +Defined in: app-hotkeys.ts:309 diff --git a/docs/reference/interfaces/AppHotkeysState.md b/docs/reference/interfaces/AppHotkeysState.md new file mode 100644 index 00000000..9277216f --- /dev/null +++ b/docs/reference/interfaces/AppHotkeysState.md @@ -0,0 +1,38 @@ +--- +id: AppHotkeysState +title: AppHotkeysState +--- + +Defined in: app-hotkeys.ts:317 + +State of an `AppHotkeysStore`. + +## Type Parameters + +### T + +`T` *extends* [`AppHotkeys`](../type-aliases/AppHotkeys.md) = [`AppHotkeys`](../type-aliases/AppHotkeys.md) + +## Properties + +### appHotkeys + +```ts +appHotkeys: T; +``` + +Defined in: app-hotkeys.ts:319 + +Effective app hotkeys: declarations with custom hotkeys applied. + +*** + +### customHotkeys + +```ts +customHotkeys: CustomHotkeys; +``` + +Defined in: app-hotkeys.ts:321 + +The user's hotkeys that differ from the defaults. diff --git a/docs/reference/interfaces/AppHotkeysStoreOptions.md b/docs/reference/interfaces/AppHotkeysStoreOptions.md new file mode 100644 index 00000000..4db8f65c --- /dev/null +++ b/docs/reference/interfaces/AppHotkeysStoreOptions.md @@ -0,0 +1,60 @@ +--- +id: AppHotkeysStoreOptions +title: AppHotkeysStoreOptions +--- + +Defined in: app-hotkeys.ts:325 + +Options for `AppHotkeysStore`. + +## Type Parameters + +### T + +`T` *extends* [`AppHotkeys`](../type-aliases/AppHotkeys.md) = [`AppHotkeys`](../type-aliases/AppHotkeys.md) + +## Properties + +### customHotkeys? + +```ts +optional customHotkeys?: CustomHotkeys | null; +``` + +Defined in: app-hotkeys.ts:327 + +Starting custom hotkeys, such as saved data. Invalid entries are ignored with a warning. + +*** + +### onCustomHotkeysChange? + +```ts +optional onCustomHotkeysChange?: (customHotkeys) => void; +``` + +Defined in: app-hotkeys.ts:329 + +Called with the custom hotkeys after every change, ready to save. + +#### Parameters + +##### customHotkeys + +[`CustomHotkeys`](../type-aliases/CustomHotkeys.md)\<`T`\> + +#### Returns + +`void` + +*** + +### platform? + +```ts +optional platform?: Platform; +``` + +Defined in: app-hotkeys.ts:331 + +Platform used to compare hotkeys. Defaults to detection. diff --git a/docs/reference/interfaces/CreateHotkeyHandlerOptions.md b/docs/reference/interfaces/CreateHotkeyHandlerOptions.md index 5ea774b2..d822a7cd 100644 --- a/docs/reference/interfaces/CreateHotkeyHandlerOptions.md +++ b/docs/reference/interfaces/CreateHotkeyHandlerOptions.md @@ -3,7 +3,7 @@ id: CreateHotkeyHandlerOptions title: CreateHotkeyHandlerOptions --- -Defined in: [match.ts:60](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L60) +Defined in: [match.ts:68](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L68) Options for creating a hotkey handler. @@ -15,7 +15,7 @@ Options for creating a hotkey handler. optional platform?: "mac" | "windows" | "linux"; ``` -Defined in: [match.ts:66](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L66) +Defined in: [match.ts:74](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L74) The target platform for resolving 'Mod' @@ -27,7 +27,7 @@ The target platform for resolving 'Mod' optional preventDefault?: boolean; ``` -Defined in: [match.ts:62](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L62) +Defined in: [match.ts:70](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L70) Prevent the default browser action when the hotkey matches. Defaults to true @@ -39,6 +39,6 @@ Prevent the default browser action when the hotkey matches. Defaults to true optional stopPropagation?: boolean; ``` -Defined in: [match.ts:64](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L64) +Defined in: [match.ts:72](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L72) Stop event propagation when the hotkey matches. Defaults to true diff --git a/docs/reference/interfaces/FormatDisplayOptions.md b/docs/reference/interfaces/FormatDisplayOptions.md index 5376a47d..82081016 100644 --- a/docs/reference/interfaces/FormatDisplayOptions.md +++ b/docs/reference/interfaces/FormatDisplayOptions.md @@ -3,7 +3,7 @@ id: FormatDisplayOptions title: FormatDisplayOptions --- -Defined in: [format.ts:195](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L195) +Defined in: [format.ts:210](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L210) Options for formatting hotkeys for display. @@ -15,7 +15,7 @@ Options for formatting hotkeys for display. optional keyLabels?: Readonly>; ``` -Defined in: [format.ts:210](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L210) +Defined in: [format.ts:225](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L225) Final display overrides for logical keys or physical codes; take precedence over layoutMap. Never changes matching. @@ -27,7 +27,7 @@ Final display overrides for logical keys or physical codes; take precedence over optional layoutMap?: object; ``` -Defined in: [format.ts:208](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L208) +Defined in: [format.ts:223](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L223) An already-resolved code-to-key map, such as KeyboardLayoutMap or Map. Used only for physical bindings. Mapped keys receive normal display formatting; @@ -58,7 +58,7 @@ get: (code) => string | undefined; optional parts?: boolean; ``` -Defined in: [format.ts:201](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L201) +Defined in: [format.ts:216](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L216) Return individual key labels instead of a joined string. Default: false. @@ -70,7 +70,7 @@ Return individual key labels instead of a joined string. Default: false. optional platform?: "mac" | "windows" | "linux"; ``` -Defined in: [format.ts:197](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L197) +Defined in: [format.ts:212](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L212) The target platform. Defaults to auto-detection. @@ -82,12 +82,24 @@ The target platform. Defaults to auto-detection. optional separatorToken?: string | null; ``` -Defined in: [format.ts:212](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L212) +Defined in: [format.ts:227](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L227) Override the separator between display tokens. Defaults to platform-specific formatting when null/undefined. *** +### sequenceSeparator? + +```ts +optional sequenceSeparator?: string; +``` + +Defined in: [format.ts:229](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L229) + +Separator between sequence steps. Default: a space. + +*** + ### useSymbols? ```ts @@ -99,6 +111,6 @@ optional useSymbols?: }; ``` -Defined in: [format.ts:199](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L199) +Defined in: [format.ts:214](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L214) Whether to use symbols for the display. Defaults to true. diff --git a/docs/reference/interfaces/HotkeyCallbackContext.md b/docs/reference/interfaces/HotkeyCallbackContext.md index 09db246e..0236a5c6 100644 --- a/docs/reference/interfaces/HotkeyCallbackContext.md +++ b/docs/reference/interfaces/HotkeyCallbackContext.md @@ -3,7 +3,7 @@ id: HotkeyCallbackContext title: HotkeyCallbackContext --- -Defined in: [hotkey.types.ts:212](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L212) +Defined in: [hotkey.types.ts:229](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L229) Context passed to hotkey callbacks along with the keyboard event. @@ -15,9 +15,9 @@ Context passed to hotkey callbacks along with the keyboard event. hotkey: Hotkey; ``` -Defined in: [hotkey.types.ts:214](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L214) +Defined in: [hotkey.types.ts:231](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L231) -The original hotkey string that was registered +The registered hotkey: a chord string or a sequence *** @@ -27,6 +27,6 @@ The original hotkey string that was registered parsedHotkey: ParsedHotkey; ``` -Defined in: [hotkey.types.ts:216](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L216) +Defined in: [hotkey.types.ts:233](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L233) -The parsed representation of the hotkey +The parsed chord that completed the match (the last step of a sequence) diff --git a/docs/reference/interfaces/HotkeyChordOptions.md b/docs/reference/interfaces/HotkeyChordOptions.md new file mode 100644 index 00000000..07720320 --- /dev/null +++ b/docs/reference/interfaces/HotkeyChordOptions.md @@ -0,0 +1,186 @@ +--- +id: HotkeyChordOptions +title: HotkeyChordOptions +--- + +Defined in: [hotkey-manager.ts:73](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L73) + +Options for a single chord such as `'Mod+S'`. + +## Extends + +- [`HotkeyOptions`](HotkeyOptions.md) + +## Properties + +### appHotkeyId? + +```ts +optional appHotkeyId?: string; +``` + +Defined in: [hotkey-manager.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L69) + +Id of the app hotkey this registration implements. Adapters set this when registering an app hotkey from `defineAppHotkeys`. + +#### Inherited from + +[`HotkeyOptions`](HotkeyOptions.md).[`appHotkeyId`](HotkeyOptions.md#apphotkeyid) + +*** + +### conflictBehavior? + +```ts +optional conflictBehavior?: ConflictBehavior; +``` + +Defined in: [hotkey-manager.ts:47](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L47) + +Behavior when this hotkey conflicts with an existing registration on the same target. Defaults to 'warn' + +#### Inherited from + +[`HotkeyOptions`](HotkeyOptions.md).[`conflictBehavior`](HotkeyOptions.md#conflictbehavior) + +*** + +### enabled? + +```ts +optional enabled?: boolean; +``` + +Defined in: [hotkey-manager.ts:53](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L53) + +Soft-disable: when `false`, the callback does not run but the registration +stays in `HotkeyManager` (and in devtools). Toggling this should update the +existing handle via `setOptions` rather than unregistering. Defaults to `true`. + +#### Inherited from + +[`HotkeyOptions`](HotkeyOptions.md).[`enabled`](HotkeyOptions.md#enabled) + +*** + +### eventType? + +```ts +optional eventType?: "keyup" | "keydown"; +``` + +Defined in: [hotkey-manager.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L55) + +The event type to listen for. Defaults to 'keydown' + +#### Inherited from + +[`HotkeyOptions`](HotkeyOptions.md).[`eventType`](HotkeyOptions.md#eventtype) + +*** + +### ignoreInputs? + +```ts +optional ignoreInputs?: boolean; +``` + +Defined in: [hotkey-manager.ts:57](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L57) + +Whether to ignore hotkeys when keyboard events originate from input-like elements (text inputs, textarea, select, contenteditable — button-type inputs like type=button/submit/reset are not ignored). Defaults based on the hotkey (the first step of a sequence): true for single keys and Shift/Alt combos; false for Ctrl/Meta shortcuts and Escape + +#### Inherited from + +[`HotkeyOptions`](HotkeyOptions.md).[`ignoreInputs`](HotkeyOptions.md#ignoreinputs) + +*** + +### meta? + +```ts +optional meta?: HotkeyMeta; +``` + +Defined in: [hotkey-manager.ts:67](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L67) + +Optional metadata (name, description, custom fields via declaration merging) + +#### Inherited from + +[`HotkeyOptions`](HotkeyOptions.md).[`meta`](HotkeyOptions.md#meta) + +*** + +### platform? + +```ts +optional platform?: "mac" | "windows" | "linux"; +``` + +Defined in: [hotkey-manager.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L59) + +The target platform for resolving 'Mod' + +#### Inherited from + +[`HotkeyOptions`](HotkeyOptions.md).[`platform`](HotkeyOptions.md#platform) + +*** + +### preventDefault? + +```ts +optional preventDefault?: boolean; +``` + +Defined in: [hotkey-manager.ts:61](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L61) + +Prevent the default browser action when the hotkey matches. Defaults to true + +#### Inherited from + +[`HotkeyOptions`](HotkeyOptions.md).[`preventDefault`](HotkeyOptions.md#preventdefault) + +*** + +### requireReset? + +```ts +optional requireReset?: boolean; +``` + +Defined in: [hotkey-manager.ts:75](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L75) + +If true, only trigger once until all keys are released. Default: false + +*** + +### stopPropagation? + +```ts +optional stopPropagation?: boolean; +``` + +Defined in: [hotkey-manager.ts:63](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L63) + +Stop event propagation when the hotkey matches. Defaults to true + +#### Inherited from + +[`HotkeyOptions`](HotkeyOptions.md).[`stopPropagation`](HotkeyOptions.md#stoppropagation) + +*** + +### target? + +```ts +optional target?: Target | null; +``` + +Defined in: [hotkey-manager.ts:65](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L65) + +The DOM element to attach the event listener to. Defaults to document. + +#### Inherited from + +[`HotkeyOptions`](HotkeyOptions.md).[`target`](HotkeyOptions.md#target) diff --git a/docs/reference/interfaces/HotkeyRecorderOptions.md b/docs/reference/interfaces/HotkeyChordRecorderOptions.md similarity index 54% rename from docs/reference/interfaces/HotkeyRecorderOptions.md rename to docs/reference/interfaces/HotkeyChordRecorderOptions.md index 98c9c661..02f6c9fe 100644 --- a/docs/reference/interfaces/HotkeyRecorderOptions.md +++ b/docs/reference/interfaces/HotkeyChordRecorderOptions.md @@ -1,15 +1,15 @@ --- -id: HotkeyRecorderOptions -title: HotkeyRecorderOptions +id: HotkeyChordRecorderOptions +title: HotkeyChordRecorderOptions --- -Defined in: [hotkey-recorder.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L33) +Defined in: [hotkey-recorder.ts:53](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L53) -Options for configuring a HotkeyRecorder instance. +Options for recording a single chord. This is the default (`maxSteps: 1`). ## Extends -- [`RecorderOptions`](RecorderOptions.md) +- `HotkeyRecorderBaseOptions` ## Properties @@ -25,7 +25,9 @@ Opt in to live-registry conflicts, including sequence prefixes. Default: false. #### Inherited from -[`RecorderOptions`](RecorderOptions.md).[`detectConflicts`](RecorderOptions.md#detectconflicts) +```ts +HotkeyRecorderBaseOptions.detectConflicts +``` *** @@ -35,19 +37,30 @@ Opt in to live-registry conflicts, including sequence prefixes. Default: false. optional ignoreInputs?: boolean; ``` -Defined in: [hotkey-recorder.ts:52](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L52) +Defined in: [hotkey-recorder.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L49) Whether to ignore keyboard events from input-like elements (text inputs, -textarea, select, contenteditable). When true, typing in inputs passes -through normally instead of being captured as a hotkey recording. -Escape always works regardless of this setting. +textarea, select, contenteditable) while recording. When true, typing in +inputs passes through normally (Escape still cancels). Defaults to true. + +#### Inherited from + +```ts +HotkeyRecorderBaseOptions.ignoreInputs +``` -#### Default +*** + +### maxSteps? ```ts -true +optional maxSteps?: 1; ``` +Defined in: [hotkey-recorder.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L55) + +Maximum number of chords to record. Default: 1 + *** ### onCancel? @@ -56,7 +69,7 @@ true optional onCancel?: () => void; ``` -Defined in: [hotkey-recorder.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L42) +Defined in: [hotkey-recorder.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L38) Optional callback when recording is cancelled (Escape pressed) @@ -64,6 +77,12 @@ Optional callback when recording is cancelled (Escape pressed) `void` +#### Inherited from + +```ts +HotkeyRecorderBaseOptions.onCancel +``` + *** ### onClear? @@ -72,14 +91,21 @@ Optional callback when recording is cancelled (Escape pressed) optional onClear?: () => void; ``` -Defined in: [hotkey-recorder.ts:44](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L44) +Defined in: [hotkey-recorder.ts:43](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L43) -Optional callback when shortcut is cleared (Backspace/Delete pressed) +Optional callback when the recording is cleared: Backspace/Delete with no +modifiers and no recorded steps #### Returns `void` +#### Inherited from + +```ts +HotkeyRecorderBaseOptions.onClear +``` + *** ### onRecord @@ -88,15 +114,15 @@ Optional callback when shortcut is cleared (Backspace/Delete pressed) onRecord: (hotkey) => void; ``` -Defined in: [hotkey-recorder.ts:40](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L40) +Defined in: [hotkey-recorder.ts:62](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L62) -Callback when a hotkey is successfully recorded +Callback when a chord is successfully recorded #### Parameters ##### hotkey -[`Hotkey`](../type-aliases/Hotkey.md) +[`HotkeyChord`](../type-aliases/HotkeyChord.md) #### Returns @@ -126,7 +152,9 @@ Rejected candidates leave recording active. #### Inherited from -[`RecorderOptions`](RecorderOptions.md).[`onReject`](RecorderOptions.md#onreject) +```ts +HotkeyRecorderBaseOptions.onReject +``` *** @@ -142,7 +170,9 @@ Platform used for Mod and AltGraph handling. Defaults to platform detection. #### Inherited from -[`RecorderOptions`](RecorderOptions.md).[`platform`](RecorderOptions.md#platform) +```ts +HotkeyRecorderBaseOptions.platform +``` *** @@ -158,7 +188,9 @@ Physical code recording is the default. Choose key to record logical characters. #### Inherited from -[`RecorderOptions`](RecorderOptions.md).[`recordBy`](RecorderOptions.md#recordby) +```ts +HotkeyRecorderBaseOptions.recordBy +``` *** @@ -168,7 +200,7 @@ Physical code recording is the default. Choose key to record logical characters. optional validate?: (hotkey, context) => string | boolean; ``` -Defined in: [hotkey-recorder.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L35) +Defined in: [hotkey-recorder.ts:57](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L57) Return true to accept, or false/a message to reject while staying in recording mode. @@ -176,7 +208,7 @@ Return true to accept, or false/a message to reject while staying in recording m ##### hotkey -[`Hotkey`](../type-aliases/Hotkey.md) +[`HotkeyChord`](../type-aliases/HotkeyChord.md) ##### context diff --git a/docs/reference/interfaces/HotkeyChordRegistration.md b/docs/reference/interfaces/HotkeyChordRegistration.md new file mode 100644 index 00000000..7788066c --- /dev/null +++ b/docs/reference/interfaces/HotkeyChordRegistration.md @@ -0,0 +1,174 @@ +--- +id: HotkeyChordRegistration +title: HotkeyChordRegistration +--- + +Defined in: [hotkey-manager.ts:112](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L112) + +A registered chord in the HotkeyManager. + +## Extends + +- `RegistrationBase`\<[`HotkeyChordOptions`](HotkeyChordOptions.md)\> + +## Properties + +### activeMatch? + +```ts +optional activeMatch?: object; +``` + +Defined in: [hotkey-manager.ts:117](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L117) + +The concrete key/code that activated a requireReset registration. + +#### code + +```ts +code: string; +``` + +#### key + +```ts +key: string; +``` + +*** + +### callback + +```ts +callback: HotkeyCallback; +``` + +Defined in: [hotkey-manager.ts:100](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L100) + +The callback to invoke + +#### Inherited from + +```ts +RegistrationBase.callback +``` + +*** + +### hasFired + +```ts +hasFired: boolean; +``` + +Defined in: [hotkey-manager.ts:108](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L108) + +Whether this registration has fired (and, for requireReset chords, needs a reset) + +#### Inherited from + +```ts +RegistrationBase.hasFired +``` + +*** + +### hotkey + +```ts +hotkey: HotkeyChord; +``` + +Defined in: [hotkey-manager.ts:114](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L114) + +*** + +### id + +```ts +id: string; +``` + +Defined in: [hotkey-manager.ts:98](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L98) + +Unique identifier for this registration + +#### Inherited from + +```ts +RegistrationBase.id +``` + +*** + +### kind + +```ts +kind: "chord"; +``` + +Defined in: [hotkey-manager.ts:113](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L113) + +*** + +### options + +```ts +options: HotkeyChordOptions; +``` + +Defined in: [hotkey-manager.ts:102](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L102) + +Resolved options for this registration + +#### Inherited from + +```ts +RegistrationBase.options +``` + +*** + +### parsedHotkey + +```ts +parsedHotkey: ParsedHotkey; +``` + +Defined in: [hotkey-manager.ts:115](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L115) + +*** + +### target + +```ts +target: Target; +``` + +Defined in: [hotkey-manager.ts:104](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L104) + +The resolved target element for this registration + +#### Inherited from + +```ts +RegistrationBase.target +``` + +*** + +### triggerCount + +```ts +triggerCount: number; +``` + +Defined in: [hotkey-manager.ts:106](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L106) + +How many times this registration's callback has been triggered + +#### Inherited from + +```ts +RegistrationBase.triggerCount +``` diff --git a/docs/reference/interfaces/HotkeyChordRegistrationView.md b/docs/reference/interfaces/HotkeyChordRegistrationView.md new file mode 100644 index 00000000..3d428710 --- /dev/null +++ b/docs/reference/interfaces/HotkeyChordRegistrationView.md @@ -0,0 +1,116 @@ +--- +id: HotkeyChordRegistrationView +title: HotkeyChordRegistrationView +--- + +Defined in: [hotkey-manager.ts:143](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L143) + +Public view of a chord registration, without its callback. + +## Extends + +- `RegistrationViewBase` + +## Properties + +### hasFired + +```ts +hasFired: boolean; +``` + +Defined in: [hotkey-manager.ts:139](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L139) + +#### Inherited from + +```ts +RegistrationViewBase.hasFired +``` + +*** + +### hotkey + +```ts +hotkey: HotkeyChord; +``` + +Defined in: [hotkey-manager.ts:145](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L145) + +*** + +### id + +```ts +id: string; +``` + +Defined in: [hotkey-manager.ts:136](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L136) + +#### Inherited from + +```ts +RegistrationViewBase.id +``` + +*** + +### kind + +```ts +kind: "chord"; +``` + +Defined in: [hotkey-manager.ts:144](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L144) + +*** + +### options + +```ts +options: HotkeyChordOptions; +``` + +Defined in: [hotkey-manager.ts:147](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L147) + +*** + +### parsedHotkey + +```ts +parsedHotkey: ParsedHotkey; +``` + +Defined in: [hotkey-manager.ts:146](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L146) + +*** + +### target + +```ts +target: Target; +``` + +Defined in: [hotkey-manager.ts:137](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L137) + +#### Inherited from + +```ts +RegistrationViewBase.target +``` + +*** + +### triggerCount + +```ts +triggerCount: number; +``` + +Defined in: [hotkey-manager.ts:138](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L138) + +#### Inherited from + +```ts +RegistrationViewBase.triggerCount +``` diff --git a/docs/reference/interfaces/HotkeyConflict.md b/docs/reference/interfaces/HotkeyConflict.md new file mode 100644 index 00000000..7d7b5eed --- /dev/null +++ b/docs/reference/interfaces/HotkeyConflict.md @@ -0,0 +1,18 @@ +--- +id: HotkeyConflict +title: HotkeyConflict +--- + +Defined in: [conflicts.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L13) + +A live registration that overlaps a candidate hotkey. + +## Properties + +### registration + +```ts +registration: HotkeyRegistrationView; +``` + +Defined in: [conflicts.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L14) diff --git a/docs/reference/interfaces/HotkeyConflictOptions.md b/docs/reference/interfaces/HotkeyConflictOptions.md index 5a695e13..c085cc41 100644 --- a/docs/reference/interfaces/HotkeyConflictOptions.md +++ b/docs/reference/interfaces/HotkeyConflictOptions.md @@ -3,7 +3,7 @@ id: HotkeyConflictOptions title: HotkeyConflictOptions --- -Defined in: [conflicts.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L15) +Defined in: [conflicts.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L17) ## Properties @@ -25,7 +25,7 @@ Source events allow detecting physical/logical overlap on the recorded layout. optional eventType?: "keyup" | "keydown"; ``` -Defined in: [conflicts.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L21) +Defined in: [conflicts.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L23) Intended event type. Default: keydown. @@ -37,7 +37,7 @@ Intended event type. Default: keydown. optional exclude?: (registration) => boolean; ``` -Defined in: [conflicts.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L27) +Defined in: [conflicts.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L29) Additional app-specific exclusions, such as all instances of one action. @@ -45,8 +45,7 @@ Additional app-specific exclusions, such as all instances of one action. ##### registration - \| [`HotkeyRegistrationView`](HotkeyRegistrationView.md) - \| [`SequenceRegistrationView`](SequenceRegistrationView.md) +[`HotkeyRegistrationView`](../type-aliases/HotkeyRegistrationView.md) #### Returns @@ -60,7 +59,7 @@ Additional app-specific exclusions, such as all instances of one action. optional excludeIds?: readonly string[]; ``` -Defined in: [conflicts.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L25) +Defined in: [conflicts.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L27) IDs of registrations being edited. @@ -72,7 +71,7 @@ IDs of registrations being edited. optional includeDisabled?: boolean; ``` -Defined in: [conflicts.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L23) +Defined in: [conflicts.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L25) Include disabled registrations. Default: false. @@ -96,7 +95,7 @@ Platform used to resolve Mod in the candidate. Registrations retain their own pl optional scope?: "all" | "overlapping"; ``` -Defined in: [conflicts.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L19) +Defined in: [conflicts.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L21) Check all targets instead of overlapping targets. Default: overlapping. @@ -108,6 +107,6 @@ Check all targets instead of overlapping targets. Default: overlapping. optional target?: Document | Window | HTMLElement; ``` -Defined in: [conflicts.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L17) +Defined in: [conflicts.ts:19](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L19) Intended registration target. Defaults to document; disjoint targets are excluded. diff --git a/docs/reference/interfaces/HotkeyMeta.md b/docs/reference/interfaces/HotkeyMeta.md index 4425f11c..4cf85c3b 100644 --- a/docs/reference/interfaces/HotkeyMeta.md +++ b/docs/reference/interfaces/HotkeyMeta.md @@ -3,9 +3,9 @@ id: HotkeyMeta title: HotkeyMeta --- -Defined in: [hotkey.types.ts:252](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L252) +Defined in: [hotkey.types.ts:269](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L269) -Metadata for hotkey and sequence registrations. +Metadata for hotkey registrations, chords and sequences alike. Includes `name`, `description`, and `group` by default. Extend via declaration merging: ## Example @@ -27,7 +27,7 @@ declare module '@tanstack/hotkeys' { optional description?: string; ``` -Defined in: [hotkey.types.ts:256](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L256) +Defined in: [hotkey.types.ts:273](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L273) Description of what this hotkey does @@ -39,7 +39,7 @@ Description of what this hotkey does optional group?: string; ``` -Defined in: [hotkey.types.ts:258](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L258) +Defined in: [hotkey.types.ts:275](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L275) Optional display group. Does not affect matching or scope. @@ -51,6 +51,6 @@ Optional display group. Does not affect matching or scope. optional name?: string; ``` -Defined in: [hotkey.types.ts:254](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L254) +Defined in: [hotkey.types.ts:271](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L271) Human-readable name for this hotkey (e.g., "Save Document") diff --git a/docs/reference/interfaces/HotkeyOptions.md b/docs/reference/interfaces/HotkeyOptions.md index 5bbc1596..1de688b0 100644 --- a/docs/reference/interfaces/HotkeyOptions.md +++ b/docs/reference/interfaces/HotkeyOptions.md @@ -3,19 +3,36 @@ id: HotkeyOptions title: HotkeyOptions --- -Defined in: [hotkey-manager.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L31) +Defined in: [hotkey-manager.ts:45](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L45) + +Options shared by chords and sequences. -Options for registering a hotkey. +## Extended by + +- [`HotkeyChordOptions`](HotkeyChordOptions.md) +- [`HotkeySequenceOptions`](HotkeySequenceOptions.md) ## Properties +### appHotkeyId? + +```ts +optional appHotkeyId?: string; +``` + +Defined in: [hotkey-manager.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L69) + +Id of the app hotkey this registration implements. Adapters set this when registering an app hotkey from `defineAppHotkeys`. + +*** + ### conflictBehavior? ```ts optional conflictBehavior?: ConflictBehavior; ``` -Defined in: [hotkey-manager.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L33) +Defined in: [hotkey-manager.ts:47](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L47) Behavior when this hotkey conflicts with an existing registration on the same target. Defaults to 'warn' @@ -27,7 +44,7 @@ Behavior when this hotkey conflicts with an existing registration on the same ta optional enabled?: boolean; ``` -Defined in: [hotkey-manager.ts:39](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L39) +Defined in: [hotkey-manager.ts:53](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L53) Soft-disable: when `false`, the callback does not run but the registration stays in `HotkeyManager` (and in devtools). Toggling this should update the @@ -41,7 +58,7 @@ existing handle via `setOptions` rather than unregistering. Defaults to `true`. optional eventType?: "keyup" | "keydown"; ``` -Defined in: [hotkey-manager.ts:41](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L41) +Defined in: [hotkey-manager.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L55) The event type to listen for. Defaults to 'keydown' @@ -53,9 +70,9 @@ The event type to listen for. Defaults to 'keydown' optional ignoreInputs?: boolean; ``` -Defined in: [hotkey-manager.ts:43](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L43) +Defined in: [hotkey-manager.ts:57](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L57) -Whether to ignore hotkeys when keyboard events originate from input-like elements (text inputs, textarea, select, contenteditable — button-type inputs like type=button/submit/reset are not ignored). Defaults based on hotkey: true for single keys and Shift/Alt combos; false for Ctrl/Meta shortcuts and Escape +Whether to ignore hotkeys when keyboard events originate from input-like elements (text inputs, textarea, select, contenteditable — button-type inputs like type=button/submit/reset are not ignored). Defaults based on the hotkey (the first step of a sequence): true for single keys and Shift/Alt combos; false for Ctrl/Meta shortcuts and Escape *** @@ -65,7 +82,7 @@ Whether to ignore hotkeys when keyboard events originate from input-like element optional meta?: HotkeyMeta; ``` -Defined in: [hotkey-manager.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L55) +Defined in: [hotkey-manager.ts:67](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L67) Optional metadata (name, description, custom fields via declaration merging) @@ -77,7 +94,7 @@ Optional metadata (name, description, custom fields via declaration merging) optional platform?: "mac" | "windows" | "linux"; ``` -Defined in: [hotkey-manager.ts:45](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L45) +Defined in: [hotkey-manager.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L59) The target platform for resolving 'Mod' @@ -89,31 +106,19 @@ The target platform for resolving 'Mod' optional preventDefault?: boolean; ``` -Defined in: [hotkey-manager.ts:47](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L47) +Defined in: [hotkey-manager.ts:61](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L61) Prevent the default browser action when the hotkey matches. Defaults to true *** -### requireReset? - -```ts -optional requireReset?: boolean; -``` - -Defined in: [hotkey-manager.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L49) - -If true, only trigger once until all keys are released. Default: false - -*** - ### stopPropagation? ```ts optional stopPropagation?: boolean; ``` -Defined in: [hotkey-manager.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L51) +Defined in: [hotkey-manager.ts:63](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L63) Stop event propagation when the hotkey matches. Defaults to true @@ -122,9 +127,9 @@ Stop event propagation when the hotkey matches. Defaults to true ### target? ```ts -optional target?: Document | Window | HTMLElement | null; +optional target?: Target | null; ``` -Defined in: [hotkey-manager.ts:53](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L53) +Defined in: [hotkey-manager.ts:65](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L65) The DOM element to attach the event listener to. Defaults to document. diff --git a/docs/reference/interfaces/HotkeyRecorderState.md b/docs/reference/interfaces/HotkeyRecorderState.md index 0d1b0769..94e9145e 100644 --- a/docs/reference/interfaces/HotkeyRecorderState.md +++ b/docs/reference/interfaces/HotkeyRecorderState.md @@ -3,7 +3,7 @@ id: HotkeyRecorderState title: HotkeyRecorderState --- -Defined in: [hotkey-recorder.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L23) +Defined in: [hotkey-recorder.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L24) State interface for the HotkeyRecorder. @@ -15,7 +15,7 @@ State interface for the HotkeyRecorder. isRecording: boolean; ``` -Defined in: [hotkey-recorder.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L25) +Defined in: [hotkey-recorder.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L26) Whether recording is currently active @@ -27,6 +27,18 @@ Whether recording is currently active recordedHotkey: Hotkey | null; ``` -Defined in: [hotkey-recorder.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L27) +Defined in: [hotkey-recorder.ts:30](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L30) -The currently recorded hotkey (for live preview) +The last committed hotkey: a chord, or a sequence of two or more steps + +*** + +### steps + +```ts +steps: HotkeySequence; +``` + +Defined in: [hotkey-recorder.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L28) + +Chords recorded so far in a multi-step recording (empty for `maxSteps: 1`) diff --git a/docs/reference/interfaces/HotkeyRecorderValidationContext.md b/docs/reference/interfaces/HotkeyRecorderValidationContext.md index 03431de1..0510fa6a 100644 --- a/docs/reference/interfaces/HotkeyRecorderValidationContext.md +++ b/docs/reference/interfaces/HotkeyRecorderValidationContext.md @@ -3,7 +3,9 @@ id: HotkeyRecorderValidationContext title: HotkeyRecorderValidationContext --- -Defined in: [recorder-options.ts:26](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L26) +Defined in: [recorder-options.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L27) + +Validation context for a recorded chord or sequence. ## Properties @@ -13,7 +15,21 @@ Defined in: [recorder-options.ts:26](https://github.com/TanStack/hotkeys/blob/ma event: KeyboardEvent; ``` -Defined in: [recorder-options.ts:27](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L27) +Defined in: [recorder-options.ts:29](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L29) + +The event that recorded the last chord. + +*** + +### events + +```ts +events: readonly KeyboardEvent[]; +``` + +Defined in: [recorder-options.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L33) + +The event that recorded each step. *** @@ -23,4 +39,18 @@ Defined in: [recorder-options.ts:27](https://github.com/TanStack/hotkeys/blob/ma parsedHotkey: ParsedHotkey; ``` -Defined in: [recorder-options.ts:28](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L28) +Defined in: [recorder-options.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L31) + +The last chord, parsed. + +*** + +### parsedSequence + +```ts +parsedSequence: readonly ParsedHotkey[]; +``` + +Defined in: [recorder-options.ts:35](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L35) + +Every step, parsed. diff --git a/docs/reference/interfaces/HotkeyRegistration.md b/docs/reference/interfaces/HotkeyRegistration.md deleted file mode 100644 index 944b2f40..00000000 --- a/docs/reference/interfaces/HotkeyRegistration.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -id: HotkeyRegistration -title: HotkeyRegistration ---- - -Defined in: [hotkey-manager.ts:61](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L61) - -A registered hotkey handler in the HotkeyManager. - -## Properties - -### activeMatch? - -```ts -optional activeMatch?: object; -``` - -Defined in: [hotkey-manager.ts:67](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L67) - -The concrete key/code that activated a requireReset registration. - -#### code - -```ts -code: string; -``` - -#### key - -```ts -key: string; -``` - -*** - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [hotkey-manager.ts:63](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L63) - -The callback to invoke - -*** - -### hasFired - -```ts -hasFired: boolean; -``` - -Defined in: [hotkey-manager.ts:65](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L65) - -Whether this registration has fired and needs reset (for requireReset) - -*** - -### hotkey - -```ts -hotkey: Hotkey; -``` - -Defined in: [hotkey-manager.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L69) - -The original hotkey string - -*** - -### id - -```ts -id: string; -``` - -Defined in: [hotkey-manager.ts:71](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L71) - -Unique identifier for this registration - -*** - -### options - -```ts -options: HotkeyOptions; -``` - -Defined in: [hotkey-manager.ts:73](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L73) - -Options for this registration - -*** - -### parsedHotkey - -```ts -parsedHotkey: ParsedHotkey; -``` - -Defined in: [hotkey-manager.ts:75](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L75) - -The parsed hotkey - -*** - -### target - -```ts -target: Document | Window | HTMLElement; -``` - -Defined in: [hotkey-manager.ts:77](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L77) - -The resolved target element for this registration - -*** - -### triggerCount - -```ts -triggerCount: number; -``` - -Defined in: [hotkey-manager.ts:79](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L79) - -How many times this registration's callback has been triggered diff --git a/docs/reference/interfaces/HotkeyRegistrationHandle.md b/docs/reference/interfaces/HotkeyRegistrationHandle.md index 33d2dfaf..e9de1fe0 100644 --- a/docs/reference/interfaces/HotkeyRegistrationHandle.md +++ b/docs/reference/interfaces/HotkeyRegistrationHandle.md @@ -3,7 +3,7 @@ id: HotkeyRegistrationHandle title: HotkeyRegistrationHandle --- -Defined in: [hotkey-manager.ts:144](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L144) +Defined in: [hotkey-manager.ts:219](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L219) A handle returned from HotkeyManager.register() that allows updating the callback and options without re-registering the hotkey. @@ -19,15 +19,18 @@ handle.callback = newCallback // Update options without re-registering handle.setOptions({ enabled: false }) -// Check if still active -if (handle.isActive) { - // ... -} - // Unregister when done handle.unregister() ``` +## Type Parameters + +### TOptions + +`TOptions` *extends* [`HotkeyOptions`](HotkeyOptions.md) = + \| [`HotkeyChordOptions`](HotkeyChordOptions.md) + \| [`HotkeySequenceOptions`](HotkeySequenceOptions.md) + ## Properties ### callback @@ -36,7 +39,7 @@ handle.unregister() callback: HotkeyCallback; ``` -Defined in: [hotkey-manager.ts:149](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L149) +Defined in: [hotkey-manager.ts:226](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L226) The callback function. Can be set directly to update without re-registering. This avoids stale closures when the callback references React state. @@ -49,7 +52,7 @@ This avoids stale closures when the callback references React state. readonly id: string; ``` -Defined in: [hotkey-manager.ts:151](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L151) +Defined in: [hotkey-manager.ts:228](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L228) Unique identifier for this registration @@ -61,7 +64,7 @@ Unique identifier for this registration readonly isActive: boolean; ``` -Defined in: [hotkey-manager.ts:153](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L153) +Defined in: [hotkey-manager.ts:230](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L230) Check if this registration is still active (not unregistered) @@ -73,7 +76,7 @@ Check if this registration is still active (not unregistered) setOptions: (options) => void; ``` -Defined in: [hotkey-manager.ts:158](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L158) +Defined in: [hotkey-manager.ts:235](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L235) Update options (merged with existing options). Useful for updating `enabled`, `preventDefault`, etc. without re-registering. @@ -82,7 +85,7 @@ Useful for updating `enabled`, `preventDefault`, etc. without re-registering. ##### options -`Partial`\<[`HotkeyOptions`](HotkeyOptions.md)\> +`Partial`\<`TOptions`\> #### Returns @@ -96,7 +99,7 @@ Useful for updating `enabled`, `preventDefault`, etc. without re-registering. unregister: () => void; ``` -Defined in: [hotkey-manager.ts:160](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L160) +Defined in: [hotkey-manager.ts:237](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L237) Unregister this hotkey diff --git a/docs/reference/interfaces/HotkeyRegistrationView.md b/docs/reference/interfaces/HotkeyRegistrationView.md deleted file mode 100644 index 71c34bb8..00000000 --- a/docs/reference/interfaces/HotkeyRegistrationView.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -id: HotkeyRegistrationView -title: HotkeyRegistrationView ---- - -Defined in: [hotkey-manager.ts:86](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L86) - -Public view of a hotkey registration for display and introspection. -Omits the callback function which is an internal implementation detail. - -## Properties - -### hasFired - -```ts -hasFired: boolean; -``` - -Defined in: [hotkey-manager.ts:100](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L100) - -Whether this registration has fired and needs reset (for requireReset) - -*** - -### hotkey - -```ts -hotkey: Hotkey; -``` - -Defined in: [hotkey-manager.ts:88](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L88) - -The original hotkey string - -*** - -### id - -```ts -id: string; -``` - -Defined in: [hotkey-manager.ts:90](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L90) - -Unique identifier for this registration - -*** - -### options - -```ts -options: HotkeyOptions; -``` - -Defined in: [hotkey-manager.ts:92](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L92) - -Options for this registration - -*** - -### parsedHotkey - -```ts -parsedHotkey: ParsedHotkey; -``` - -Defined in: [hotkey-manager.ts:94](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L94) - -The parsed hotkey - -*** - -### target - -```ts -target: Document | Window | HTMLElement; -``` - -Defined in: [hotkey-manager.ts:96](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L96) - -The resolved target element for this registration - -*** - -### triggerCount - -```ts -triggerCount: number; -``` - -Defined in: [hotkey-manager.ts:98](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L98) - -How many times this registration's callback has been triggered diff --git a/docs/reference/interfaces/SequenceOptions.md b/docs/reference/interfaces/HotkeySequenceOptions.md similarity index 61% rename from docs/reference/interfaces/SequenceOptions.md rename to docs/reference/interfaces/HotkeySequenceOptions.md index 19991eb2..46e84adb 100644 --- a/docs/reference/interfaces/SequenceOptions.md +++ b/docs/reference/interfaces/HotkeySequenceOptions.md @@ -1,26 +1,41 @@ --- -id: SequenceOptions -title: SequenceOptions +id: HotkeySequenceOptions +title: HotkeySequenceOptions --- -Defined in: [sequence-manager.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L31) +Defined in: [hotkey-manager.ts:79](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L79) -Options for hotkey sequence matching. -Extends HotkeyOptions but excludes requireReset (not applicable to sequences). +Options for a sequence such as `['G', 'G']`. ## Extends -- `Omit`\<[`HotkeyOptions`](HotkeyOptions.md), `"requireReset"`\> +- [`HotkeyOptions`](HotkeyOptions.md) ## Properties +### appHotkeyId? + +```ts +optional appHotkeyId?: string; +``` + +Defined in: [hotkey-manager.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L69) + +Id of the app hotkey this registration implements. Adapters set this when registering an app hotkey from `defineAppHotkeys`. + +#### Inherited from + +[`HotkeyOptions`](HotkeyOptions.md).[`appHotkeyId`](HotkeyOptions.md#apphotkeyid) + +*** + ### conflictBehavior? ```ts optional conflictBehavior?: ConflictBehavior; ``` -Defined in: [hotkey-manager.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L33) +Defined in: [hotkey-manager.ts:47](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L47) Behavior when this hotkey conflicts with an existing registration on the same target. Defaults to 'warn' @@ -36,7 +51,7 @@ Behavior when this hotkey conflicts with an existing registration on the same ta optional enabled?: boolean; ``` -Defined in: [hotkey-manager.ts:39](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L39) +Defined in: [hotkey-manager.ts:53](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L53) Soft-disable: when `false`, the callback does not run but the registration stays in `HotkeyManager` (and in devtools). Toggling this should update the @@ -54,7 +69,7 @@ existing handle via `setOptions` rather than unregistering. Defaults to `true`. optional eventType?: "keyup" | "keydown"; ``` -Defined in: [hotkey-manager.ts:41](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L41) +Defined in: [hotkey-manager.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L55) The event type to listen for. Defaults to 'keydown' @@ -70,9 +85,9 @@ The event type to listen for. Defaults to 'keydown' optional ignoreInputs?: boolean; ``` -Defined in: [hotkey-manager.ts:43](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L43) +Defined in: [hotkey-manager.ts:57](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L57) -Whether to ignore hotkeys when keyboard events originate from input-like elements (text inputs, textarea, select, contenteditable — button-type inputs like type=button/submit/reset are not ignored). Defaults based on hotkey: true for single keys and Shift/Alt combos; false for Ctrl/Meta shortcuts and Escape +Whether to ignore hotkeys when keyboard events originate from input-like elements (text inputs, textarea, select, contenteditable — button-type inputs like type=button/submit/reset are not ignored). Defaults based on the hotkey (the first step of a sequence): true for single keys and Shift/Alt combos; false for Ctrl/Meta shortcuts and Escape #### Inherited from @@ -86,7 +101,7 @@ Whether to ignore hotkeys when keyboard events originate from input-like element optional meta?: HotkeyMeta; ``` -Defined in: [hotkey-manager.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L55) +Defined in: [hotkey-manager.ts:67](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L67) Optional metadata (name, description, custom fields via declaration merging) @@ -102,7 +117,7 @@ Optional metadata (name, description, custom fields via declaration merging) optional platform?: "mac" | "windows" | "linux"; ``` -Defined in: [hotkey-manager.ts:45](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L45) +Defined in: [hotkey-manager.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L59) The target platform for resolving 'Mod' @@ -118,7 +133,7 @@ The target platform for resolving 'Mod' optional preventDefault?: boolean; ``` -Defined in: [hotkey-manager.ts:47](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L47) +Defined in: [hotkey-manager.ts:61](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L61) Prevent the default browser action when the hotkey matches. Defaults to true @@ -134,7 +149,7 @@ Prevent the default browser action when the hotkey matches. Defaults to true optional stopPropagation?: boolean; ``` -Defined in: [hotkey-manager.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L51) +Defined in: [hotkey-manager.ts:63](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L63) Stop event propagation when the hotkey matches. Defaults to true @@ -147,10 +162,10 @@ Stop event propagation when the hotkey matches. Defaults to true ### target? ```ts -optional target?: Document | Window | HTMLElement | null; +optional target?: Target | null; ``` -Defined in: [hotkey-manager.ts:53](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L53) +Defined in: [hotkey-manager.ts:65](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L65) The DOM element to attach the event listener to. Defaults to document. @@ -166,6 +181,6 @@ The DOM element to attach the event listener to. Defaults to document. optional timeout?: number; ``` -Defined in: [sequence-manager.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L33) +Defined in: [hotkey-manager.ts:81](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L81) -Timeout between keys in milliseconds. Default: 1000 +Maximum time between steps in milliseconds. Default: 1000 diff --git a/docs/reference/interfaces/HotkeySequenceRecorderOptions.md b/docs/reference/interfaces/HotkeySequenceRecorderOptions.md index b117454a..1da3f77f 100644 --- a/docs/reference/interfaces/HotkeySequenceRecorderOptions.md +++ b/docs/reference/interfaces/HotkeySequenceRecorderOptions.md @@ -3,49 +3,26 @@ id: HotkeySequenceRecorderOptions title: HotkeySequenceRecorderOptions --- -Defined in: [hotkey-sequence-recorder.ts:42](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L42) +Defined in: [hotkey-recorder.ts:69](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L69) -Options for configuring a HotkeySequenceRecorder instance. +Options for recording up to `maxSteps` chords. A one-step recording commits +as a chord string; two or more steps commit as a sequence. ## Extends -- [`RecorderOptions`](RecorderOptions.md) +- `HotkeyRecorderBaseOptions` ## Properties ### commitKeys? ```ts -optional commitKeys?: HotkeySequenceRecorderCommitKeys; +optional commitKeys?: HotkeyRecorderCommitKeys; ``` -Defined in: [hotkey-sequence-recorder.ts:63](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L63) +Defined in: [hotkey-recorder.ts:80](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L80) -Keyboard commit mode. When `'none'`, use [HotkeySequenceRecorder.commit](../classes/HotkeySequenceRecorder.md#commit) (and optional idle timeout). - -#### Default - -```ts -'enter' -``` - -*** - -### commitOnEnter? - -```ts -optional commitOnEnter?: boolean; -``` - -Defined in: [hotkey-sequence-recorder.ts:58](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L58) - -Whether plain Enter commits the current steps. Ignored when [commitKeys](#commitkeys) is `'none'`. - -#### Default - -```ts -true -``` +Whether plain Enter commits the recorded steps. Default: 'enter' *** @@ -61,7 +38,9 @@ Opt in to live-registry conflicts, including sequence prefixes. Default: false. #### Inherited from -[`RecorderOptions`](RecorderOptions.md).[`detectConflicts`](RecorderOptions.md#detectconflicts) +```ts +HotkeyRecorderBaseOptions.detectConflicts +``` *** @@ -71,10 +50,9 @@ Opt in to live-registry conflicts, including sequence prefixes. Default: false. optional idleTimeoutMs?: number; ``` -Defined in: [hotkey-sequence-recorder.ts:68](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L68) +Defined in: [hotkey-recorder.ts:82](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L82) -Milliseconds of inactivity after the **last completed chord** before auto-committing. -The timer does not run while waiting for the first chord (`steps.length === 0`). +Commit automatically after this many milliseconds without a new step. Disabled when unset or 0. *** @@ -84,28 +62,39 @@ The timer does not run while waiting for the first chord (`steps.length === 0`). optional ignoreInputs?: boolean; ``` -Defined in: [hotkey-sequence-recorder.ts:76](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L76) +Defined in: [hotkey-recorder.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L49) Whether to ignore keyboard events from input-like elements (text inputs, -textarea, select, contenteditable). When true, typing in inputs passes -through normally instead of being captured as a sequence recording. -Escape always works regardless of this setting. +textarea, select, contenteditable) while recording. When true, typing in +inputs passes through normally (Escape still cancels). Defaults to true. -#### Default +#### Inherited from ```ts -true +HotkeyRecorderBaseOptions.ignoreInputs ``` *** +### maxSteps + +```ts +maxSteps: number; +``` + +Defined in: [hotkey-recorder.ts:71](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L71) + +Maximum number of chords to record. Reaching it commits automatically. Use `Infinity` for no limit. + +*** + ### onCancel? ```ts optional onCancel?: () => void; ``` -Defined in: [hotkey-sequence-recorder.ts:51](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L51) +Defined in: [hotkey-recorder.ts:38](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L38) Optional callback when recording is cancelled (Escape pressed) @@ -113,6 +102,12 @@ Optional callback when recording is cancelled (Escape pressed) `void` +#### Inherited from + +```ts +HotkeyRecorderBaseOptions.onCancel +``` + *** ### onClear? @@ -121,31 +116,38 @@ Optional callback when recording is cancelled (Escape pressed) optional onClear?: () => void; ``` -Defined in: [hotkey-sequence-recorder.ts:53](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L53) +Defined in: [hotkey-recorder.ts:43](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L43) -Optional callback when the sequence is cleared (Backspace/Delete with no steps) +Optional callback when the recording is cleared: Backspace/Delete with no +modifiers and no recorded steps #### Returns `void` +#### Inherited from + +```ts +HotkeyRecorderBaseOptions.onClear +``` + *** ### onRecord ```ts -onRecord: (sequence) => void; +onRecord: (hotkey) => void; ``` -Defined in: [hotkey-sequence-recorder.ts:49](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L49) +Defined in: [hotkey-recorder.ts:78](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L78) -Callback when a sequence is successfully recorded +Callback when a hotkey is successfully recorded #### Parameters -##### sequence +##### hotkey -[`HotkeySequence`](../type-aliases/HotkeySequence.md) +[`Hotkey`](../type-aliases/Hotkey.md) #### Returns @@ -175,7 +177,9 @@ Rejected candidates leave recording active. #### Inherited from -[`RecorderOptions`](RecorderOptions.md).[`onReject`](RecorderOptions.md#onreject) +```ts +HotkeyRecorderBaseOptions.onReject +``` *** @@ -191,7 +195,9 @@ Platform used for Mod and AltGraph handling. Defaults to platform detection. #### Inherited from -[`RecorderOptions`](RecorderOptions.md).[`platform`](RecorderOptions.md#platform) +```ts +HotkeyRecorderBaseOptions.platform +``` *** @@ -207,29 +213,31 @@ Physical code recording is the default. Choose key to record logical characters. #### Inherited from -[`RecorderOptions`](RecorderOptions.md).[`recordBy`](RecorderOptions.md#recordby) +```ts +HotkeyRecorderBaseOptions.recordBy +``` *** ### validate? ```ts -optional validate?: (sequence, context) => string | boolean; +optional validate?: (hotkey, context) => string | boolean; ``` -Defined in: [hotkey-sequence-recorder.ts:44](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L44) +Defined in: [hotkey-recorder.ts:73](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L73) -Validate the completed sequence. Rejection preserves the steps for editing. +Return true to accept, or false/a message to reject while staying in recording mode. #### Parameters -##### sequence +##### hotkey -[`HotkeySequence`](../type-aliases/HotkeySequence.md) +[`Hotkey`](../type-aliases/Hotkey.md) ##### context -[`HotkeySequenceRecorderValidationContext`](HotkeySequenceRecorderValidationContext.md) +[`HotkeyRecorderValidationContext`](HotkeyRecorderValidationContext.md) #### Returns diff --git a/docs/reference/interfaces/HotkeySequenceRecorderState.md b/docs/reference/interfaces/HotkeySequenceRecorderState.md deleted file mode 100644 index 9e92d0f7..00000000 --- a/docs/reference/interfaces/HotkeySequenceRecorderState.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: HotkeySequenceRecorderState -title: HotkeySequenceRecorderState ---- - -Defined in: [hotkey-sequence-recorder.ts:30](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L30) - -State interface for the HotkeySequenceRecorder. - -## Properties - -### isRecording - -```ts -isRecording: boolean; -``` - -Defined in: [hotkey-sequence-recorder.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L32) - -Whether recording is currently active - -*** - -### recordedSequence - -```ts -recordedSequence: HotkeySequence | null; -``` - -Defined in: [hotkey-sequence-recorder.ts:36](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L36) - -The last successfully committed sequence, or null if none / after starting a new session - -*** - -### steps - -```ts -steps: HotkeySequence; -``` - -Defined in: [hotkey-sequence-recorder.ts:34](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L34) - -Chords captured so far in the current recording session diff --git a/docs/reference/interfaces/HotkeySequenceRecorderValidationContext.md b/docs/reference/interfaces/HotkeySequenceRecorderValidationContext.md deleted file mode 100644 index 637ad067..00000000 --- a/docs/reference/interfaces/HotkeySequenceRecorderValidationContext.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -id: HotkeySequenceRecorderValidationContext -title: HotkeySequenceRecorderValidationContext ---- - -Defined in: [recorder-options.ts:31](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L31) - -## Properties - -### events - -```ts -events: readonly KeyboardEvent[]; -``` - -Defined in: [recorder-options.ts:32](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L32) - -*** - -### parsedSequence - -```ts -parsedSequence: readonly ParsedHotkey[]; -``` - -Defined in: [recorder-options.ts:33](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L33) diff --git a/docs/reference/interfaces/HotkeySequenceRegistration.md b/docs/reference/interfaces/HotkeySequenceRegistration.md new file mode 100644 index 00000000..4172c30e --- /dev/null +++ b/docs/reference/interfaces/HotkeySequenceRegistration.md @@ -0,0 +1,174 @@ +--- +id: HotkeySequenceRegistration +title: HotkeySequenceRegistration +--- + +Defined in: [hotkey-manager.ts:121](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L121) + +A registered sequence in the HotkeyManager. + +## Extends + +- `RegistrationBase`\<[`HotkeySequenceOptions`](HotkeySequenceOptions.md)\> + +## Properties + +### callback + +```ts +callback: HotkeyCallback; +``` + +Defined in: [hotkey-manager.ts:100](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L100) + +The callback to invoke + +#### Inherited from + +```ts +RegistrationBase.callback +``` + +*** + +### currentIndex + +```ts +currentIndex: number; +``` + +Defined in: [hotkey-manager.ts:126](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L126) + +Steps matched in the current attempt + +*** + +### hasFired + +```ts +hasFired: boolean; +``` + +Defined in: [hotkey-manager.ts:108](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L108) + +Whether this registration has fired (and, for requireReset chords, needs a reset) + +#### Inherited from + +```ts +RegistrationBase.hasFired +``` + +*** + +### hotkey + +```ts +hotkey: HotkeySequence; +``` + +Defined in: [hotkey-manager.ts:123](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L123) + +*** + +### id + +```ts +id: string; +``` + +Defined in: [hotkey-manager.ts:98](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L98) + +Unique identifier for this registration + +#### Inherited from + +```ts +RegistrationBase.id +``` + +*** + +### kind + +```ts +kind: "sequence"; +``` + +Defined in: [hotkey-manager.ts:122](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L122) + +*** + +### lastKeyTime + +```ts +lastKeyTime: number; +``` + +Defined in: [hotkey-manager.ts:128](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L128) + +`Date.now()` when the last step in the current attempt matched + +*** + +### options + +```ts +options: HotkeySequenceOptions; +``` + +Defined in: [hotkey-manager.ts:102](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L102) + +Resolved options for this registration + +#### Inherited from + +```ts +RegistrationBase.options +``` + +*** + +### parsedSequence + +```ts +parsedSequence: ParsedHotkey[]; +``` + +Defined in: [hotkey-manager.ts:124](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L124) + +*** + +### target + +```ts +target: Target; +``` + +Defined in: [hotkey-manager.ts:104](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L104) + +The resolved target element for this registration + +#### Inherited from + +```ts +RegistrationBase.target +``` + +*** + +### triggerCount + +```ts +triggerCount: number; +``` + +Defined in: [hotkey-manager.ts:106](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L106) + +How many times this registration's callback has been triggered + +#### Inherited from + +```ts +RegistrationBase.triggerCount +``` diff --git a/docs/reference/interfaces/HotkeySequenceRegistrationView.md b/docs/reference/interfaces/HotkeySequenceRegistrationView.md new file mode 100644 index 00000000..d2fb66a9 --- /dev/null +++ b/docs/reference/interfaces/HotkeySequenceRegistrationView.md @@ -0,0 +1,140 @@ +--- +id: HotkeySequenceRegistrationView +title: HotkeySequenceRegistrationView +--- + +Defined in: [hotkey-manager.ts:151](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L151) + +Public view of a sequence registration, without its callback. + +## Extends + +- `RegistrationViewBase` + +## Properties + +### hasFired + +```ts +hasFired: boolean; +``` + +Defined in: [hotkey-manager.ts:139](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L139) + +#### Inherited from + +```ts +RegistrationViewBase.hasFired +``` + +*** + +### hotkey + +```ts +hotkey: HotkeySequence; +``` + +Defined in: [hotkey-manager.ts:153](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L153) + +*** + +### id + +```ts +id: string; +``` + +Defined in: [hotkey-manager.ts:136](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L136) + +#### Inherited from + +```ts +RegistrationViewBase.id +``` + +*** + +### kind + +```ts +kind: "sequence"; +``` + +Defined in: [hotkey-manager.ts:152](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L152) + +*** + +### matchedStepCount + +```ts +matchedStepCount: number; +``` + +Defined in: [hotkey-manager.ts:157](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L157) + +Steps matched in the current attempt (0 when idle or just completed). + +*** + +### options + +```ts +options: HotkeySequenceOptions; +``` + +Defined in: [hotkey-manager.ts:155](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L155) + +*** + +### parsedSequence + +```ts +parsedSequence: ParsedHotkey[]; +``` + +Defined in: [hotkey-manager.ts:154](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L154) + +*** + +### partialMatchLastKeyTime + +```ts +partialMatchLastKeyTime: number; +``` + +Defined in: [hotkey-manager.ts:159](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L159) + +`Date.now()` when the last step in the current attempt matched; 0 if none. + +*** + +### target + +```ts +target: Target; +``` + +Defined in: [hotkey-manager.ts:137](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L137) + +#### Inherited from + +```ts +RegistrationViewBase.target +``` + +*** + +### triggerCount + +```ts +triggerCount: number; +``` + +Defined in: [hotkey-manager.ts:138](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L138) + +#### Inherited from + +```ts +RegistrationViewBase.triggerCount +``` diff --git a/docs/reference/interfaces/KeyboardEventMatch.md b/docs/reference/interfaces/KeyboardEventMatch.md index 3ac18665..3e3a1b1d 100644 --- a/docs/reference/interfaces/KeyboardEventMatch.md +++ b/docs/reference/interfaces/KeyboardEventMatch.md @@ -3,7 +3,7 @@ id: KeyboardEventMatch title: KeyboardEventMatch --- -Defined in: [match.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L13) +Defined in: [match.ts:21](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L21) ## Properties @@ -13,7 +13,7 @@ Defined in: [match.ts:13](https://github.com/TanStack/hotkeys/blob/main/packages optional identity?: object; ``` -Defined in: [match.ts:17](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L17) +Defined in: [match.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L25) #### code @@ -35,7 +35,7 @@ key: string; matched: boolean; ``` -Defined in: [match.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L14) +Defined in: [match.ts:22](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L22) *** @@ -45,7 +45,7 @@ Defined in: [match.ts:14](https://github.com/TanStack/hotkeys/blob/main/packages score: 0 | 1 | 2 | 3; ``` -Defined in: [match.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L15) +Defined in: [match.ts:23](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L23) *** @@ -55,4 +55,4 @@ Defined in: [match.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages optional source?: "key" | "code" | "fallback"; ``` -Defined in: [match.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L16) +Defined in: [match.ts:24](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L24) diff --git a/docs/reference/interfaces/RecorderOptions.md b/docs/reference/interfaces/RecorderOptions.md index 33f006f8..6cf99fe9 100644 --- a/docs/reference/interfaces/RecorderOptions.md +++ b/docs/reference/interfaces/RecorderOptions.md @@ -5,11 +5,6 @@ title: RecorderOptions Defined in: [recorder-options.ts:15](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L15) -## Extended by - -- [`HotkeyRecorderOptions`](HotkeyRecorderOptions.md) -- [`HotkeySequenceRecorderOptions`](HotkeySequenceRecorderOptions.md) - ## Properties ### detectConflicts? diff --git a/docs/reference/interfaces/RecorderRejection.md b/docs/reference/interfaces/RecorderRejection.md index 71091854..93f9aa32 100644 --- a/docs/reference/interfaces/RecorderRejection.md +++ b/docs/reference/interfaces/RecorderRejection.md @@ -23,7 +23,9 @@ Defined in: [recorder-options.ts:12](https://github.com/TanStack/hotkeys/blob/ma optional hotkey?: Hotkey; ``` -Defined in: [recorder-options.ts:10](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L10) +Defined in: [recorder-options.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L11) + +The rejected chord or sequence, when one was recorded. *** @@ -44,13 +46,3 @@ reason: "invalid" | "missing-code" | "alt-graph" | "conflict" | "validation"; ``` Defined in: [recorder-options.ts:8](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L8) - -*** - -### sequence? - -```ts -optional sequence?: Hotkey[]; -``` - -Defined in: [recorder-options.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/recorder-options.ts#L11) diff --git a/docs/reference/interfaces/Register.md b/docs/reference/interfaces/Register.md new file mode 100644 index 00000000..e367f53f --- /dev/null +++ b/docs/reference/interfaces/Register.md @@ -0,0 +1,18 @@ +--- +id: Register +title: Register +--- + +Defined in: app-hotkeys.ts:141 + +Register your app hotkeys to type `useAppHotkeys()` and `CustomHotkeys`. + +## Example + +```ts +declare module '@tanstack/react-hotkeys' { + interface Register { + appHotkeys: typeof appHotkeys + } +} +``` diff --git a/docs/reference/interfaces/SequenceRegistration.md b/docs/reference/interfaces/SequenceRegistration.md deleted file mode 100644 index 6a8b439e..00000000 --- a/docs/reference/interfaces/SequenceRegistration.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -id: SequenceRegistration -title: SequenceRegistration ---- - -Defined in: [sequence-manager.ts:90](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L90) - -Internal representation of a sequence registration. - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [sequence-manager.ts:94](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L94) - -*** - -### currentIndex - -```ts -currentIndex: number; -``` - -Defined in: [sequence-manager.ts:97](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L97) - -*** - -### hasFired - -```ts -hasFired: boolean; -``` - -Defined in: [sequence-manager.ts:100](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L100) - -*** - -### id - -```ts -id: string; -``` - -Defined in: [sequence-manager.ts:91](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L91) - -*** - -### lastKeyTime - -```ts -lastKeyTime: number; -``` - -Defined in: [sequence-manager.ts:98](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L98) - -*** - -### options - -```ts -options: SequenceOptions; -``` - -Defined in: [sequence-manager.ts:95](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L95) - -*** - -### parsedSequence - -```ts -parsedSequence: ParsedHotkey[]; -``` - -Defined in: [sequence-manager.ts:93](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L93) - -*** - -### sequence - -```ts -sequence: HotkeySequence; -``` - -Defined in: [sequence-manager.ts:92](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L92) - -*** - -### target - -```ts -target: Target; -``` - -Defined in: [sequence-manager.ts:96](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L96) - -*** - -### triggerCount - -```ts -triggerCount: number; -``` - -Defined in: [sequence-manager.ts:99](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L99) diff --git a/docs/reference/interfaces/SequenceRegistrationHandle.md b/docs/reference/interfaces/SequenceRegistrationHandle.md deleted file mode 100644 index 58a79ea7..00000000 --- a/docs/reference/interfaces/SequenceRegistrationHandle.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -id: SequenceRegistrationHandle -title: SequenceRegistrationHandle ---- - -Defined in: [sequence-manager.ts:116](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L116) - -A handle returned from SequenceManager.register() that allows updating -the callback and options without re-registering the sequence. - -## Example - -```ts -const handle = manager.register(['G', 'G'], callback, options) - -handle.callback = newCallback -handle.setOptions({ timeout: 500 }) -handle.unregister() -``` - -## Properties - -### callback - -```ts -callback: HotkeyCallback; -``` - -Defined in: [sequence-manager.ts:119](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L119) - -*** - -### id - -```ts -readonly id: string; -``` - -Defined in: [sequence-manager.ts:117](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L117) - -*** - -### isActive - -```ts -readonly isActive: boolean; -``` - -Defined in: [sequence-manager.ts:118](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L118) - -*** - -### setOptions - -```ts -setOptions: (options) => void; -``` - -Defined in: [sequence-manager.ts:120](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L120) - -#### Parameters - -##### options - -`Partial`\<[`SequenceOptions`](SequenceOptions.md)\> - -#### Returns - -`void` - -*** - -### unregister - -```ts -unregister: () => void; -``` - -Defined in: [sequence-manager.ts:121](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L121) - -#### Returns - -`void` diff --git a/docs/reference/interfaces/SequenceRegistrationView.md b/docs/reference/interfaces/SequenceRegistrationView.md deleted file mode 100644 index ce29b3ea..00000000 --- a/docs/reference/interfaces/SequenceRegistrationView.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -id: SequenceRegistrationView -title: SequenceRegistrationView ---- - -Defined in: [sequence-manager.ts:73](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L73) - -View of a sequence registration for devtools display. -Progress fields reflect an in-progress match (between first key and completion or timeout). - -## Properties - -### hasFired - -```ts -hasFired: boolean; -``` - -Defined in: [sequence-manager.ts:80](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L80) - -Whether this sequence has been triggered at least once. - -*** - -### id - -```ts -id: string; -``` - -Defined in: [sequence-manager.ts:74](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L74) - -*** - -### matchedStepCount - -```ts -matchedStepCount: number; -``` - -Defined in: [sequence-manager.ts:82](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L82) - -Steps matched in the current attempt (0 when idle or just completed). - -*** - -### options - -```ts -options: SequenceOptions; -``` - -Defined in: [sequence-manager.ts:76](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L76) - -*** - -### partialMatchLastKeyTime - -```ts -partialMatchLastKeyTime: number; -``` - -Defined in: [sequence-manager.ts:84](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L84) - -`Date.now()` when the last step in the current attempt matched; 0 if none. - -*** - -### sequence - -```ts -sequence: HotkeySequence; -``` - -Defined in: [sequence-manager.ts:75](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L75) - -*** - -### target - -```ts -target: Target; -``` - -Defined in: [sequence-manager.ts:77](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L77) - -*** - -### triggerCount - -```ts -triggerCount: number; -``` - -Defined in: [sequence-manager.ts:78](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L78) diff --git a/docs/reference/interfaces/ValidationResult.md b/docs/reference/interfaces/ValidationResult.md index 25155b75..7c156e5c 100644 --- a/docs/reference/interfaces/ValidationResult.md +++ b/docs/reference/interfaces/ValidationResult.md @@ -3,7 +3,7 @@ id: ValidationResult title: ValidationResult --- -Defined in: [validate.ts:172](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L172) +Defined in: [validate.ts:209](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L209) Result of validating a hotkey string. @@ -15,7 +15,7 @@ Result of validating a hotkey string. errors: string[]; ``` -Defined in: [validate.ts:178](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L178) +Defined in: [validate.ts:215](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L215) Error messages about invalid syntax @@ -27,7 +27,7 @@ Error messages about invalid syntax valid: boolean; ``` -Defined in: [validate.ts:174](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L174) +Defined in: [validate.ts:211](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L211) Whether the hotkey is valid (can still have warnings) @@ -39,6 +39,6 @@ Whether the hotkey is valid (can still have warnings) warnings: string[]; ``` -Defined in: [validate.ts:176](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L176) +Defined in: [validate.ts:213](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L213) Warning messages about potential issues diff --git a/docs/reference/type-aliases/AppHotkeyChordOptions.md b/docs/reference/type-aliases/AppHotkeyChordOptions.md new file mode 100644 index 00000000..8a93bedc --- /dev/null +++ b/docs/reference/type-aliases/AppHotkeyChordOptions.md @@ -0,0 +1,12 @@ +--- +id: AppHotkeyChordOptions +title: AppHotkeyChordOptions +--- + +```ts +type AppHotkeyChordOptions = Pick; +``` + +Defined in: app-hotkeys.ts:29 + +Declared options for an app hotkey whose default is a chord. diff --git a/docs/reference/type-aliases/AppHotkeyDefaultKind.md b/docs/reference/type-aliases/AppHotkeyDefaultKind.md new file mode 100644 index 00000000..e0e906bd --- /dev/null +++ b/docs/reference/type-aliases/AppHotkeyDefaultKind.md @@ -0,0 +1,18 @@ +--- +id: AppHotkeyDefaultKind +title: AppHotkeyDefaultKind +--- + +```ts +type AppHotkeyDefaultKind = THotkey extends null ? null : THotkey extends readonly [unknown] ? HotkeyChord : THotkey extends ReadonlyArray ? HotkeySequence : HotkeyChord; +``` + +Defined in: app-hotkeys.ts:113 + +The kind of a declared default hotkey; a one-step sequence is a chord. + +## Type Parameters + +### THotkey + +`THotkey` diff --git a/docs/reference/type-aliases/AppHotkeyId.md b/docs/reference/type-aliases/AppHotkeyId.md new file mode 100644 index 00000000..d57d92c8 --- /dev/null +++ b/docs/reference/type-aliases/AppHotkeyId.md @@ -0,0 +1,12 @@ +--- +id: AppHotkeyId +title: AppHotkeyId +--- + +```ts +type AppHotkeyId = keyof RegisteredAppHotkeys & string; +``` + +Defined in: app-hotkeys.ts:151 + +Ids of the registered app hotkeys. diff --git a/docs/reference/type-aliases/AppHotkeyOptions.md b/docs/reference/type-aliases/AppHotkeyOptions.md new file mode 100644 index 00000000..01fdaa72 --- /dev/null +++ b/docs/reference/type-aliases/AppHotkeyOptions.md @@ -0,0 +1,12 @@ +--- +id: AppHotkeyOptions +title: AppHotkeyOptions +--- + +```ts +type AppHotkeyOptions = AppHotkeyChordOptions & AppHotkeySequenceOptions; +``` + +Defined in: app-hotkeys.ts:41 + +Every declared option. Kind-specific options apply only while the effective hotkey has that kind. diff --git a/docs/reference/type-aliases/AppHotkeySequenceOptions.md b/docs/reference/type-aliases/AppHotkeySequenceOptions.md new file mode 100644 index 00000000..05ec1277 --- /dev/null +++ b/docs/reference/type-aliases/AppHotkeySequenceOptions.md @@ -0,0 +1,12 @@ +--- +id: AppHotkeySequenceOptions +title: AppHotkeySequenceOptions +--- + +```ts +type AppHotkeySequenceOptions = Pick; +``` + +Defined in: app-hotkeys.ts:35 + +Declared options for an app hotkey whose default is a sequence. diff --git a/docs/reference/type-aliases/AppHotkeyStatus.md b/docs/reference/type-aliases/AppHotkeyStatus.md new file mode 100644 index 00000000..7172e068 --- /dev/null +++ b/docs/reference/type-aliases/AppHotkeyStatus.md @@ -0,0 +1,16 @@ +--- +id: AppHotkeyStatus +title: AppHotkeyStatus +--- + +```ts +type AppHotkeyStatus = "active" | "disabled" | "inactive" | "unbound"; +``` + +Defined in: app-hotkeys.ts:304 + +Status of an app hotkey in the running app. +- `'active'`: at least one enabled registration +- `'disabled'`: registered, but every registration has `enabled: false` +- `'inactive'`: has a hotkey, but nothing has registered it +- `'unbound'`: no hotkey assigned diff --git a/docs/reference/type-aliases/AppHotkeys.md b/docs/reference/type-aliases/AppHotkeys.md new file mode 100644 index 00000000..b06043ac --- /dev/null +++ b/docs/reference/type-aliases/AppHotkeys.md @@ -0,0 +1,12 @@ +--- +id: AppHotkeys +title: AppHotkeys +--- + +```ts +type AppHotkeys = Readonly>; +``` + +Defined in: app-hotkeys.ts:88 + +A map of app hotkeys keyed by id. diff --git a/docs/reference/type-aliases/ConflictBehavior.md b/docs/reference/type-aliases/ConflictBehavior.md index 29c22f77..68d92535 100644 --- a/docs/reference/type-aliases/ConflictBehavior.md +++ b/docs/reference/type-aliases/ConflictBehavior.md @@ -7,7 +7,7 @@ title: ConflictBehavior type ConflictBehavior = "warn" | "error" | "replace" | "allow"; ``` -Defined in: [hotkey.types.ts:269](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L269) +Defined in: [hotkey.types.ts:286](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L286) Behavior when registering a hotkey/sequence that conflicts with an existing registration. diff --git a/docs/reference/type-aliases/CustomHotkeys.md b/docs/reference/type-aliases/CustomHotkeys.md new file mode 100644 index 00000000..3f97a912 --- /dev/null +++ b/docs/reference/type-aliases/CustomHotkeys.md @@ -0,0 +1,19 @@ +--- +id: CustomHotkeys +title: CustomHotkeys +--- + +```ts +type CustomHotkeys = { -readonly [K in keyof T]?: Hotkey | null }; +``` + +Defined in: app-hotkeys.ts:157 + +The user's hotkeys that differ from the declared defaults, keyed by app +hotkey id. This is the shape to save. `null` means the user unbound it. + +## Type Parameters + +### T + +`T` *extends* [`AppHotkeys`](AppHotkeys.md) = [`RegisteredAppHotkeys`](RegisteredAppHotkeys.md) diff --git a/docs/reference/type-aliases/DefinedAppHotkeys.md b/docs/reference/type-aliases/DefinedAppHotkeys.md new file mode 100644 index 00000000..f5c3a207 --- /dev/null +++ b/docs/reference/type-aliases/DefinedAppHotkeys.md @@ -0,0 +1,18 @@ +--- +id: DefinedAppHotkeys +title: DefinedAppHotkeys +--- + +```ts +type DefinedAppHotkeys = { readonly [K in keyof T & string]: AppHotkey> }; +``` + +Defined in: app-hotkeys.ts:122 + +The app hotkeys returned by `defineAppHotkeys` for a declaration object. + +## Type Parameters + +### T + +`T` *extends* `Record`\<`string`, [`AppHotkeyInput`](../interfaces/AppHotkeyInput.md)\> diff --git a/docs/reference/type-aliases/DisplayHotkey.md b/docs/reference/type-aliases/DisplayHotkey.md index fde7a023..82b21b1d 100644 --- a/docs/reference/type-aliases/DisplayHotkey.md +++ b/docs/reference/type-aliases/DisplayHotkey.md @@ -5,9 +5,10 @@ title: DisplayHotkey ```ts type DisplayHotkey = - | RegisterableHotkey - | ParsedHotkey - | string & object; + | DisplayHotkeyChord +| ReadonlyArray; ``` -Defined in: [format.ts:71](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L71) +Defined in: [format.ts:59](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L59) + +A hotkey to display: a chord or a sequence of chords. diff --git a/docs/reference/type-aliases/DisplayHotkeyChord.md b/docs/reference/type-aliases/DisplayHotkeyChord.md new file mode 100644 index 00000000..7c14faa1 --- /dev/null +++ b/docs/reference/type-aliases/DisplayHotkeyChord.md @@ -0,0 +1,15 @@ +--- +id: DisplayHotkeyChord +title: DisplayHotkeyChord +--- + +```ts +type DisplayHotkeyChord = + | RegisterableHotkeyChord + | ParsedHotkey + | string & object; +``` + +Defined in: [format.ts:55](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/format.ts#L55) + +A single chord to display: a string, raw object, or parsed hotkey. diff --git a/docs/reference/type-aliases/Hotkey.md b/docs/reference/type-aliases/Hotkey.md index 7b0d1087..62b7e93e 100644 --- a/docs/reference/type-aliases/Hotkey.md +++ b/docs/reference/type-aliases/Hotkey.md @@ -4,64 +4,9 @@ title: Hotkey --- ```ts -type Hotkey = - | Key - | SingleModifierHotkey - | TwoModifierHotkey - | ThreeModifierHotkey - | FourModifierHotkey; +type Hotkey = HotkeyChord | HotkeySequence; ``` -Defined in: [hotkey.types.ts:126](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L126) +Defined in: [hotkey.types.ts:216](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L216) -A type-safe hotkey string. - -Provides autocomplete for: -- All single keys (letters, numbers, function keys, navigation, editing, punctuation) -- Single modifier + common key (Control+S, Mod+A, Mod+/, etc.) -- Two modifiers + common key (Mod+Shift+S, Control+Alt+A, etc.) -- Three modifiers + common key (Control+Alt+Shift+A, Mod+Alt+Shift+S, etc.) -- Four modifiers + common key (Control+Alt+Shift+Meta+A, etc.) - -## Modifier Names - -Use canonical modifier names: -- `Control` (not Ctrl) - The Control key -- `Alt` (not Option) - The Alt key (Option on macOS) -- `Meta` (not Command/Cmd) - The Meta/Command key (macOS only) -- `Shift` - The Shift key - -## Platform-Adaptive `Mod` Modifier - -The `Mod` modifier is a special platform-adaptive modifier that automatically resolves -to the "primary modifier" on each platform: - -- **macOS**: `Mod` → `Meta` (Command key ⌘) -- **Windows/Linux**: `Mod` → `Control` (Ctrl key) - -This enables cross-platform hotkey definitions that work correctly on all platforms -without platform-specific code. The `Mod` modifier is resolved at runtime based on -the detected platform. - -**When to use `Mod` vs platform-specific modifiers:** -- Use `Mod` for cross-platform shortcuts (e.g., `Mod+S` for save) -- Use `Meta` or `Control` when you need platform-specific behavior -- Use `Mod` when you want your shortcuts to follow platform conventions automatically - -**Limitations:** -- `Mod+Control` and `Mod+Meta` combinations are not allowed (they create duplicate - modifiers on one platform) -- In four-modifier combinations, only canonical modifiers are allowed (no `Mod`) - -## Example - -```ts -// Cross-platform shortcuts (recommended) -const save: Hotkey = 'Mod+S' // Command+S on Mac, Ctrl+S on Windows/Linux -const saveAs: Hotkey = 'Mod+Shift+S' // Command+Shift+S on Mac, Ctrl+Shift+S elsewhere -const comment: Hotkey = 'Mod+/' // Command+/ on Mac, Ctrl+/ elsewhere - -// Platform-specific shortcuts -const macOnly: Hotkey = 'Meta+S' // Command+S on Mac only -const windowsOnly: Hotkey = 'Control+S' // Ctrl+S on Windows/Linux only -``` +A hotkey: one chord (`'Mod+S'`) or a sequence of chords (`['G', 'G']`). diff --git a/docs/reference/type-aliases/HotkeyCallback.md b/docs/reference/type-aliases/HotkeyCallback.md index c2d272be..44c9700b 100644 --- a/docs/reference/type-aliases/HotkeyCallback.md +++ b/docs/reference/type-aliases/HotkeyCallback.md @@ -7,7 +7,7 @@ title: HotkeyCallback type HotkeyCallback = (event, context) => void; ``` -Defined in: [hotkey.types.ts:233](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L233) +Defined in: [hotkey.types.ts:250](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L250) Callback function type for hotkey handlers. diff --git a/docs/reference/type-aliases/HotkeyChord.md b/docs/reference/type-aliases/HotkeyChord.md new file mode 100644 index 00000000..f7efaa76 --- /dev/null +++ b/docs/reference/type-aliases/HotkeyChord.md @@ -0,0 +1,67 @@ +--- +id: HotkeyChord +title: HotkeyChord +--- + +```ts +type HotkeyChord = + | Key + | SingleModifierHotkey + | TwoModifierHotkey + | ThreeModifierHotkey + | FourModifierHotkey; +``` + +Defined in: [hotkey.types.ts:126](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L126) + +A type-safe hotkey string. + +Provides autocomplete for: +- All single keys (letters, numbers, function keys, navigation, editing, punctuation) +- Single modifier + common key (Control+S, Mod+A, Mod+/, etc.) +- Two modifiers + common key (Mod+Shift+S, Control+Alt+A, etc.) +- Three modifiers + common key (Control+Alt+Shift+A, Mod+Alt+Shift+S, etc.) +- Four modifiers + common key (Control+Alt+Shift+Meta+A, etc.) + +## Modifier Names + +Use canonical modifier names: +- `Control` (not Ctrl) - The Control key +- `Alt` (not Option) - The Alt key (Option on macOS) +- `Meta` (not Command/Cmd) - The Meta/Command key (macOS only) +- `Shift` - The Shift key + +## Platform-Adaptive `Mod` Modifier + +The `Mod` modifier is a special platform-adaptive modifier that automatically resolves +to the "primary modifier" on each platform: + +- **macOS**: `Mod` → `Meta` (Command key ⌘) +- **Windows/Linux**: `Mod` → `Control` (Ctrl key) + +This enables cross-platform hotkey definitions that work correctly on all platforms +without platform-specific code. The `Mod` modifier is resolved at runtime based on +the detected platform. + +**When to use `Mod` vs platform-specific modifiers:** +- Use `Mod` for cross-platform shortcuts (e.g., `Mod+S` for save) +- Use `Meta` or `Control` when you need platform-specific behavior +- Use `Mod` when you want your shortcuts to follow platform conventions automatically + +**Limitations:** +- `Mod+Control` and `Mod+Meta` combinations are not allowed (they create duplicate + modifiers on one platform) +- In four-modifier combinations, only canonical modifiers are allowed (no `Mod`) + +## Example + +```ts +// Cross-platform shortcuts (recommended) +const save: HotkeyChord = 'Mod+S' // Command+S on Mac, Ctrl+S on Windows/Linux +const saveAs: HotkeyChord = 'Mod+Shift+S' // Command+Shift+S on Mac, Ctrl+Shift+S elsewhere +const comment: HotkeyChord = 'Mod+/' // Command+/ on Mac, Ctrl+/ elsewhere + +// Platform-specific shortcuts +const macOnly: HotkeyChord = 'Meta+S' // Command+S on Mac only +const windowsOnly: HotkeyChord = 'Control+S' // Ctrl+S on Windows/Linux only +``` diff --git a/docs/reference/type-aliases/HotkeyConflict.md b/docs/reference/type-aliases/HotkeyConflict.md deleted file mode 100644 index b8602ab2..00000000 --- a/docs/reference/type-aliases/HotkeyConflict.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -id: HotkeyConflict -title: HotkeyConflict ---- - -```ts -type HotkeyConflict = - | { - registration: HotkeyRegistrationView; - type: "hotkey"; -} - | { - registration: SequenceRegistrationView; - type: "sequence"; -}; -``` - -Defined in: [conflicts.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/conflicts.ts#L11) diff --git a/docs/reference/type-aliases/HotkeyOptionsFor.md b/docs/reference/type-aliases/HotkeyOptionsFor.md new file mode 100644 index 00000000..e30c8b46 --- /dev/null +++ b/docs/reference/type-aliases/HotkeyOptionsFor.md @@ -0,0 +1,19 @@ +--- +id: HotkeyOptionsFor +title: HotkeyOptionsFor +--- + +```ts +type HotkeyOptionsFor = [T] extends [never] ? HotkeyChordOptions & HotkeySequenceOptions : T extends readonly [RegisterableHotkeyChord] ? HotkeyChordOptions : T extends HotkeySequence ? HotkeySequenceOptions : HotkeyChordOptions; +``` + +Defined in: [hotkey-manager.ts:88](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L88) + +The options type for a hotkey: chord options for chords (including one-step +tuples), sequence options for sequences. + +## Type Parameters + +### T + +`T` *extends* [`RegisterableHotkey`](RegisterableHotkey.md) diff --git a/docs/reference/type-aliases/HotkeyRecorderCommitKeys.md b/docs/reference/type-aliases/HotkeyRecorderCommitKeys.md new file mode 100644 index 00000000..065a616c --- /dev/null +++ b/docs/reference/type-aliases/HotkeyRecorderCommitKeys.md @@ -0,0 +1,12 @@ +--- +id: HotkeyRecorderCommitKeys +title: HotkeyRecorderCommitKeys +--- + +```ts +type HotkeyRecorderCommitKeys = "enter" | "none"; +``` + +Defined in: [hotkey-recorder.ts:34](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L34) + +Which keys commit a multi-step recording before it reaches `maxSteps`. diff --git a/docs/reference/type-aliases/HotkeyRecorderOptions.md b/docs/reference/type-aliases/HotkeyRecorderOptions.md new file mode 100644 index 00000000..7a0c8f19 --- /dev/null +++ b/docs/reference/type-aliases/HotkeyRecorderOptions.md @@ -0,0 +1,15 @@ +--- +id: HotkeyRecorderOptions +title: HotkeyRecorderOptions +--- + +```ts +type HotkeyRecorderOptions = + | HotkeyChordRecorderOptions + | HotkeySequenceRecorderOptions; +``` + +Defined in: [hotkey-recorder.ts:89](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-recorder.ts#L89) + +Options for HotkeyRecorder. `maxSteps` (default 1) selects between recording +a single chord and recording a sequence. diff --git a/docs/reference/type-aliases/HotkeyRegistration.md b/docs/reference/type-aliases/HotkeyRegistration.md new file mode 100644 index 00000000..0e6e2398 --- /dev/null +++ b/docs/reference/type-aliases/HotkeyRegistration.md @@ -0,0 +1,14 @@ +--- +id: HotkeyRegistration +title: HotkeyRegistration +--- + +```ts +type HotkeyRegistration = + | HotkeyChordRegistration + | HotkeySequenceRegistration; +``` + +Defined in: [hotkey-manager.ts:132](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L132) + +A registered hotkey in the HotkeyManager. diff --git a/docs/reference/type-aliases/HotkeyRegistrationView.md b/docs/reference/type-aliases/HotkeyRegistrationView.md new file mode 100644 index 00000000..c0126f76 --- /dev/null +++ b/docs/reference/type-aliases/HotkeyRegistrationView.md @@ -0,0 +1,15 @@ +--- +id: HotkeyRegistrationView +title: HotkeyRegistrationView +--- + +```ts +type HotkeyRegistrationView = + | HotkeyChordRegistrationView + | HotkeySequenceRegistrationView; +``` + +Defined in: [hotkey-manager.ts:166](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L166) + +Public view of a hotkey registration for display and introspection. +Narrow with `kind` to read chord- or sequence-specific fields. diff --git a/docs/reference/type-aliases/HotkeySequence.md b/docs/reference/type-aliases/HotkeySequence.md index 5ad23a84..faf50940 100644 --- a/docs/reference/type-aliases/HotkeySequence.md +++ b/docs/reference/type-aliases/HotkeySequence.md @@ -4,23 +4,14 @@ title: HotkeySequence --- ```ts -type HotkeySequence = Hotkey[]; +type HotkeySequence = ReadonlyArray; ``` -Defined in: [sequence-manager.ts:52](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L52) +Defined in: [hotkey.types.ts:211](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L211) -A sequence of hotkeys for Vim-style shortcuts. +A sequence of chords pressed one after another (Vim-style), such as `['G', 'G']`. -Each element is one step (a `Hotkey` string). Steps may include modifiers; -the same modifier can appear on consecutive steps (e.g. `Shift+R` then -`Shift+T`). Modifier-only key events do not advance or reset matching—see -`SequenceManager`. Automatic keydown repeats and IME composition are also ignored. - -## Example - -```ts -const gotoTop: HotkeySequence = ['G', 'G'] // gg -const deleteLine: HotkeySequence = ['D', 'D'] // dd -const deleteWord: HotkeySequence = ['D', 'I', 'W'] // diw -const chainedShift: HotkeySequence = ['Shift+R', 'Shift+T'] -``` +Steps may include modifiers; the same modifier can appear on consecutive +steps (e.g. `['Shift+R', 'Shift+T']`). Modifier-only key events do not +advance or reset matching. Automatic repeats and IME composition are ignored. +A one-step sequence is treated as its single chord. diff --git a/docs/reference/type-aliases/HotkeySequenceRecorderCommitKeys.md b/docs/reference/type-aliases/HotkeySequenceRecorderCommitKeys.md deleted file mode 100644 index ce936ddf..00000000 --- a/docs/reference/type-aliases/HotkeySequenceRecorderCommitKeys.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -id: HotkeySequenceRecorderCommitKeys -title: HotkeySequenceRecorderCommitKeys ---- - -```ts -type HotkeySequenceRecorderCommitKeys = "enter" | "none"; -``` - -Defined in: [hotkey-sequence-recorder.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-sequence-recorder.ts#L25) - -How the user can commit a recorded sequence from the keyboard. -- `'enter'`: plain Enter (no modifiers) commits when at least one step exists. -- `'none'`: only [HotkeySequenceRecorder.commit](../classes/HotkeySequenceRecorder.md#commit) finishes recording (or idle timeout if set). diff --git a/docs/reference/type-aliases/MultiHotkeyHandler.md b/docs/reference/type-aliases/MultiHotkeyHandler.md index c3072b53..a421fe70 100644 --- a/docs/reference/type-aliases/MultiHotkeyHandler.md +++ b/docs/reference/type-aliases/MultiHotkeyHandler.md @@ -4,7 +4,7 @@ title: MultiHotkeyHandler --- ```ts -type MultiHotkeyHandler = { [K in Hotkey]?: HotkeyCallback }; +type MultiHotkeyHandler = { [K in HotkeyChord]?: HotkeyCallback }; ``` -Defined in: [match.ts:118](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L118) +Defined in: [match.ts:126](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L126) diff --git a/docs/reference/type-aliases/RegisterableHotkey.md b/docs/reference/type-aliases/RegisterableHotkey.md index a7b9136f..59079525 100644 --- a/docs/reference/type-aliases/RegisterableHotkey.md +++ b/docs/reference/type-aliases/RegisterableHotkey.md @@ -4,10 +4,11 @@ title: RegisterableHotkey --- ```ts -type RegisterableHotkey = Hotkey | RawHotkey; +type RegisterableHotkey = + | RegisterableHotkeyChord + | HotkeySequence; ``` -Defined in: [hotkey.types.ts:207](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L207) +Defined in: [hotkey.types.ts:224](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L224) -A hotkey that can be passed to `HotkeyManager.register()` and `useHotkey()`. -Either a type-safe string (`Hotkey`) or a raw object (`RawHotkey`). +Anything that can be registered: a chord string, a raw chord object, or a sequence. diff --git a/docs/reference/type-aliases/RegisterableHotkeyChord.md b/docs/reference/type-aliases/RegisterableHotkeyChord.md new file mode 100644 index 00000000..a7eb805c --- /dev/null +++ b/docs/reference/type-aliases/RegisterableHotkeyChord.md @@ -0,0 +1,12 @@ +--- +id: RegisterableHotkeyChord +title: RegisterableHotkeyChord +--- + +```ts +type RegisterableHotkeyChord = HotkeyChord | RawHotkey; +``` + +Defined in: [hotkey.types.ts:219](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey.types.ts#L219) + +A single chord as a type-safe string (`HotkeyChord`) or a raw object (`RawHotkey`). diff --git a/docs/reference/type-aliases/RegisteredAppHotkeys.md b/docs/reference/type-aliases/RegisteredAppHotkeys.md new file mode 100644 index 00000000..247af6a6 --- /dev/null +++ b/docs/reference/type-aliases/RegisteredAppHotkeys.md @@ -0,0 +1,12 @@ +--- +id: RegisteredAppHotkeys +title: RegisteredAppHotkeys +--- + +```ts +type RegisteredAppHotkeys = Register extends object ? T : AppHotkeys; +``` + +Defined in: app-hotkeys.ts:144 + +The app hotkeys type from `Register`, or a generic map when none is registered. diff --git a/docs/reference/type-aliases/Target.md b/docs/reference/type-aliases/Target.md index 6bd059fe..ca4039b3 100644 --- a/docs/reference/type-aliases/Target.md +++ b/docs/reference/type-aliases/Target.md @@ -7,4 +7,6 @@ title: Target type Target = HTMLElement | Document | Window; ``` -Defined in: [sequence-manager.ts:25](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L25) +Defined in: [hotkey-manager.ts:40](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/hotkey-manager.ts#L40) + +An element, document, or window that hotkeys can listen on. diff --git a/docs/reference/type-aliases/ValidatableHotkey.md b/docs/reference/type-aliases/ValidatableHotkey.md new file mode 100644 index 00000000..64af1e4f --- /dev/null +++ b/docs/reference/type-aliases/ValidatableHotkey.md @@ -0,0 +1,12 @@ +--- +id: ValidatableHotkey +title: ValidatableHotkey +--- + +```ts +type ValidatableHotkey = Hotkey | string & object | ReadonlyArray; +``` + +Defined in: [validate.ts:6](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/validate.ts#L6) + +A hotkey to validate: a chord string, a sequence, or untyped input such as saved data. diff --git a/docs/reference/variables/DEFAULT_SEQUENCE_TIMEOUT.md b/docs/reference/variables/DEFAULT_SEQUENCE_TIMEOUT.md index 14a84d70..5709b1b9 100644 --- a/docs/reference/variables/DEFAULT_SEQUENCE_TIMEOUT.md +++ b/docs/reference/variables/DEFAULT_SEQUENCE_TIMEOUT.md @@ -7,7 +7,6 @@ title: DEFAULT_SEQUENCE_TIMEOUT const DEFAULT_SEQUENCE_TIMEOUT: 1000 = 1000; ``` -Defined in: [sequence-manager.ts:58](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/sequence-manager.ts#L58) +Defined in: [match.ts:235](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/match.ts#L235) -Default timeout between keys in a sequence (in milliseconds). -Exported for consumers that display sequence state (e.g. devtools). +Default timeout between sequence steps (in milliseconds). diff --git a/examples/alpine/createHotkeySequence/index.html b/examples/alpine/createHotkey-sequences/index.html similarity index 96% rename from examples/alpine/createHotkeySequence/index.html rename to examples/alpine/createHotkey-sequences/index.html index f1d17032..3f3b9411 100644 --- a/examples/alpine/createHotkeySequence/index.html +++ b/examples/alpine/createHotkey-sequences/index.html @@ -3,15 +3,16 @@ - TanStack Hotkeys | Alpine createHotkeySequence + TanStack Hotkeys | Alpine Sequences with createHotkey
    -

    scope.createHotkeySequence

    +

    Sequences with scope.createHotkey

    - Register multi-key sequences (like Vim commands). Keys must be + Pass an array of chords to scope.createHotkey to + register a multi-key sequence (like Vim commands). Keys must be pressed within the timeout window (default: 1000ms).

    diff --git a/examples/alpine/createHotkeySequences/package.json b/examples/alpine/createHotkey-sequences/package.json similarity index 100% rename from examples/alpine/createHotkeySequences/package.json rename to examples/alpine/createHotkey-sequences/package.json diff --git a/examples/alpine/createHotkeySequence/src/index.css b/examples/alpine/createHotkey-sequences/src/index.css similarity index 100% rename from examples/alpine/createHotkeySequence/src/index.css rename to examples/alpine/createHotkey-sequences/src/index.css diff --git a/examples/alpine/createHotkeySequence/src/main.ts b/examples/alpine/createHotkey-sequences/src/main.ts similarity index 60% rename from examples/alpine/createHotkeySequence/src/main.ts rename to examples/alpine/createHotkey-sequences/src/main.ts index 6d71c933..2f928087 100644 --- a/examples/alpine/createHotkeySequence/src/main.ts +++ b/examples/alpine/createHotkey-sequences/src/main.ts @@ -6,7 +6,7 @@ class App { private hotkeysScope = createHotkeysScope() usage0 = - "scope.createHotkeySequence(['G', 'G'], scrollToTop)\nscope.createHotkeySequence(\n ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown'],\n activateCheatMode,\n { timeout: 1500 },\n)\nscope.createHotkeySequence(['C', 'I', 'W'], changeInnerWord)\nscope.createHotkeySequence(['Shift+[KeyR]', 'Shift+[KeyT]'], runChain)" + "scope.createHotkey(['G', 'G'], scrollToTop)\nscope.createHotkey(\n ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown'],\n activateCheatMode,\n { timeout: 1500 },\n)\nscope.createHotkey(['C', 'I', 'W'], changeInnerWord)\nscope.createHotkey(['Shift+[KeyR]', 'Shift+[KeyT]'], runChain)" lastSequence: string | null = null history: Array = [] helloSequenceEnabled = true @@ -17,42 +17,41 @@ class App { init() { this.addToHistory = this.addToHistory.bind(this) - this.hotkeysScope.createHotkeySequence(['G', 'G'], () => + this.hotkeysScope.createHotkey(['G', 'G'], () => this.addToHistory('gg → Go to top'), ) - this.hotkeysScope.createHotkeySequence(['Shift+G'], () => + this.hotkeysScope.createHotkey(['Shift+G'], () => this.addToHistory('G → Go to bottom'), ) - this.hotkeysScope.createHotkeySequence(['D', 'D'], () => + this.hotkeysScope.createHotkey(['D', 'D'], () => this.addToHistory('dd → Delete line'), ) - this.hotkeysScope.createHotkeySequence(['Y', 'Y'], () => + this.hotkeysScope.createHotkey(['Y', 'Y'], () => this.addToHistory('yy → Yank (copy) line'), ) - this.hotkeysScope.createHotkeySequence(['D', 'W'], () => + this.hotkeysScope.createHotkey(['D', 'W'], () => this.addToHistory('dw → Delete word'), ) - this.hotkeysScope.createHotkeySequence(['C', 'I', 'W'], () => + this.hotkeysScope.createHotkey(['C', 'I', 'W'], () => this.addToHistory('ciw → Change inner word'), ) - this.hotkeysScope.createHotkeySequence( + this.hotkeysScope.createHotkey( ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown'], () => this.addToHistory('↑↑↓↓ → Konami code (partial)'), { timeout: 1500 }, ) - this.hotkeysScope.createHotkeySequence( + this.hotkeysScope.createHotkey( ['ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight'], () => this.addToHistory('←→←→ → Side to side!'), { timeout: 1500 }, ) - this.hotkeysScope.createHotkeySequence( + this.hotkeysScope.createHotkey( ['H', 'E', 'L', 'L', 'O'], () => this.addToHistory('hello → Hello World!'), () => ({ enabled: this.helloSequenceEnabled }), ) - this.hotkeysScope.createHotkeySequence( - ['Shift+[KeyR]', 'Shift+[KeyT]'], - () => this.addToHistory('⇧R ⇧T → Chained Shift+letter (2 steps)'), + this.hotkeysScope.createHotkey(['Shift+[KeyR]', 'Shift+[KeyT]'], () => + this.addToHistory('⇧R ⇧T → Chained Shift+letter (2 steps)'), ) this.hotkeysScope.createHotkey('Escape', () => { this.lastSequence = null diff --git a/examples/alpine/createHotkeySequence/tsconfig.json b/examples/alpine/createHotkey-sequences/tsconfig.json similarity index 100% rename from examples/alpine/createHotkeySequence/tsconfig.json rename to examples/alpine/createHotkey-sequences/tsconfig.json diff --git a/examples/alpine/createHotkeySequenceRecorder/index.html b/examples/alpine/createHotkeyRecorder-sequences/index.html similarity index 82% rename from examples/alpine/createHotkeySequenceRecorder/index.html rename to examples/alpine/createHotkeyRecorder-sequences/index.html index 51ea9104..6c9a71a2 100644 --- a/examples/alpine/createHotkeySequenceRecorder/index.html +++ b/examples/alpine/createHotkeyRecorder-sequences/index.html @@ -3,17 +3,19 @@ - TanStack Hotkeys | Alpine createHotkeySequenceRecorder + TanStack Hotkeys | Alpine Recording Sequences
    -

    Sequence Shortcut Settings

    +

    Recording Sequences

    - Customize Vim-style sequences. Click Edit, press each chord in - order, then press Enter to save. Escape cancels; Backspace removes - the last chord or clears when empty. + scope.createHotkeyRecorder with + maxSteps records up to that many chords. Click Edit, + press each chord in order, then press Enter (or reach four chords) + to save. One chord saves as a regular hotkey. Escape cancels; + Backspace removes the last chord or clears when empty.

    @@ -70,7 +72,7 @@

    Shortcuts