@tooee/commands 0.1.16 → 0.1.17
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/dist/context.d.ts +4 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +90 -11
- package/dist/context.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/sequence.d.ts +17 -1
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +46 -6
- package/dist/sequence.js.map +1 -1
- package/dist/types.d.ts +23 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/use-actions.d.ts +4 -0
- package/dist/use-actions.d.ts.map +1 -1
- package/dist/use-actions.js +7 -1
- package/dist/use-actions.js.map +1 -1
- package/package.json +1 -1
- package/src/context.tsx +112 -11
- package/src/index.ts +17 -2
- package/src/sequence.ts +65 -6
- package/src/types.ts +27 -0
- package/src/use-actions.ts +15 -1
package/dist/context.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { Command, CommandContext, CommandRegistry } from "./types.js";
|
|
2
|
+
import type { Command, CommandContext, CommandGroup, CommandRegistry, CommandSequenceState, RegisteredCommandGroup } from "./types.js";
|
|
3
3
|
import type { Mode } from "./mode.js";
|
|
4
4
|
type ContextGetter = () => Partial<CommandContext>;
|
|
5
5
|
interface CommandContextValue {
|
|
6
6
|
registry: CommandRegistry;
|
|
7
7
|
leaderKey?: string;
|
|
8
8
|
contextSources: Map<string, ContextGetter>;
|
|
9
|
+
groups: Map<string, RegisteredCommandGroup>;
|
|
9
10
|
}
|
|
10
11
|
declare const CommandContext: import("react").Context<CommandContextValue | null>;
|
|
11
12
|
export interface CommandProviderProps {
|
|
@@ -20,6 +21,8 @@ export declare function useCommandContext(): {
|
|
|
20
21
|
invoke: (id: string) => void;
|
|
21
22
|
};
|
|
22
23
|
export declare function useCommandRegistry(): CommandContextValue;
|
|
24
|
+
export declare function useCommandSequenceState(): CommandSequenceState | null;
|
|
25
|
+
export declare function useCommandGroup(group: CommandGroup): void;
|
|
23
26
|
export declare function useProvideCommandContext(getter: () => Partial<CommandContext>): void;
|
|
24
27
|
export {};
|
|
25
28
|
//# sourceMappingURL=context.d.ts.map
|
package/dist/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAA;AAEd,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,YAAY,EACZ,eAAe,EACf,oBAAoB,EAGpB,sBAAsB,EACvB,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAQrC,KAAK,aAAa,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,CAAA;AAElD,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,eAAe,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IAC1C,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAA;CAC5C;AAED,QAAA,MAAM,cAAc,qDAAkD,CAAA;AAGtE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,SAAS,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,WAAW,CAAC,EAAE,IAAI,CAAA;CACnB;AAED,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,oBAAoB,aAQ9F;AA8KD,wBAAgB,iBAAiB,IAAI;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,CAazF;AAED,wBAAgB,kBAAkB,IAAI,mBAAmB,CAMxD;AAED,wBAAgB,uBAAuB,IAAI,oBAAoB,GAAG,IAAI,CAErE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CA8BzD;AAkBD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,CAuBpF"}
|
package/dist/context.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
-
import { createContext, useContext, useRef, useCallback, useEffect, useMemo, } from "react";
|
|
2
|
+
import { createContext, useContext, useRef, useCallback, useEffect, useMemo, useState, } from "react";
|
|
3
3
|
import { useKeyboard } from "@opentui/react";
|
|
4
4
|
import { ModeProvider, useMode, useSetMode } from "./mode.js";
|
|
5
5
|
import { parseHotkey } from "./parse.js";
|
|
@@ -7,16 +7,21 @@ import { matchStep } from "./match.js";
|
|
|
7
7
|
import { SequenceTracker } from "./sequence.js";
|
|
8
8
|
const DEFAULT_MODES = ["cursor"];
|
|
9
9
|
const CommandContext = createContext(null);
|
|
10
|
+
const CommandSequenceContext = createContext(null);
|
|
10
11
|
export function CommandProvider({ children, leader, keymap, initialMode }) {
|
|
11
12
|
return (_jsx(ModeProvider, { initialMode: initialMode, children: _jsx(CommandDispatcher, { leader: leader, keymap: keymap, children: children }) }));
|
|
12
13
|
}
|
|
13
14
|
function CommandDispatcher({ children, leader, keymap, }) {
|
|
14
15
|
const registryRef = useRef(null);
|
|
15
16
|
const contextSourcesRef = useRef(new Map());
|
|
17
|
+
const groupsRef = useRef(new Map());
|
|
16
18
|
const mode = useMode();
|
|
17
19
|
const modeRef = useRef(mode);
|
|
18
20
|
modeRef.current = mode;
|
|
19
21
|
const setMode = useSetMode();
|
|
22
|
+
const [sequenceState, setSequenceState] = useState(null);
|
|
23
|
+
const clearSequenceStateRef = useRef(() => setSequenceState(null));
|
|
24
|
+
clearSequenceStateRef.current = () => setSequenceState(null);
|
|
20
25
|
const buildCtx = useCallback(() => {
|
|
21
26
|
const ctx = {
|
|
22
27
|
mode: modeRef.current,
|
|
@@ -51,7 +56,7 @@ function CommandDispatcher({ children, leader, keymap, }) {
|
|
|
51
56
|
},
|
|
52
57
|
};
|
|
53
58
|
}
|
|
54
|
-
const trackerRef = useRef(new SequenceTracker());
|
|
59
|
+
const trackerRef = useRef(new SequenceTracker({ onReset: () => clearSequenceStateRef.current() }));
|
|
55
60
|
const parseCacheRef = useRef(new Map());
|
|
56
61
|
const getParsedHotkey = useCallback((hotkey) => {
|
|
57
62
|
const cache = parseCacheRef.current;
|
|
@@ -73,8 +78,7 @@ function CommandDispatcher({ children, leader, keymap, }) {
|
|
|
73
78
|
const ctx = buildCtx();
|
|
74
79
|
// Collect eligible commands with their parsed hotkeys
|
|
75
80
|
const singleStepCandidates = [];
|
|
76
|
-
const
|
|
77
|
-
const multiStepCommands = [];
|
|
81
|
+
const multiStepCandidates = [];
|
|
78
82
|
for (const command of registry.commands.values()) {
|
|
79
83
|
const commandModes = command.modes ?? DEFAULT_MODES;
|
|
80
84
|
if (!commandModes.includes(currentMode))
|
|
@@ -89,34 +93,61 @@ function CommandDispatcher({ children, leader, keymap, }) {
|
|
|
89
93
|
singleStepCandidates.push({ command, parsed });
|
|
90
94
|
}
|
|
91
95
|
else {
|
|
92
|
-
|
|
93
|
-
multiStepCommands.push(command);
|
|
96
|
+
multiStepCandidates.push({ command, hotkey, parsed });
|
|
94
97
|
}
|
|
95
98
|
}
|
|
96
99
|
// Check multi-step sequences first (they consume buffer state)
|
|
97
|
-
if (
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
+
if (multiStepCandidates.length > 0) {
|
|
101
|
+
const multiStepHotkeys = multiStepCandidates.map((candidate) => candidate.parsed);
|
|
102
|
+
const result = trackerRef.current.feedWithState(event, multiStepHotkeys);
|
|
103
|
+
if (result.matchedIndex >= 0) {
|
|
100
104
|
event.preventDefault();
|
|
101
|
-
|
|
105
|
+
setSequenceState(null);
|
|
106
|
+
multiStepCandidates[result.matchedIndex].command.handler(ctx);
|
|
102
107
|
return;
|
|
103
108
|
}
|
|
109
|
+
if (result.pending) {
|
|
110
|
+
const firstCandidate = multiStepCandidates[result.pending.indexes[0]];
|
|
111
|
+
setSequenceState({
|
|
112
|
+
prefix: firstCandidate.parsed.steps.slice(0, result.pending.prefixLength),
|
|
113
|
+
candidates: result.pending.indexes
|
|
114
|
+
.map((idx) => multiStepCandidates[idx])
|
|
115
|
+
.filter(({ command }) => !command.hidden)
|
|
116
|
+
.map(({ command, hotkey, parsed }) => ({
|
|
117
|
+
command,
|
|
118
|
+
hotkey,
|
|
119
|
+
steps: parsed.steps,
|
|
120
|
+
remainingSteps: parsed.steps.slice(result.pending.prefixLength),
|
|
121
|
+
nextStep: parsed.steps[result.pending.prefixLength],
|
|
122
|
+
group: groupsRef.current.get(stepsKey(parsed.steps.slice(0, result.pending.prefixLength + 1))),
|
|
123
|
+
})),
|
|
124
|
+
});
|
|
125
|
+
event.preventDefault();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
setSequenceState(null);
|
|
104
129
|
}
|
|
105
130
|
// Check single-step matches
|
|
106
131
|
for (const { command, parsed } of singleStepCandidates) {
|
|
107
132
|
if (matchStep(event, parsed.steps[0])) {
|
|
108
133
|
event.preventDefault();
|
|
134
|
+
setSequenceState(null);
|
|
109
135
|
command.handler(ctx);
|
|
110
136
|
return;
|
|
111
137
|
}
|
|
112
138
|
}
|
|
113
139
|
});
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
trackerRef.current.reset();
|
|
142
|
+
setSequenceState(null);
|
|
143
|
+
}, [mode]);
|
|
114
144
|
const ctxValue = useMemo(() => ({
|
|
115
145
|
registry: registryRef.current,
|
|
116
146
|
leaderKey: leader,
|
|
117
147
|
contextSources: contextSourcesRef.current,
|
|
148
|
+
groups: groupsRef.current,
|
|
118
149
|
}), [leader]);
|
|
119
|
-
return _jsx(CommandContext.Provider, { value: ctxValue, children: children });
|
|
150
|
+
return (_jsx(CommandContext.Provider, { value: ctxValue, children: _jsx(CommandSequenceContext, { value: sequenceState, children: children }) }));
|
|
120
151
|
}
|
|
121
152
|
export function useCommandContext() {
|
|
122
153
|
const ctx = useContext(CommandContext);
|
|
@@ -138,6 +169,54 @@ export function useCommandRegistry() {
|
|
|
138
169
|
}
|
|
139
170
|
return ctx;
|
|
140
171
|
}
|
|
172
|
+
export function useCommandSequenceState() {
|
|
173
|
+
return useContext(CommandSequenceContext);
|
|
174
|
+
}
|
|
175
|
+
export function useCommandGroup(group) {
|
|
176
|
+
const ctx = useContext(CommandContext);
|
|
177
|
+
if (!ctx) {
|
|
178
|
+
throw new Error("useCommandGroup must be used within a CommandProvider");
|
|
179
|
+
}
|
|
180
|
+
const groupRef = useRef(group);
|
|
181
|
+
groupRef.current = group;
|
|
182
|
+
const { groups, leaderKey } = ctx;
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
const parsed = parseHotkey(groupRef.current.prefix, leaderKey);
|
|
185
|
+
const registered = {
|
|
186
|
+
...groupRef.current,
|
|
187
|
+
prefixKey: stepsKey(parsed.steps),
|
|
188
|
+
};
|
|
189
|
+
groups.set(registered.prefixKey, registered);
|
|
190
|
+
return () => {
|
|
191
|
+
groups.delete(registered.prefixKey);
|
|
192
|
+
};
|
|
193
|
+
}, [
|
|
194
|
+
group.id,
|
|
195
|
+
group.prefix,
|
|
196
|
+
group.title,
|
|
197
|
+
group.description,
|
|
198
|
+
group.icon,
|
|
199
|
+
group.order,
|
|
200
|
+
groups,
|
|
201
|
+
leaderKey,
|
|
202
|
+
]);
|
|
203
|
+
}
|
|
204
|
+
function stepsKey(steps) {
|
|
205
|
+
return steps.map(formatStepKey).join(" ");
|
|
206
|
+
}
|
|
207
|
+
function formatStepKey(step) {
|
|
208
|
+
const modifiers = [];
|
|
209
|
+
if (step.ctrl)
|
|
210
|
+
modifiers.push("ctrl");
|
|
211
|
+
if (step.meta)
|
|
212
|
+
modifiers.push("meta");
|
|
213
|
+
if (step.option)
|
|
214
|
+
modifiers.push("option");
|
|
215
|
+
if (step.shift)
|
|
216
|
+
modifiers.push("shift");
|
|
217
|
+
modifiers.push(step.key);
|
|
218
|
+
return modifiers.join("+");
|
|
219
|
+
}
|
|
141
220
|
let nextContextSourceId = 0;
|
|
142
221
|
export function useProvideCommandContext(getter) {
|
|
143
222
|
const ctx = useContext(CommandContext);
|
package/dist/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,aAAa,EACb,UAAU,EACV,MAAM,EACN,WAAW,EACX,SAAS,EACT,OAAO,
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export type { Command, CommandContext, CommandContextBase, CommandHandler, CommandWhen, ParsedHotkey, ParsedStep, } from "./types.js";
|
|
1
|
+
export type { Command, CommandContext, CommandContextBase, CommandGroup, CommandHandler, CommandSequenceCandidate, CommandSequenceState, CommandWhen, ParsedHotkey, ParsedStep, RegisteredCommandGroup, } from "./types.js";
|
|
2
2
|
export type { Mode } from "./mode.js";
|
|
3
3
|
export { ModeProvider, useMode, useSetMode } from "./mode.js";
|
|
4
4
|
export type { ModeProviderProps } from "./mode.js";
|
|
5
5
|
export { parseHotkey } from "./parse.js";
|
|
6
6
|
export { matchStep } from "./match.js";
|
|
7
7
|
export { SequenceTracker } from "./sequence.js";
|
|
8
|
-
export type { SequenceTrackerOptions } from "./sequence.js";
|
|
9
|
-
export { CommandProvider, useCommandContext, useProvideCommandContext } from "./context.js";
|
|
8
|
+
export type { SequenceFeedResult, SequencePendingMatch, SequenceTrackerOptions, } from "./sequence.js";
|
|
9
|
+
export { CommandProvider, useCommandContext, useCommandGroup, useCommandRegistry, useCommandSequenceState, useProvideCommandContext, } from "./context.js";
|
|
10
10
|
export type { CommandProviderProps } from "./context.js";
|
|
11
11
|
export { useCommand } from "./use-command.js";
|
|
12
12
|
export type { UseCommandOptions } from "./use-command.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,OAAO,EACP,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,YAAY,EACZ,UAAU,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,OAAO,EACP,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,wBAAwB,EACxB,oBAAoB,EACpB,WAAW,EACX,YAAY,EACZ,UAAU,EACV,sBAAsB,GACvB,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC7D,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,YAAY,EACV,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ export { ModeProvider, useMode, useSetMode } from "./mode.js";
|
|
|
2
2
|
export { parseHotkey } from "./parse.js";
|
|
3
3
|
export { matchStep } from "./match.js";
|
|
4
4
|
export { SequenceTracker } from "./sequence.js";
|
|
5
|
-
export { CommandProvider, useCommandContext, useProvideCommandContext } from "./context.js";
|
|
5
|
+
export { CommandProvider, useCommandContext, useCommandGroup, useCommandRegistry, useCommandSequenceState, useProvideCommandContext, } from "./context.js";
|
|
6
6
|
export { useCommand } from "./use-command.js";
|
|
7
7
|
export { useActions } from "./use-actions.js";
|
|
8
8
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE7D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAM/C,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA"}
|
package/dist/sequence.d.ts
CHANGED
|
@@ -2,18 +2,34 @@ import type { KeyEvent } from "@opentui/core";
|
|
|
2
2
|
import type { ParsedHotkey } from "./types.js";
|
|
3
3
|
export interface SequenceTrackerOptions {
|
|
4
4
|
timeout?: number;
|
|
5
|
+
onReset?: () => void;
|
|
6
|
+
}
|
|
7
|
+
export interface SequencePendingMatch {
|
|
8
|
+
prefixLength: number;
|
|
9
|
+
indexes: number[];
|
|
10
|
+
}
|
|
11
|
+
export interface SequenceFeedResult {
|
|
12
|
+
matchedIndex: number;
|
|
13
|
+
pending: SequencePendingMatch | null;
|
|
5
14
|
}
|
|
6
15
|
export declare class SequenceTracker {
|
|
7
16
|
private buffer;
|
|
8
17
|
private timer;
|
|
9
18
|
private timeout;
|
|
19
|
+
private onReset?;
|
|
10
20
|
constructor(options?: SequenceTrackerOptions);
|
|
11
21
|
/**
|
|
12
22
|
* Feed a key event and check against registered hotkeys.
|
|
13
|
-
* Returns the index of the matched hotkey, or -1 if no match.
|
|
23
|
+
* Returns the index of the matched hotkey, or -1 if no full match.
|
|
14
24
|
*/
|
|
15
25
|
feed(event: KeyEvent, hotkeys: ParsedHotkey[]): number;
|
|
26
|
+
/**
|
|
27
|
+
* Feed a key event and return both full-match and pending-prefix state.
|
|
28
|
+
*/
|
|
29
|
+
feedWithState(event: KeyEvent, hotkeys: ParsedHotkey[]): SequenceFeedResult;
|
|
30
|
+
getPendingMatch(hotkeys: ParsedHotkey[]): SequencePendingMatch | null;
|
|
16
31
|
private matchesBuffer;
|
|
32
|
+
private findPendingMatch;
|
|
17
33
|
reset(): void;
|
|
18
34
|
private resetTimer;
|
|
19
35
|
private clearTimer;
|
package/dist/sequence.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sequence.d.ts","sourceRoot":"","sources":["../src/sequence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAG9C,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"sequence.d.ts","sourceRoot":"","sources":["../src/sequence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAG9C,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAAA;CACrC;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,OAAO,CAAC,CAAY;gBAEhB,OAAO,CAAC,EAAE,sBAAsB;IAK5C;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM;IAItD;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,kBAAkB;IAuB3E,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,oBAAoB,GAAG,IAAI;IAIrE,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,gBAAgB;IA+BxB,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,UAAU;CAMnB"}
|
package/dist/sequence.js
CHANGED
|
@@ -3,29 +3,42 @@ export class SequenceTracker {
|
|
|
3
3
|
buffer = [];
|
|
4
4
|
timer = null;
|
|
5
5
|
timeout;
|
|
6
|
+
onReset;
|
|
6
7
|
constructor(options) {
|
|
7
8
|
this.timeout = options?.timeout ?? 500;
|
|
9
|
+
this.onReset = options?.onReset;
|
|
8
10
|
}
|
|
9
11
|
/**
|
|
10
12
|
* Feed a key event and check against registered hotkeys.
|
|
11
|
-
* Returns the index of the matched hotkey, or -1 if no match.
|
|
13
|
+
* Returns the index of the matched hotkey, or -1 if no full match.
|
|
12
14
|
*/
|
|
13
15
|
feed(event, hotkeys) {
|
|
16
|
+
return this.feedWithState(event, hotkeys).matchedIndex;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Feed a key event and return both full-match and pending-prefix state.
|
|
20
|
+
*/
|
|
21
|
+
feedWithState(event, hotkeys) {
|
|
14
22
|
this.buffer.push(event);
|
|
15
23
|
this.resetTimer();
|
|
16
24
|
for (let i = 0; i < hotkeys.length; i++) {
|
|
17
25
|
const hotkey = hotkeys[i];
|
|
18
26
|
if (this.matchesBuffer(hotkey)) {
|
|
19
27
|
this.reset();
|
|
20
|
-
return i;
|
|
28
|
+
return { matchedIndex: i, pending: null };
|
|
21
29
|
}
|
|
22
30
|
}
|
|
23
31
|
// Prune buffer if no hotkey could possibly match
|
|
24
|
-
const maxLen = Math.max(...hotkeys.map((h) => h.steps.length));
|
|
25
|
-
if (
|
|
26
|
-
this.buffer.
|
|
32
|
+
const maxLen = Math.max(0, ...hotkeys.map((h) => h.steps.length));
|
|
33
|
+
if (maxLen > 0) {
|
|
34
|
+
while (this.buffer.length > maxLen) {
|
|
35
|
+
this.buffer.shift();
|
|
36
|
+
}
|
|
27
37
|
}
|
|
28
|
-
return -1;
|
|
38
|
+
return { matchedIndex: -1, pending: this.findPendingMatch(hotkeys) };
|
|
39
|
+
}
|
|
40
|
+
getPendingMatch(hotkeys) {
|
|
41
|
+
return this.findPendingMatch(hotkeys);
|
|
29
42
|
}
|
|
30
43
|
matchesBuffer(hotkey) {
|
|
31
44
|
const { steps } = hotkey;
|
|
@@ -38,9 +51,36 @@ export class SequenceTracker {
|
|
|
38
51
|
}
|
|
39
52
|
return true;
|
|
40
53
|
}
|
|
54
|
+
findPendingMatch(hotkeys) {
|
|
55
|
+
const maxPrefixLength = Math.min(this.buffer.length, Math.max(0, ...hotkeys.map((h) => h.steps.length - 1)));
|
|
56
|
+
for (let prefixLength = maxPrefixLength; prefixLength > 0; prefixLength--) {
|
|
57
|
+
const start = this.buffer.length - prefixLength;
|
|
58
|
+
const indexes = [];
|
|
59
|
+
for (let hotkeyIndex = 0; hotkeyIndex < hotkeys.length; hotkeyIndex++) {
|
|
60
|
+
const hotkey = hotkeys[hotkeyIndex];
|
|
61
|
+
if (hotkey.steps.length <= prefixLength)
|
|
62
|
+
continue;
|
|
63
|
+
let matches = true;
|
|
64
|
+
for (let i = 0; i < prefixLength; i++) {
|
|
65
|
+
if (!matchStep(this.buffer[start + i], hotkey.steps[i])) {
|
|
66
|
+
matches = false;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (matches)
|
|
71
|
+
indexes.push(hotkeyIndex);
|
|
72
|
+
}
|
|
73
|
+
if (indexes.length > 0)
|
|
74
|
+
return { prefixLength, indexes };
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
41
78
|
reset() {
|
|
79
|
+
const hadBuffer = this.buffer.length > 0;
|
|
42
80
|
this.buffer = [];
|
|
43
81
|
this.clearTimer();
|
|
82
|
+
if (hadBuffer)
|
|
83
|
+
this.onReset?.();
|
|
44
84
|
}
|
|
45
85
|
resetTimer() {
|
|
46
86
|
this.clearTimer();
|
package/dist/sequence.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sequence.js","sourceRoot":"","sources":["../src/sequence.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"sequence.js","sourceRoot":"","sources":["../src/sequence.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -40,4 +40,27 @@ export interface CommandRegistry {
|
|
|
40
40
|
register: (command: Command) => () => void;
|
|
41
41
|
invoke: (id: string) => void;
|
|
42
42
|
}
|
|
43
|
+
export interface CommandGroup {
|
|
44
|
+
id: string;
|
|
45
|
+
title: string;
|
|
46
|
+
prefix: string;
|
|
47
|
+
description?: string;
|
|
48
|
+
icon?: string;
|
|
49
|
+
order?: number;
|
|
50
|
+
}
|
|
51
|
+
export interface RegisteredCommandGroup extends CommandGroup {
|
|
52
|
+
prefixKey: string;
|
|
53
|
+
}
|
|
54
|
+
export interface CommandSequenceCandidate {
|
|
55
|
+
command: Command;
|
|
56
|
+
hotkey: string;
|
|
57
|
+
steps: ParsedStep[];
|
|
58
|
+
remainingSteps: ParsedStep[];
|
|
59
|
+
nextStep: ParsedStep;
|
|
60
|
+
group?: CommandGroup;
|
|
61
|
+
}
|
|
62
|
+
export interface CommandSequenceState {
|
|
63
|
+
prefix: ParsedStep[];
|
|
64
|
+
candidates: CommandSequenceCandidate[];
|
|
65
|
+
}
|
|
43
66
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,IAAI,CAAA;IACV,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAC7B,QAAQ,EAAE;QAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,OAAO,EAAE,CAAA;KAAE,CAAA;IACjE,IAAI,EAAE,MAAM,IAAI,CAAA;CACjB;AAED,MAAM,WAAW,cAAe,SAAQ,kBAAkB;IACxD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAC1E,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAA;AAE1D,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,cAAc,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAA;IACd,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,EAAE,CAAA;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,OAAO,CAAA;IACb,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,IAAI,CAAA;IAC1C,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAC7B"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,IAAI,CAAA;IACV,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAC7B,QAAQ,EAAE;QAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,OAAO,EAAE,CAAA;KAAE,CAAA;IACjE,IAAI,EAAE,MAAM,IAAI,CAAA;CACjB;AAED,MAAM,WAAW,cAAe,SAAQ,kBAAkB;IACxD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAC1E,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAA;AAE1D,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,cAAc,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAA;IACd,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,EAAE,CAAA;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,OAAO,CAAA;IACb,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,IAAI,CAAA;IAC1C,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,sBAAuB,SAAQ,YAAY;IAC1D,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,cAAc,EAAE,UAAU,EAAE,CAAA;IAC5B,QAAQ,EAAE,UAAU,CAAA;IACpB,KAAK,CAAC,EAAE,YAAY,CAAA;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,UAAU,EAAE,wBAAwB,EAAE,CAAA;CACvC"}
|
package/dist/use-actions.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ export interface ActionDefinition {
|
|
|
7
7
|
modes?: Mode[];
|
|
8
8
|
handler: CommandHandler;
|
|
9
9
|
when?: CommandWhen;
|
|
10
|
+
category?: string;
|
|
11
|
+
group?: string;
|
|
12
|
+
icon?: string;
|
|
13
|
+
hidden?: boolean;
|
|
10
14
|
}
|
|
11
15
|
export declare function useActions(actions: ActionDefinition[] | undefined): void;
|
|
12
16
|
//# sourceMappingURL=use-actions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-actions.d.ts","sourceRoot":"","sources":["../src/use-actions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACtE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,IAAI,EAAE,CAAA;IACd,OAAO,EAAE,cAAc,CAAA;IACvB,IAAI,CAAC,EAAE,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"use-actions.d.ts","sourceRoot":"","sources":["../src/use-actions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACtE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,IAAI,EAAE,CAAA;IACd,OAAO,EAAE,cAAc,CAAA;IACvB,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,SAAS,GAAG,IAAI,CA0CxE"}
|
package/dist/use-actions.js
CHANGED
|
@@ -4,7 +4,9 @@ export function useActions(actions) {
|
|
|
4
4
|
const { registry } = useCommandRegistry();
|
|
5
5
|
const actionsRef = useRef(actions);
|
|
6
6
|
actionsRef.current = actions;
|
|
7
|
-
const key = useMemo(() => actions
|
|
7
|
+
const key = useMemo(() => actions
|
|
8
|
+
?.map((a) => `${a.id}:${a.title}:${a.hotkey ?? ""}:${a.category ?? ""}:${a.group ?? ""}:${a.icon ?? ""}:${a.hidden ?? false}`)
|
|
9
|
+
.join(",") ?? "", [actions]);
|
|
8
10
|
useEffect(() => {
|
|
9
11
|
const current = actionsRef.current;
|
|
10
12
|
if (!current || current.length === 0)
|
|
@@ -17,6 +19,10 @@ export function useActions(actions) {
|
|
|
17
19
|
modes: action.modes,
|
|
18
20
|
handler: (ctx) => actionsRef.current?.[i]?.handler(ctx),
|
|
19
21
|
when: action.when ? (ctx) => actionsRef.current?.[i]?.when?.(ctx) ?? false : undefined,
|
|
22
|
+
category: action.category,
|
|
23
|
+
group: action.group,
|
|
24
|
+
icon: action.icon,
|
|
25
|
+
hidden: action.hidden,
|
|
20
26
|
};
|
|
21
27
|
return registry.register(command);
|
|
22
28
|
});
|
package/dist/use-actions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-actions.js","sourceRoot":"","sources":["../src/use-actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"use-actions.js","sourceRoot":"","sources":["../src/use-actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAiBjD,MAAM,UAAU,UAAU,CAAC,OAAuC;IAChE,MAAM,EAAE,QAAQ,EAAE,GAAG,kBAAkB,EAAE,CAAA;IACzC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;IAClC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAA;IAE5B,MAAM,GAAG,GAAG,OAAO,CACjB,GAAG,EAAE,CACH,OAAO;QACL,EAAE,GAAG,CACH,CAAC,CAAC,EAAE,EAAE,CACJ,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,IAAI,EAAE,IAAI,CAAC,CAAC,QAAQ,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,IAAI,CAAC,CAAC,MAAM,IAAI,KAAK,EAAE,CACnH;SACA,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EACpB,CAAC,OAAO,CAAC,CACV,CAAA;IAED,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAA;QAClC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAM;QAE5C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YAC5C,MAAM,OAAO,GAAY;gBACvB,EAAE,EAAE,MAAM,CAAC,EAAE;gBACb,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,aAAa,EAAE,MAAM,CAAC,MAAM;gBAC5B,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC;gBACvD,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,SAAS;gBACtF,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,MAAM,EAAE,MAAM,CAAC,MAAM;aACtB,CAAA;YACD,OAAO,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;QACnC,CAAC,CAAC,CAAA;QAEF,OAAO,GAAG,EAAE;YACV,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;gBACrC,UAAU,EAAE,CAAA;YACd,CAAC;QACH,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAA;AACrB,CAAC"}
|
package/package.json
CHANGED
package/src/context.tsx
CHANGED
|
@@ -5,10 +5,20 @@ import {
|
|
|
5
5
|
useCallback,
|
|
6
6
|
useEffect,
|
|
7
7
|
useMemo,
|
|
8
|
+
useState,
|
|
8
9
|
type ReactNode,
|
|
9
10
|
} from "react"
|
|
10
11
|
import { useKeyboard } from "@opentui/react"
|
|
11
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
Command,
|
|
14
|
+
CommandContext,
|
|
15
|
+
CommandGroup,
|
|
16
|
+
CommandRegistry,
|
|
17
|
+
CommandSequenceState,
|
|
18
|
+
ParsedHotkey,
|
|
19
|
+
ParsedStep,
|
|
20
|
+
RegisteredCommandGroup,
|
|
21
|
+
} from "./types.js"
|
|
12
22
|
import type { Mode } from "./mode.js"
|
|
13
23
|
import { ModeProvider, useMode, useSetMode } from "./mode.js"
|
|
14
24
|
import { parseHotkey } from "./parse.js"
|
|
@@ -23,9 +33,11 @@ interface CommandContextValue {
|
|
|
23
33
|
registry: CommandRegistry
|
|
24
34
|
leaderKey?: string
|
|
25
35
|
contextSources: Map<string, ContextGetter>
|
|
36
|
+
groups: Map<string, RegisteredCommandGroup>
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
const CommandContext = createContext<CommandContextValue | null>(null)
|
|
40
|
+
const CommandSequenceContext = createContext<CommandSequenceState | null>(null)
|
|
29
41
|
|
|
30
42
|
export interface CommandProviderProps {
|
|
31
43
|
children: ReactNode
|
|
@@ -55,10 +67,14 @@ function CommandDispatcher({
|
|
|
55
67
|
}) {
|
|
56
68
|
const registryRef = useRef<CommandRegistry | null>(null)
|
|
57
69
|
const contextSourcesRef = useRef(new Map<string, ContextGetter>())
|
|
70
|
+
const groupsRef = useRef(new Map<string, RegisteredCommandGroup>())
|
|
58
71
|
const mode = useMode()
|
|
59
72
|
const modeRef = useRef(mode)
|
|
60
73
|
modeRef.current = mode
|
|
61
74
|
const setMode = useSetMode()
|
|
75
|
+
const [sequenceState, setSequenceState] = useState<CommandSequenceState | null>(null)
|
|
76
|
+
const clearSequenceStateRef = useRef(() => setSequenceState(null))
|
|
77
|
+
clearSequenceStateRef.current = () => setSequenceState(null)
|
|
62
78
|
|
|
63
79
|
const buildCtx = useCallback((): CommandContext => {
|
|
64
80
|
const ctx: Record<string, any> = {
|
|
@@ -96,7 +112,7 @@ function CommandDispatcher({
|
|
|
96
112
|
}
|
|
97
113
|
}
|
|
98
114
|
|
|
99
|
-
const trackerRef = useRef(new SequenceTracker())
|
|
115
|
+
const trackerRef = useRef(new SequenceTracker({ onReset: () => clearSequenceStateRef.current() }))
|
|
100
116
|
const parseCacheRef = useRef(new Map<string, ParsedHotkey>())
|
|
101
117
|
|
|
102
118
|
const getParsedHotkey = useCallback(
|
|
@@ -124,8 +140,7 @@ function CommandDispatcher({
|
|
|
124
140
|
|
|
125
141
|
// Collect eligible commands with their parsed hotkeys
|
|
126
142
|
const singleStepCandidates: { command: Command; parsed: ParsedHotkey }[] = []
|
|
127
|
-
const
|
|
128
|
-
const multiStepCommands: Command[] = []
|
|
143
|
+
const multiStepCandidates: { command: Command; hotkey: string; parsed: ParsedHotkey }[] = []
|
|
129
144
|
|
|
130
145
|
for (const command of registry.commands.values()) {
|
|
131
146
|
const commandModes = command.modes ?? DEFAULT_MODES
|
|
@@ -140,41 +155,77 @@ function CommandDispatcher({
|
|
|
140
155
|
if (parsed.steps.length === 1) {
|
|
141
156
|
singleStepCandidates.push({ command, parsed })
|
|
142
157
|
} else {
|
|
143
|
-
|
|
144
|
-
multiStepCommands.push(command)
|
|
158
|
+
multiStepCandidates.push({ command, hotkey, parsed })
|
|
145
159
|
}
|
|
146
160
|
}
|
|
147
161
|
|
|
148
162
|
// Check multi-step sequences first (they consume buffer state)
|
|
149
|
-
if (
|
|
150
|
-
const
|
|
151
|
-
|
|
163
|
+
if (multiStepCandidates.length > 0) {
|
|
164
|
+
const multiStepHotkeys = multiStepCandidates.map((candidate) => candidate.parsed)
|
|
165
|
+
const result = trackerRef.current.feedWithState(event, multiStepHotkeys)
|
|
166
|
+
if (result.matchedIndex >= 0) {
|
|
152
167
|
event.preventDefault()
|
|
153
|
-
|
|
168
|
+
setSequenceState(null)
|
|
169
|
+
multiStepCandidates[result.matchedIndex]!.command.handler(ctx)
|
|
154
170
|
return
|
|
155
171
|
}
|
|
172
|
+
|
|
173
|
+
if (result.pending) {
|
|
174
|
+
const firstCandidate = multiStepCandidates[result.pending.indexes[0]!]!
|
|
175
|
+
setSequenceState({
|
|
176
|
+
prefix: firstCandidate.parsed.steps.slice(0, result.pending.prefixLength),
|
|
177
|
+
candidates: result.pending.indexes
|
|
178
|
+
.map((idx) => multiStepCandidates[idx]!)
|
|
179
|
+
.filter(({ command }) => !command.hidden)
|
|
180
|
+
.map(({ command, hotkey, parsed }) => ({
|
|
181
|
+
command,
|
|
182
|
+
hotkey,
|
|
183
|
+
steps: parsed.steps,
|
|
184
|
+
remainingSteps: parsed.steps.slice(result.pending!.prefixLength),
|
|
185
|
+
nextStep: parsed.steps[result.pending!.prefixLength]!,
|
|
186
|
+
group: groupsRef.current.get(
|
|
187
|
+
stepsKey(parsed.steps.slice(0, result.pending!.prefixLength + 1)),
|
|
188
|
+
),
|
|
189
|
+
})),
|
|
190
|
+
})
|
|
191
|
+
event.preventDefault()
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
setSequenceState(null)
|
|
156
196
|
}
|
|
157
197
|
|
|
158
198
|
// Check single-step matches
|
|
159
199
|
for (const { command, parsed } of singleStepCandidates) {
|
|
160
200
|
if (matchStep(event, parsed.steps[0]!)) {
|
|
161
201
|
event.preventDefault()
|
|
202
|
+
setSequenceState(null)
|
|
162
203
|
command.handler(ctx)
|
|
163
204
|
return
|
|
164
205
|
}
|
|
165
206
|
}
|
|
166
207
|
})
|
|
167
208
|
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
trackerRef.current.reset()
|
|
211
|
+
setSequenceState(null)
|
|
212
|
+
}, [mode])
|
|
213
|
+
|
|
168
214
|
const ctxValue = useMemo(
|
|
169
215
|
() => ({
|
|
170
216
|
registry: registryRef.current!,
|
|
171
217
|
leaderKey: leader,
|
|
172
218
|
contextSources: contextSourcesRef.current,
|
|
219
|
+
groups: groupsRef.current,
|
|
173
220
|
}),
|
|
174
221
|
[leader],
|
|
175
222
|
)
|
|
176
223
|
|
|
177
|
-
return
|
|
224
|
+
return (
|
|
225
|
+
<CommandContext.Provider value={ctxValue}>
|
|
226
|
+
<CommandSequenceContext value={sequenceState}>{children}</CommandSequenceContext>
|
|
227
|
+
</CommandContext.Provider>
|
|
228
|
+
)
|
|
178
229
|
}
|
|
179
230
|
|
|
180
231
|
export function useCommandContext(): { commands: Command[]; invoke: (id: string) => void } {
|
|
@@ -200,6 +251,56 @@ export function useCommandRegistry(): CommandContextValue {
|
|
|
200
251
|
return ctx
|
|
201
252
|
}
|
|
202
253
|
|
|
254
|
+
export function useCommandSequenceState(): CommandSequenceState | null {
|
|
255
|
+
return useContext(CommandSequenceContext)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function useCommandGroup(group: CommandGroup): void {
|
|
259
|
+
const ctx = useContext(CommandContext)
|
|
260
|
+
if (!ctx) {
|
|
261
|
+
throw new Error("useCommandGroup must be used within a CommandProvider")
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const groupRef = useRef(group)
|
|
265
|
+
groupRef.current = group
|
|
266
|
+
const { groups, leaderKey } = ctx
|
|
267
|
+
|
|
268
|
+
useEffect(() => {
|
|
269
|
+
const parsed = parseHotkey(groupRef.current.prefix, leaderKey)
|
|
270
|
+
const registered: RegisteredCommandGroup = {
|
|
271
|
+
...groupRef.current,
|
|
272
|
+
prefixKey: stepsKey(parsed.steps),
|
|
273
|
+
}
|
|
274
|
+
groups.set(registered.prefixKey, registered)
|
|
275
|
+
return () => {
|
|
276
|
+
groups.delete(registered.prefixKey)
|
|
277
|
+
}
|
|
278
|
+
}, [
|
|
279
|
+
group.id,
|
|
280
|
+
group.prefix,
|
|
281
|
+
group.title,
|
|
282
|
+
group.description,
|
|
283
|
+
group.icon,
|
|
284
|
+
group.order,
|
|
285
|
+
groups,
|
|
286
|
+
leaderKey,
|
|
287
|
+
])
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function stepsKey(steps: ParsedStep[]): string {
|
|
291
|
+
return steps.map(formatStepKey).join(" ")
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function formatStepKey(step: ParsedStep): string {
|
|
295
|
+
const modifiers = []
|
|
296
|
+
if (step.ctrl) modifiers.push("ctrl")
|
|
297
|
+
if (step.meta) modifiers.push("meta")
|
|
298
|
+
if (step.option) modifiers.push("option")
|
|
299
|
+
if (step.shift) modifiers.push("shift")
|
|
300
|
+
modifiers.push(step.key)
|
|
301
|
+
return modifiers.join("+")
|
|
302
|
+
}
|
|
303
|
+
|
|
203
304
|
let nextContextSourceId = 0
|
|
204
305
|
|
|
205
306
|
export function useProvideCommandContext(getter: () => Partial<CommandContext>): void {
|
package/src/index.ts
CHANGED
|
@@ -2,10 +2,14 @@ export type {
|
|
|
2
2
|
Command,
|
|
3
3
|
CommandContext,
|
|
4
4
|
CommandContextBase,
|
|
5
|
+
CommandGroup,
|
|
5
6
|
CommandHandler,
|
|
7
|
+
CommandSequenceCandidate,
|
|
8
|
+
CommandSequenceState,
|
|
6
9
|
CommandWhen,
|
|
7
10
|
ParsedHotkey,
|
|
8
11
|
ParsedStep,
|
|
12
|
+
RegisteredCommandGroup,
|
|
9
13
|
} from "./types.js"
|
|
10
14
|
export type { Mode } from "./mode.js"
|
|
11
15
|
export { ModeProvider, useMode, useSetMode } from "./mode.js"
|
|
@@ -13,8 +17,19 @@ export type { ModeProviderProps } from "./mode.js"
|
|
|
13
17
|
export { parseHotkey } from "./parse.js"
|
|
14
18
|
export { matchStep } from "./match.js"
|
|
15
19
|
export { SequenceTracker } from "./sequence.js"
|
|
16
|
-
export type {
|
|
17
|
-
|
|
20
|
+
export type {
|
|
21
|
+
SequenceFeedResult,
|
|
22
|
+
SequencePendingMatch,
|
|
23
|
+
SequenceTrackerOptions,
|
|
24
|
+
} from "./sequence.js"
|
|
25
|
+
export {
|
|
26
|
+
CommandProvider,
|
|
27
|
+
useCommandContext,
|
|
28
|
+
useCommandGroup,
|
|
29
|
+
useCommandRegistry,
|
|
30
|
+
useCommandSequenceState,
|
|
31
|
+
useProvideCommandContext,
|
|
32
|
+
} from "./context.js"
|
|
18
33
|
export type { CommandProviderProps } from "./context.js"
|
|
19
34
|
export { useCommand } from "./use-command.js"
|
|
20
35
|
export type { UseCommandOptions } from "./use-command.js"
|
package/src/sequence.ts
CHANGED
|
@@ -4,22 +4,42 @@ import { matchStep } from "./match.js"
|
|
|
4
4
|
|
|
5
5
|
export interface SequenceTrackerOptions {
|
|
6
6
|
timeout?: number // ms, default 500
|
|
7
|
+
onReset?: () => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SequencePendingMatch {
|
|
11
|
+
prefixLength: number
|
|
12
|
+
indexes: number[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface SequenceFeedResult {
|
|
16
|
+
matchedIndex: number
|
|
17
|
+
pending: SequencePendingMatch | null
|
|
7
18
|
}
|
|
8
19
|
|
|
9
20
|
export class SequenceTracker {
|
|
10
21
|
private buffer: KeyEvent[] = []
|
|
11
22
|
private timer: ReturnType<typeof setTimeout> | null = null
|
|
12
23
|
private timeout: number
|
|
24
|
+
private onReset?: () => void
|
|
13
25
|
|
|
14
26
|
constructor(options?: SequenceTrackerOptions) {
|
|
15
27
|
this.timeout = options?.timeout ?? 500
|
|
28
|
+
this.onReset = options?.onReset
|
|
16
29
|
}
|
|
17
30
|
|
|
18
31
|
/**
|
|
19
32
|
* Feed a key event and check against registered hotkeys.
|
|
20
|
-
* Returns the index of the matched hotkey, or -1 if no match.
|
|
33
|
+
* Returns the index of the matched hotkey, or -1 if no full match.
|
|
21
34
|
*/
|
|
22
35
|
feed(event: KeyEvent, hotkeys: ParsedHotkey[]): number {
|
|
36
|
+
return this.feedWithState(event, hotkeys).matchedIndex
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Feed a key event and return both full-match and pending-prefix state.
|
|
41
|
+
*/
|
|
42
|
+
feedWithState(event: KeyEvent, hotkeys: ParsedHotkey[]): SequenceFeedResult {
|
|
23
43
|
this.buffer.push(event)
|
|
24
44
|
this.resetTimer()
|
|
25
45
|
|
|
@@ -27,17 +47,23 @@ export class SequenceTracker {
|
|
|
27
47
|
const hotkey = hotkeys[i]!
|
|
28
48
|
if (this.matchesBuffer(hotkey)) {
|
|
29
49
|
this.reset()
|
|
30
|
-
return i
|
|
50
|
+
return { matchedIndex: i, pending: null }
|
|
31
51
|
}
|
|
32
52
|
}
|
|
33
53
|
|
|
34
54
|
// Prune buffer if no hotkey could possibly match
|
|
35
|
-
const maxLen = Math.max(...hotkeys.map((h) => h.steps.length))
|
|
36
|
-
if (
|
|
37
|
-
this.buffer.
|
|
55
|
+
const maxLen = Math.max(0, ...hotkeys.map((h) => h.steps.length))
|
|
56
|
+
if (maxLen > 0) {
|
|
57
|
+
while (this.buffer.length > maxLen) {
|
|
58
|
+
this.buffer.shift()
|
|
59
|
+
}
|
|
38
60
|
}
|
|
39
61
|
|
|
40
|
-
return -1
|
|
62
|
+
return { matchedIndex: -1, pending: this.findPendingMatch(hotkeys) }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
getPendingMatch(hotkeys: ParsedHotkey[]): SequencePendingMatch | null {
|
|
66
|
+
return this.findPendingMatch(hotkeys)
|
|
41
67
|
}
|
|
42
68
|
|
|
43
69
|
private matchesBuffer(hotkey: ParsedHotkey): boolean {
|
|
@@ -51,9 +77,42 @@ export class SequenceTracker {
|
|
|
51
77
|
return true
|
|
52
78
|
}
|
|
53
79
|
|
|
80
|
+
private findPendingMatch(hotkeys: ParsedHotkey[]): SequencePendingMatch | null {
|
|
81
|
+
const maxPrefixLength = Math.min(
|
|
82
|
+
this.buffer.length,
|
|
83
|
+
Math.max(0, ...hotkeys.map((h) => h.steps.length - 1)),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
for (let prefixLength = maxPrefixLength; prefixLength > 0; prefixLength--) {
|
|
87
|
+
const start = this.buffer.length - prefixLength
|
|
88
|
+
const indexes: number[] = []
|
|
89
|
+
|
|
90
|
+
for (let hotkeyIndex = 0; hotkeyIndex < hotkeys.length; hotkeyIndex++) {
|
|
91
|
+
const hotkey = hotkeys[hotkeyIndex]!
|
|
92
|
+
if (hotkey.steps.length <= prefixLength) continue
|
|
93
|
+
|
|
94
|
+
let matches = true
|
|
95
|
+
for (let i = 0; i < prefixLength; i++) {
|
|
96
|
+
if (!matchStep(this.buffer[start + i]!, hotkey.steps[i]!)) {
|
|
97
|
+
matches = false
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (matches) indexes.push(hotkeyIndex)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (indexes.length > 0) return { prefixLength, indexes }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return null
|
|
109
|
+
}
|
|
110
|
+
|
|
54
111
|
reset(): void {
|
|
112
|
+
const hadBuffer = this.buffer.length > 0
|
|
55
113
|
this.buffer = []
|
|
56
114
|
this.clearTimer()
|
|
115
|
+
if (hadBuffer) this.onReset?.()
|
|
57
116
|
}
|
|
58
117
|
|
|
59
118
|
private resetTimer(): void {
|
package/src/types.ts
CHANGED
|
@@ -44,3 +44,30 @@ export interface CommandRegistry {
|
|
|
44
44
|
register: (command: Command) => () => void
|
|
45
45
|
invoke: (id: string) => void
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
export interface CommandGroup {
|
|
49
|
+
id: string
|
|
50
|
+
title: string
|
|
51
|
+
prefix: string
|
|
52
|
+
description?: string
|
|
53
|
+
icon?: string
|
|
54
|
+
order?: number
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface RegisteredCommandGroup extends CommandGroup {
|
|
58
|
+
prefixKey: string
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface CommandSequenceCandidate {
|
|
62
|
+
command: Command
|
|
63
|
+
hotkey: string
|
|
64
|
+
steps: ParsedStep[]
|
|
65
|
+
remainingSteps: ParsedStep[]
|
|
66
|
+
nextStep: ParsedStep
|
|
67
|
+
group?: CommandGroup
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface CommandSequenceState {
|
|
71
|
+
prefix: ParsedStep[]
|
|
72
|
+
candidates: CommandSequenceCandidate[]
|
|
73
|
+
}
|
package/src/use-actions.ts
CHANGED
|
@@ -10,6 +10,10 @@ export interface ActionDefinition {
|
|
|
10
10
|
modes?: Mode[]
|
|
11
11
|
handler: CommandHandler
|
|
12
12
|
when?: CommandWhen
|
|
13
|
+
category?: string
|
|
14
|
+
group?: string
|
|
15
|
+
icon?: string
|
|
16
|
+
hidden?: boolean
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
export function useActions(actions: ActionDefinition[] | undefined): void {
|
|
@@ -18,7 +22,13 @@ export function useActions(actions: ActionDefinition[] | undefined): void {
|
|
|
18
22
|
actionsRef.current = actions
|
|
19
23
|
|
|
20
24
|
const key = useMemo(
|
|
21
|
-
() =>
|
|
25
|
+
() =>
|
|
26
|
+
actions
|
|
27
|
+
?.map(
|
|
28
|
+
(a) =>
|
|
29
|
+
`${a.id}:${a.title}:${a.hotkey ?? ""}:${a.category ?? ""}:${a.group ?? ""}:${a.icon ?? ""}:${a.hidden ?? false}`,
|
|
30
|
+
)
|
|
31
|
+
.join(",") ?? "",
|
|
22
32
|
[actions],
|
|
23
33
|
)
|
|
24
34
|
|
|
@@ -34,6 +44,10 @@ export function useActions(actions: ActionDefinition[] | undefined): void {
|
|
|
34
44
|
modes: action.modes,
|
|
35
45
|
handler: (ctx) => actionsRef.current?.[i]?.handler(ctx),
|
|
36
46
|
when: action.when ? (ctx) => actionsRef.current?.[i]?.when?.(ctx) ?? false : undefined,
|
|
47
|
+
category: action.category,
|
|
48
|
+
group: action.group,
|
|
49
|
+
icon: action.icon,
|
|
50
|
+
hidden: action.hidden,
|
|
37
51
|
}
|
|
38
52
|
return registry.register(command)
|
|
39
53
|
})
|