@tanstack/octane-hotkeys 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +139 -2
- package/dist/HotkeysProvider.d.ts +25 -0
- package/dist/HotkeysProvider.js +23 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +16 -0
- package/dist/internal.js +35 -0
- package/dist/types.d.ts +38 -0
- package/dist/useHeldKeyCodes.d.ts +4 -0
- package/dist/useHeldKeyCodes.js +10 -0
- package/dist/useHeldKeys.d.ts +4 -0
- package/dist/useHeldKeys.js +10 -0
- package/dist/useHotkey.d.ts +5 -0
- package/dist/useHotkey.js +14 -0
- package/dist/useHotkeyHint.d.ts +5 -0
- package/dist/useHotkeyHint.js +11 -0
- package/dist/useHotkeyRecorder.d.ts +6 -0
- package/dist/useHotkeyRecorder.js +28 -0
- package/dist/useHotkeyRegistrations.d.ts +5 -0
- package/dist/useHotkeyRegistrations.js +13 -0
- package/dist/useHotkeySequence.d.ts +5 -0
- package/dist/useHotkeySequence.js +14 -0
- package/dist/useHotkeySequenceRecorder.d.ts +6 -0
- package/dist/useHotkeySequenceRecorder.js +29 -0
- package/dist/useHotkeySequences.d.ts +6 -0
- package/dist/useHotkeySequences.js +23 -0
- package/dist/useHotkeys.d.ts +6 -0
- package/dist/useHotkeys.js +23 -0
- package/dist/useKeyHold.d.ts +5 -0
- package/dist/useKeyHold.js +10 -0
- package/package.json +62 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tanner Linsley
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,140 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source
|
|
4
|
+
media="(prefers-color-scheme: dark)"
|
|
5
|
+
srcset="https://tanstack.com/api/readme/hotkeys.png?theme=dark"
|
|
6
|
+
/>
|
|
7
|
+
<source
|
|
8
|
+
media="(prefers-color-scheme: light)"
|
|
9
|
+
srcset="https://tanstack.com/api/readme/hotkeys.png"
|
|
10
|
+
/>
|
|
11
|
+
<img
|
|
12
|
+
src="https://tanstack.com/api/readme/hotkeys.png"
|
|
13
|
+
alt="TanStack Hotkeys"
|
|
14
|
+
width="900"
|
|
15
|
+
/>
|
|
16
|
+
</picture>
|
|
17
|
+
</div>
|
|
2
18
|
|
|
3
|
-
|
|
19
|
+
<br />
|
|
20
|
+
|
|
21
|
+
<div align="center">
|
|
22
|
+
<a href="https://www.npmjs.com/package/@tanstack/hotkeys" target="\_parent">
|
|
23
|
+
<img alt="" src="https://img.shields.io/npm/dm/@tanstack/hotkeys.svg" alt="npm downloads" />
|
|
24
|
+
</a>
|
|
25
|
+
<a href="https://github.com/TanStack/hotkeys" target="\_parent">
|
|
26
|
+
<img alt="" src="https://img.shields.io/github/stars/TanStack/hotkeys.svg?style=social&label=Star" alt="GitHub stars" />
|
|
27
|
+
</a>
|
|
28
|
+
<a href="https://bundlephobia.com/result?p=@tanstack/react-hotkeys@latest" target="\_parent">
|
|
29
|
+
<img alt="" src="https://badgen.net/bundlephobia/minzip/@tanstack/react-hotkeys@latest" alt="Bundle size" />
|
|
30
|
+
</a>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div align="center">
|
|
34
|
+
<a href="#badge">
|
|
35
|
+
<img alt="semantic-release" src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg">
|
|
36
|
+
</a>
|
|
37
|
+
<a href="#badge">
|
|
38
|
+
<img src="https://img.shields.io/github/v/release/tanstack/hotkeys" alt="Release"/>
|
|
39
|
+
</a>
|
|
40
|
+
<a href="https://twitter.com/tan_stack">
|
|
41
|
+
<img src="https://img.shields.io/twitter/follow/tan_stack.svg?style=social" alt="Follow @TanStack"/>
|
|
42
|
+
</a>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div align="center">
|
|
46
|
+
|
|
47
|
+
### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
|
|
48
|
+
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
# TanStack Hotkeys
|
|
52
|
+
|
|
53
|
+
> [!NOTE]
|
|
54
|
+
> TanStack Hotkeys is alpha. We are actively developing the library and are open to feedback and contributions.
|
|
55
|
+
|
|
56
|
+
Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objects, a cross-platform `Mod` key, a singleton Hotkey Manager, and utilities for cheatsheet UIs—built to stay SSR-friendly.
|
|
57
|
+
|
|
58
|
+
- Type-safe bindings — template strings (`Mod+Shift+S`, `Escape`) or parsed objects for full control
|
|
59
|
+
- Flexible options — `keydown`/`keyup`, `preventDefault`, `stopPropagation`, conditional enabled, `requireReset`
|
|
60
|
+
- Cross-platform Mod — maps to Cmd on macOS and Ctrl on Windows/Linux
|
|
61
|
+
- Batteries included — validation + matching, sequences (Vim-style), key-state tracking, recorder UI helpers, framework adapters, and devtools
|
|
62
|
+
|
|
63
|
+
### <a href="https://tanstack.com/hotkeys">Read the docs →</a>
|
|
64
|
+
|
|
65
|
+
<br />
|
|
66
|
+
|
|
67
|
+
> [!NOTE]
|
|
68
|
+
> You may know **TanStack Hotkeys** by our adapter names, too!
|
|
69
|
+
>
|
|
70
|
+
> - [**Alpine Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/alpine/quick-start)
|
|
71
|
+
> - [**Ember Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/ember/quick-start)
|
|
72
|
+
> - [**Octane Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/octane/quick-start)
|
|
73
|
+
> - [**React Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/react/react-hotkeys)
|
|
74
|
+
> - [**Preact Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/preact/preact-hotkeys)
|
|
75
|
+
> - [**Solid Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/solid/reference)
|
|
76
|
+
> - [**Angular Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/angular/reference)
|
|
77
|
+
> - [**Vue Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/vue/reference)
|
|
78
|
+
> - [**Lit Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/lit/reference)
|
|
79
|
+
> - [**Svelte Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/svelte/reference)
|
|
80
|
+
|
|
81
|
+
## Get Involved
|
|
82
|
+
|
|
83
|
+
- We welcome issues and pull requests!
|
|
84
|
+
- Participate in [GitHub discussions](https://github.com/TanStack/hotkeys/discussions)
|
|
85
|
+
- Chat with the community on [Discord](https://discord.com/invite/WrRKjPJ)
|
|
86
|
+
- See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions
|
|
87
|
+
|
|
88
|
+
## Partners
|
|
89
|
+
|
|
90
|
+
<div align="center">
|
|
91
|
+
|
|
92
|
+
<table align="center">
|
|
93
|
+
<tr>
|
|
94
|
+
<td>
|
|
95
|
+
<a href="https://www.coderabbit.ai/?via=tanstack&dub_id=aCcEEdAOqqutX6OS" >
|
|
96
|
+
<picture>
|
|
97
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/coderabbit-dark-D643Zkrv.svg" height="40" />
|
|
98
|
+
<source media="(prefers-color-scheme: light)" srcset="https://tanstack.com/assets/coderabbit-light-CIzGLYU_.svg" height="40" />
|
|
99
|
+
<img src="https://tanstack.com/assets/coderabbit-light-DVMJ2jHi.svg" height="40" alt="CodeRabbit" />
|
|
100
|
+
</picture>
|
|
101
|
+
</a>
|
|
102
|
+
</td>
|
|
103
|
+
<td>
|
|
104
|
+
<a href="https://www.cloudflare.com?utm_source=tanstack">
|
|
105
|
+
<picture>
|
|
106
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/cloudflare-white-Co-Tyjbl.svg" height="60" />
|
|
107
|
+
<source media="(prefers-color-scheme: light)" srcset="https://tanstack.com/assets/cloudflare-black-6Ojsn8yh.svg" height="60" />
|
|
108
|
+
<img src="https://tanstack.com/assets/cloudflare-black-CPufaW0B.svg" height="60" alt="Cloudflare" />
|
|
109
|
+
</picture>
|
|
110
|
+
</a>
|
|
111
|
+
</td>
|
|
112
|
+
</tr>
|
|
113
|
+
</table>
|
|
114
|
+
|
|
115
|
+
<div align="center">
|
|
116
|
+
<img src="media/partner_logo.svg" alt="Keys & you?" height="65">
|
|
117
|
+
<p>
|
|
118
|
+
We're looking for TanStack Hotkeys Partners to join our mission! Partner with us to push the boundaries of TanStack Hotkeys and build amazing things together.
|
|
119
|
+
</p>
|
|
120
|
+
<a href="mailto:partners@tanstack.com?subject=TanStack Hotkeys Partnership"><b>LET'S CHAT</b></a>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
## Explore the TanStack Ecosystem
|
|
126
|
+
|
|
127
|
+
- <a href="https://github.com/tanstack/config"><b>TanStack Config</b></a> – Tooling for JS/TS packages
|
|
128
|
+
- <a href="https://github.com/tanstack/db"><b>TanStack DB</b></a> – Reactive sync client store
|
|
129
|
+
- <a href="https://github.com/tanstack/devtools"><b>TanStack DevTools</b></a> – Unified devtools panel
|
|
130
|
+
- <a href="https://github.com/tanstack/form"><b>TanStack Form</b></a> – Type‑safe form state
|
|
131
|
+
- <a href="https://github.com/tanstack/hotkeys"><b>TanStack Hotkeys</b></a> – Type‑safe keyboard shortcuts
|
|
132
|
+
- <a href="https://github.com/tanstack/query"><b>TanStack Query</b></a> – Async state & caching
|
|
133
|
+
- <a href="https://github.com/tanstack/ranger"><b>TanStack Ranger</b></a> – Range & slider primitives
|
|
134
|
+
- <a href="https://github.com/tanstack/router"><b>TanStack Router</b></a> – Type‑safe routing, caching & URL state
|
|
135
|
+
- <a href="https://github.com/tanstack/start"><b>TanStack Start</b></a> – Full‑stack SSR & streaming
|
|
136
|
+
- <a href="https://github.com/tanstack/store"><b>TanStack Store</b></a> – Reactive data store
|
|
137
|
+
- <a href="https://github.com/tanstack/table"><b>TanStack Table</b></a> – Headless datagrids
|
|
138
|
+
- <a href="https://github.com/tanstack/virtual"><b>TanStack Virtual</b></a> – Virtualized rendering
|
|
139
|
+
|
|
140
|
+
… and more at <a href="https://tanstack.com"><b>TanStack.com »</b></a>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { HotkeyOptions, HotkeyRecorderOptions, HotkeySequenceRecorderOptions, SequenceOptions } from "@tanstack/hotkeys";
|
|
2
|
+
//#region src/HotkeysProvider.d.ts
|
|
3
|
+
export interface DefaultHotkeysOptions {
|
|
4
|
+
hotkey?: HotkeyOptions;
|
|
5
|
+
hotkeySequence?: SequenceOptions;
|
|
6
|
+
hotkeyRecorder?: Partial<HotkeyRecorderOptions>;
|
|
7
|
+
hotkeySequenceRecorder?: Partial<HotkeySequenceRecorderOptions>;
|
|
8
|
+
}
|
|
9
|
+
export type HotkeysProviderOptions = DefaultHotkeysOptions;
|
|
10
|
+
export interface HotkeysProviderProps {
|
|
11
|
+
children?: unknown;
|
|
12
|
+
defaultOptions?: HotkeysProviderOptions;
|
|
13
|
+
}
|
|
14
|
+
/** Reads the nearest provider, or null outside a provider. */
|
|
15
|
+
export declare function useHotkeysContext(): {
|
|
16
|
+
defaultOptions: DefaultHotkeysOptions;
|
|
17
|
+
} | null;
|
|
18
|
+
/** Reads the nearest provider's defaults, or an empty object. */
|
|
19
|
+
export declare function useDefaultHotkeysOptions(): DefaultHotkeysOptions;
|
|
20
|
+
/** Sets registration and recorder defaults for descendant components. */
|
|
21
|
+
export declare function HotkeysProvider({ children, defaultOptions }: HotkeysProviderProps): import("octane").ElementDescriptor<{
|
|
22
|
+
value: DefaultHotkeysOptions;
|
|
23
|
+
children: unknown;
|
|
24
|
+
}>;
|
|
25
|
+
//#endregion
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createContext, createElement, useContext } from "octane";
|
|
2
|
+
|
|
3
|
+
//#region src/HotkeysProvider.ts
|
|
4
|
+
const HotkeysContext = createContext(null);
|
|
5
|
+
/** Reads the nearest provider, or null outside a provider. */
|
|
6
|
+
function useHotkeysContext() {
|
|
7
|
+
const defaultOptions = useContext(HotkeysContext);
|
|
8
|
+
return defaultOptions === null ? null : { defaultOptions };
|
|
9
|
+
}
|
|
10
|
+
/** Reads the nearest provider's defaults, or an empty object. */
|
|
11
|
+
function useDefaultHotkeysOptions() {
|
|
12
|
+
return useContext(HotkeysContext) ?? {};
|
|
13
|
+
}
|
|
14
|
+
/** Sets registration and recorder defaults for descendant components. */
|
|
15
|
+
function HotkeysProvider({ children, defaultOptions = {} }) {
|
|
16
|
+
return createElement(HotkeysContext.Provider, {
|
|
17
|
+
value: defaultOptions,
|
|
18
|
+
children
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { HotkeysProvider, useDefaultHotkeysOptions, useHotkeysContext };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DefaultHotkeysOptions, HotkeysProvider, HotkeysProviderOptions, HotkeysProviderProps, useDefaultHotkeysOptions, useHotkeysContext } from "./HotkeysProvider.js";
|
|
2
|
+
import { useHotkeys } from "./useHotkeys.js";
|
|
3
|
+
import { useHotkey } from "./useHotkey.js";
|
|
4
|
+
import { useHotkeySequences } from "./useHotkeySequences.js";
|
|
5
|
+
import { useHotkeySequence } from "./useHotkeySequence.js";
|
|
6
|
+
import { useHeldKeys } from "./useHeldKeys.js";
|
|
7
|
+
import { useHeldKeyCodes } from "./useHeldKeyCodes.js";
|
|
8
|
+
import { useKeyHold } from "./useKeyHold.js";
|
|
9
|
+
import { useHotkeyHint } from "./useHotkeyHint.js";
|
|
10
|
+
import { HotkeyRegistrationsResult, OctaneHotkeyRecorder, OctaneHotkeySequenceRecorder } from "./types.js";
|
|
11
|
+
import { useHotkeyRegistrations } from "./useHotkeyRegistrations.js";
|
|
12
|
+
import { useHotkeyRecorder } from "./useHotkeyRecorder.js";
|
|
13
|
+
import { useHotkeySequenceRecorder } from "./useHotkeySequenceRecorder.js";
|
|
14
|
+
import { HotkeyDefinition, HotkeySequenceDefinition } from "@tanstack/hotkeys/adapter";
|
|
15
|
+
export * from "@tanstack/hotkeys";
|
|
16
|
+
export { type DefaultHotkeysOptions, type HotkeyDefinition, HotkeyRegistrationsResult, type HotkeySequenceDefinition, HotkeysProvider, type HotkeysProviderOptions, type HotkeysProviderProps, OctaneHotkeyRecorder, OctaneHotkeySequenceRecorder, useDefaultHotkeysOptions, useHeldKeyCodes, useHeldKeys, useHotkey, useHotkeyHint, useHotkeyRecorder, useHotkeyRegistrations, useHotkeySequence, useHotkeySequenceRecorder, useHotkeySequences, useHotkeys, useHotkeysContext, useKeyHold };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { HotkeysProvider, useDefaultHotkeysOptions, useHotkeysContext } from "./HotkeysProvider.js";
|
|
2
|
+
import { useHotkeys } from "./useHotkeys.js";
|
|
3
|
+
import { useHotkey } from "./useHotkey.js";
|
|
4
|
+
import { useHotkeySequences } from "./useHotkeySequences.js";
|
|
5
|
+
import { useHotkeySequence } from "./useHotkeySequence.js";
|
|
6
|
+
import { useHeldKeys } from "./useHeldKeys.js";
|
|
7
|
+
import { useHeldKeyCodes } from "./useHeldKeyCodes.js";
|
|
8
|
+
import { useKeyHold } from "./useKeyHold.js";
|
|
9
|
+
import { useHotkeyHint } from "./useHotkeyHint.js";
|
|
10
|
+
import { useHotkeyRegistrations } from "./useHotkeyRegistrations.js";
|
|
11
|
+
import { useHotkeyRecorder } from "./useHotkeyRecorder.js";
|
|
12
|
+
import { useHotkeySequenceRecorder } from "./useHotkeySequenceRecorder.js";
|
|
13
|
+
|
|
14
|
+
export * from "@tanstack/hotkeys"
|
|
15
|
+
|
|
16
|
+
export { HotkeysProvider, useDefaultHotkeysOptions, useHeldKeyCodes, useHeldKeys, useHotkey, useHotkeyHint, useHotkeyRecorder, useHotkeyRegistrations, useHotkeySequence, useHotkeySequenceRecorder, useHotkeySequences, useHotkeys, useHotkeysContext, useKeyHold };
|
package/dist/internal.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useSelector } from "@tanstack/octane-store";
|
|
2
|
+
|
|
3
|
+
//#region src/internal.ts
|
|
4
|
+
const subSlotCache = /* @__PURE__ */ new Map();
|
|
5
|
+
const bareTagCache = /* @__PURE__ */ new Map();
|
|
6
|
+
function subSlot(slot, tag) {
|
|
7
|
+
if (slot === void 0) {
|
|
8
|
+
let bare = bareTagCache.get(tag);
|
|
9
|
+
if (bare === void 0) {
|
|
10
|
+
bare = Symbol.for(`@tanstack/octane-hotkeys:${tag}`);
|
|
11
|
+
bareTagCache.set(tag, bare);
|
|
12
|
+
}
|
|
13
|
+
return bare;
|
|
14
|
+
}
|
|
15
|
+
let byTag = subSlotCache.get(slot);
|
|
16
|
+
if (byTag === void 0) {
|
|
17
|
+
byTag = /* @__PURE__ */ new Map();
|
|
18
|
+
subSlotCache.set(slot, byTag);
|
|
19
|
+
}
|
|
20
|
+
let child = byTag.get(tag);
|
|
21
|
+
if (child === void 0) {
|
|
22
|
+
child = Symbol.for(`${slot.description ?? ""}:${tag}`);
|
|
23
|
+
byTag.set(tag, child);
|
|
24
|
+
}
|
|
25
|
+
return child;
|
|
26
|
+
}
|
|
27
|
+
function splitSlot(args) {
|
|
28
|
+
const tail = args[args.length - 1];
|
|
29
|
+
const slot = typeof tail === "symbol" ? tail : void 0;
|
|
30
|
+
return [slot === void 0 ? args : args.slice(0, -1), slot];
|
|
31
|
+
}
|
|
32
|
+
const select = useSelector;
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
export { select, splitSlot, subSlot };
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Hotkey, HotkeyRegistrationView, HotkeySequence, SequenceRegistrationView } from "@tanstack/hotkeys";
|
|
2
|
+
//#region src/types.d.ts
|
|
3
|
+
/** Recording state and controls owned by the containing component. */
|
|
4
|
+
export interface OctaneHotkeyRecorder {
|
|
5
|
+
/** Whether the recorder is listening for a shortcut. */
|
|
6
|
+
isRecording: boolean;
|
|
7
|
+
/** The last recorded binding, including brackets for physical codes. */
|
|
8
|
+
recordedHotkey: Hotkey | null;
|
|
9
|
+
/** Start a new recording session. */
|
|
10
|
+
startRecording: () => void;
|
|
11
|
+
/** Stop and clear recorder state without calling onRecord or onCancel. */
|
|
12
|
+
stopRecording: () => void;
|
|
13
|
+
/** Stop, clear recorder state, and call onCancel. */
|
|
14
|
+
cancelRecording: () => void;
|
|
15
|
+
}
|
|
16
|
+
/** Sequence recording state and controls. */
|
|
17
|
+
export interface OctaneHotkeySequenceRecorder {
|
|
18
|
+
/** Whether the recorder is listening for chords. */
|
|
19
|
+
isRecording: boolean;
|
|
20
|
+
/** Chords captured in the current session. */
|
|
21
|
+
steps: HotkeySequence;
|
|
22
|
+
/** The last committed sequence. */
|
|
23
|
+
recordedSequence: HotkeySequence | null;
|
|
24
|
+
/** Start a new recording session. */
|
|
25
|
+
startRecording: () => void;
|
|
26
|
+
/** Stop without committing or calling onCancel. */
|
|
27
|
+
stopRecording: () => void;
|
|
28
|
+
/** Discard the current session and call onCancel. */
|
|
29
|
+
cancelRecording: () => void;
|
|
30
|
+
/** Commit the current steps. Does nothing when no steps are recorded. */
|
|
31
|
+
commitRecording: () => void;
|
|
32
|
+
}
|
|
33
|
+
/** Live registration views from the shared hotkey and sequence managers. */
|
|
34
|
+
export interface HotkeyRegistrationsResult {
|
|
35
|
+
hotkeys: Array<HotkeyRegistrationView>;
|
|
36
|
+
sequences: Array<SequenceRegistrationView>;
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { select, subSlot } from "./internal.js";
|
|
2
|
+
import { getKeyStateTracker } from "@tanstack/hotkeys";
|
|
3
|
+
|
|
4
|
+
//#region src/useHeldKeyCodes.ts
|
|
5
|
+
function useHeldKeyCodes(slot) {
|
|
6
|
+
return select(getKeyStateTracker().store, (state) => state.heldCodes, void 0, subSlot(slot, "held-codes"));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { useHeldKeyCodes };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { select, subSlot } from "./internal.js";
|
|
2
|
+
import { getKeyStateTracker } from "@tanstack/hotkeys";
|
|
3
|
+
|
|
4
|
+
//#region src/useHeldKeys.ts
|
|
5
|
+
function useHeldKeys(slot) {
|
|
6
|
+
return select(getKeyStateTracker().store, (state) => state.heldKeys, void 0, subSlot(slot, "held-keys"));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { useHeldKeys };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { HotkeyCallback, HotkeyOptions, RegisterableHotkey } from "@tanstack/hotkeys";
|
|
2
|
+
//#region src/useHotkey.d.ts
|
|
3
|
+
/** Registers one shortcut. Callback and options are refreshed after every commit. */
|
|
4
|
+
export declare function useHotkey(hotkey: RegisterableHotkey, callback: HotkeyCallback, options?: HotkeyOptions): void;
|
|
5
|
+
//#endregion
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { splitSlot, subSlot } from "./internal.js";
|
|
2
|
+
import { useHotkeys } from "./useHotkeys.js";
|
|
3
|
+
|
|
4
|
+
//#region src/useHotkey.ts
|
|
5
|
+
function useHotkey(hotkey, callback, ...rest) {
|
|
6
|
+
const [args, slot] = splitSlot(rest);
|
|
7
|
+
useHotkeys([{
|
|
8
|
+
hotkey,
|
|
9
|
+
callback
|
|
10
|
+
}], args[0], subSlot(slot, "hotkey"));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
14
|
+
export { useHotkey };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { HeldModifierOptions, RegisterableHotkey } from "@tanstack/hotkeys";
|
|
2
|
+
//#region src/useHotkeyHint.d.ts
|
|
3
|
+
/** Reports whether held modifiers reveal a shortcut hint. */
|
|
4
|
+
export declare function useHotkeyHint(hotkey: RegisterableHotkey, options?: HeldModifierOptions): boolean;
|
|
5
|
+
//#endregion
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { select, splitSlot, subSlot } from "./internal.js";
|
|
2
|
+
import { getKeyStateTracker, matchesHeldModifiers } from "@tanstack/hotkeys";
|
|
3
|
+
|
|
4
|
+
//#region src/useHotkeyHint.ts
|
|
5
|
+
function useHotkeyHint(hotkey, ...rest) {
|
|
6
|
+
const [args, slot] = splitSlot(rest);
|
|
7
|
+
return select(getKeyStateTracker().store, (state) => matchesHeldModifiers(hotkey, state.heldKeys, args[0]), void 0, subSlot(slot, "hint"));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
export { useHotkeyHint };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { OctaneHotkeyRecorder } from "./types.js";
|
|
2
|
+
import { HotkeyRecorderOptions } from "@tanstack/hotkeys";
|
|
3
|
+
//#region src/useHotkeyRecorder.d.ts
|
|
4
|
+
/** Records a shortcut and destroys the recorder on unmount. */
|
|
5
|
+
export declare function useHotkeyRecorder(options: HotkeyRecorderOptions): OctaneHotkeyRecorder;
|
|
6
|
+
//#endregion
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useDefaultHotkeysOptions } from "./HotkeysProvider.js";
|
|
2
|
+
import { select, subSlot } from "./internal.js";
|
|
3
|
+
import { HotkeyRecorder } from "@tanstack/hotkeys";
|
|
4
|
+
import { useLayoutEffect, useRef } from "octane";
|
|
5
|
+
|
|
6
|
+
//#region src/useHotkeyRecorder.ts
|
|
7
|
+
function useHotkeyRecorder(options, slot) {
|
|
8
|
+
const merged = {
|
|
9
|
+
...useDefaultHotkeysOptions().hotkeyRecorder,
|
|
10
|
+
...options
|
|
11
|
+
};
|
|
12
|
+
const ref = useRef(null, subSlot(slot, "recorder"));
|
|
13
|
+
ref.current ??= new HotkeyRecorder(merged);
|
|
14
|
+
const recorder = ref.current;
|
|
15
|
+
useLayoutEffect(() => {
|
|
16
|
+
recorder.setOptions(merged);
|
|
17
|
+
}, null, subSlot(slot, "options"));
|
|
18
|
+
useLayoutEffect(() => () => recorder.destroy(), [], subSlot(slot, "cleanup"));
|
|
19
|
+
return {
|
|
20
|
+
...select(recorder.store, (value) => value, void 0, subSlot(slot, "state")),
|
|
21
|
+
startRecording: () => recorder.start(),
|
|
22
|
+
stopRecording: () => recorder.stop(),
|
|
23
|
+
cancelRecording: () => recorder.cancel()
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
export { useHotkeyRecorder };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { select, subSlot } from "./internal.js";
|
|
2
|
+
import { getHotkeyManager, getSequenceManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
|
|
3
|
+
|
|
4
|
+
//#region src/useHotkeyRegistrations.ts
|
|
5
|
+
function useHotkeyRegistrations(slot) {
|
|
6
|
+
return {
|
|
7
|
+
hotkeys: select(getHotkeyManager().registrations, (state) => Array.from(state.values()).map(toHotkeyRegistrationView), void 0, subSlot(slot, "hotkeys")),
|
|
8
|
+
sequences: select(getSequenceManager().registrations, (state) => Array.from(state.values()), void 0, subSlot(slot, "sequences"))
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
export { useHotkeyRegistrations };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { HotkeyCallback, HotkeySequence, SequenceOptions } from "@tanstack/hotkeys";
|
|
2
|
+
//#region src/useHotkeySequence.d.ts
|
|
3
|
+
/** Registers one multi-chord shortcut. Empty sequences do not register. */
|
|
4
|
+
export declare function useHotkeySequence(sequence: HotkeySequence, callback: HotkeyCallback, options?: SequenceOptions): void;
|
|
5
|
+
//#endregion
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { splitSlot, subSlot } from "./internal.js";
|
|
2
|
+
import { useHotkeySequences } from "./useHotkeySequences.js";
|
|
3
|
+
|
|
4
|
+
//#region src/useHotkeySequence.ts
|
|
5
|
+
function useHotkeySequence(sequence, callback, ...rest) {
|
|
6
|
+
const [args, slot] = splitSlot(rest);
|
|
7
|
+
useHotkeySequences([{
|
|
8
|
+
sequence,
|
|
9
|
+
callback
|
|
10
|
+
}], args[0], subSlot(slot, "sequence"));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
14
|
+
export { useHotkeySequence };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { OctaneHotkeySequenceRecorder } from "./types.js";
|
|
2
|
+
import { HotkeySequenceRecorderOptions } from "@tanstack/hotkeys";
|
|
3
|
+
//#region src/useHotkeySequenceRecorder.d.ts
|
|
4
|
+
/** Records a sequence, including live steps and explicit commit/cancel controls. */
|
|
5
|
+
export declare function useHotkeySequenceRecorder(options: HotkeySequenceRecorderOptions): OctaneHotkeySequenceRecorder;
|
|
6
|
+
//#endregion
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useDefaultHotkeysOptions } from "./HotkeysProvider.js";
|
|
2
|
+
import { select, subSlot } from "./internal.js";
|
|
3
|
+
import { HotkeySequenceRecorder } from "@tanstack/hotkeys";
|
|
4
|
+
import { useLayoutEffect, useRef } from "octane";
|
|
5
|
+
|
|
6
|
+
//#region src/useHotkeySequenceRecorder.ts
|
|
7
|
+
function useHotkeySequenceRecorder(options, slot) {
|
|
8
|
+
const merged = {
|
|
9
|
+
...useDefaultHotkeysOptions().hotkeySequenceRecorder,
|
|
10
|
+
...options
|
|
11
|
+
};
|
|
12
|
+
const ref = useRef(null, subSlot(slot, "recorder"));
|
|
13
|
+
ref.current ??= new HotkeySequenceRecorder(merged);
|
|
14
|
+
const recorder = ref.current;
|
|
15
|
+
useLayoutEffect(() => {
|
|
16
|
+
recorder.setOptions(merged);
|
|
17
|
+
}, null, subSlot(slot, "options"));
|
|
18
|
+
useLayoutEffect(() => () => recorder.destroy(), [], subSlot(slot, "cleanup"));
|
|
19
|
+
return {
|
|
20
|
+
...select(recorder.store, (value) => value, void 0, subSlot(slot, "state")),
|
|
21
|
+
startRecording: () => recorder.start(),
|
|
22
|
+
stopRecording: () => recorder.stop(),
|
|
23
|
+
cancelRecording: () => recorder.cancel(),
|
|
24
|
+
commitRecording: () => recorder.commit()
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { useHotkeySequenceRecorder };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SequenceOptions } from "@tanstack/hotkeys";
|
|
2
|
+
import { HotkeySequenceDefinition } from "@tanstack/hotkeys/adapter";
|
|
3
|
+
//#region src/useHotkeySequences.d.ts
|
|
4
|
+
/** Registers a changing list of sequences and releases it on unmount. */
|
|
5
|
+
export declare function useHotkeySequences(definitions: Array<HotkeySequenceDefinition>, options?: SequenceOptions): void;
|
|
6
|
+
//#endregion
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useDefaultHotkeysOptions } from "./HotkeysProvider.js";
|
|
2
|
+
import { splitSlot, subSlot } from "./internal.js";
|
|
3
|
+
import { useLayoutEffect, useRef } from "octane";
|
|
4
|
+
import { createHotkeySequenceBindings } from "@tanstack/hotkeys/adapter";
|
|
5
|
+
|
|
6
|
+
//#region src/useHotkeySequences.ts
|
|
7
|
+
function useHotkeySequences(definitions, ...rest) {
|
|
8
|
+
const [args, slot] = splitSlot(rest);
|
|
9
|
+
const options = {
|
|
10
|
+
...useDefaultHotkeysOptions().hotkeySequence,
|
|
11
|
+
...args[0]
|
|
12
|
+
};
|
|
13
|
+
const ref = useRef(null, subSlot(slot, "bindings"));
|
|
14
|
+
ref.current ??= createHotkeySequenceBindings();
|
|
15
|
+
const bindings = ref.current;
|
|
16
|
+
useLayoutEffect(() => {
|
|
17
|
+
bindings.update(definitions, options);
|
|
18
|
+
}, null, subSlot(slot, "update"));
|
|
19
|
+
useLayoutEffect(() => () => bindings.destroy(), [], subSlot(slot, "cleanup"));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { useHotkeySequences };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { HotkeyOptions } from "@tanstack/hotkeys";
|
|
2
|
+
import { HotkeyDefinition } from "@tanstack/hotkeys/adapter";
|
|
3
|
+
//#region src/useHotkeys.d.ts
|
|
4
|
+
/** Registers a changing list of shortcuts after commit and releases it on unmount. */
|
|
5
|
+
export declare function useHotkeys(definitions: Array<HotkeyDefinition>, options?: HotkeyOptions): void;
|
|
6
|
+
//#endregion
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useDefaultHotkeysOptions } from "./HotkeysProvider.js";
|
|
2
|
+
import { splitSlot, subSlot } from "./internal.js";
|
|
3
|
+
import { useLayoutEffect, useRef } from "octane";
|
|
4
|
+
import { createHotkeyBindings } from "@tanstack/hotkeys/adapter";
|
|
5
|
+
|
|
6
|
+
//#region src/useHotkeys.ts
|
|
7
|
+
function useHotkeys(definitions, ...rest) {
|
|
8
|
+
const [args, slot] = splitSlot(rest);
|
|
9
|
+
const options = {
|
|
10
|
+
...useDefaultHotkeysOptions().hotkey,
|
|
11
|
+
...args[0]
|
|
12
|
+
};
|
|
13
|
+
const ref = useRef(null, subSlot(slot, "bindings"));
|
|
14
|
+
ref.current ??= createHotkeyBindings();
|
|
15
|
+
const bindings = ref.current;
|
|
16
|
+
useLayoutEffect(() => {
|
|
17
|
+
bindings.update(definitions, options);
|
|
18
|
+
}, null, subSlot(slot, "update"));
|
|
19
|
+
useLayoutEffect(() => () => bindings.destroy(), [], subSlot(slot, "cleanup"));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { useHotkeys };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { select, subSlot } from "./internal.js";
|
|
2
|
+
import { getKeyStateTracker } from "@tanstack/hotkeys";
|
|
3
|
+
|
|
4
|
+
//#region src/useKeyHold.ts
|
|
5
|
+
function useKeyHold(key, slot) {
|
|
6
|
+
return select(getKeyStateTracker().store, (state) => state.heldKeys.includes(key), void 0, subSlot(slot, "key-hold"));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { useKeyHold };
|
package/package.json
CHANGED
|
@@ -1,6 +1,65 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/octane-hotkeys",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Octane adapter for TanStack Hotkeys",
|
|
5
|
+
"author": "Tanner Linsley",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/TanStack/hotkeys.git",
|
|
10
|
+
"directory": "packages/octane-hotkeys"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://tanstack.com/hotkeys",
|
|
13
|
+
"funding": {
|
|
14
|
+
"type": "github",
|
|
15
|
+
"url": "https://github.com/sponsors/tannerlinsley"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"octane",
|
|
19
|
+
"tanstack",
|
|
20
|
+
"hotkeys"
|
|
21
|
+
],
|
|
22
|
+
"type": "module",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": "./dist/index.js",
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22.22.2"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@tanstack/hotkeys": "0.11.0",
|
|
37
|
+
"@tanstack/octane-store": "^0.12.3"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@tsrx/typescript-plugin": "^0.3.138",
|
|
41
|
+
"octane": "0.1.36",
|
|
42
|
+
"vite": "^8.3.2"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"octane": ">=0.1.36 <0.2.0"
|
|
46
|
+
},
|
|
47
|
+
"octane": {
|
|
48
|
+
"hookSlots": {
|
|
49
|
+
"manual": [
|
|
50
|
+
"dist"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"clean": "premove ./build ./dist",
|
|
56
|
+
"lint": "eslint ./src",
|
|
57
|
+
"lint:fix": "eslint ./src --fix",
|
|
58
|
+
"test:eslint": "eslint ./src",
|
|
59
|
+
"test:lib": "vitest run && vitest run --config vitest.ssr.config.ts",
|
|
60
|
+
"test:lib:dev": "vitest --watch",
|
|
61
|
+
"test:types": "tsrx-tsc --noEmit",
|
|
62
|
+
"build": "tsdown",
|
|
63
|
+
"test:build": "publint --strict && node ../../scripts/verify-package.ts"
|
|
64
|
+
}
|
|
6
65
|
}
|