@tooee/commands 0.1.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.
Files changed (47) hide show
  1. package/README.md +5 -0
  2. package/dist/context.d.ts +25 -0
  3. package/dist/context.d.ts.map +1 -0
  4. package/dist/context.js +161 -0
  5. package/dist/context.js.map +1 -0
  6. package/dist/index.d.ts +15 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +8 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/match.d.ts +7 -0
  11. package/dist/match.d.ts.map +1 -0
  12. package/dist/match.js +17 -0
  13. package/dist/match.js.map +1 -0
  14. package/dist/mode.d.ts +10 -0
  15. package/dist/mode.d.ts.map +1 -0
  16. package/dist/mode.js +23 -0
  17. package/dist/mode.js.map +1 -0
  18. package/dist/parse.d.ts +11 -0
  19. package/dist/parse.d.ts.map +1 -0
  20. package/dist/parse.js +68 -0
  21. package/dist/parse.js.map +1 -0
  22. package/dist/sequence.d.ts +21 -0
  23. package/dist/sequence.d.ts.map +1 -0
  24. package/dist/sequence.js +56 -0
  25. package/dist/sequence.js.map +1 -0
  26. package/dist/types.d.ts +43 -0
  27. package/dist/types.d.ts.map +1 -0
  28. package/dist/types.js +2 -0
  29. package/dist/types.js.map +1 -0
  30. package/dist/use-actions.d.ts +12 -0
  31. package/dist/use-actions.d.ts.map +1 -0
  32. package/dist/use-actions.js +30 -0
  33. package/dist/use-actions.js.map +1 -0
  34. package/dist/use-command.d.ts +16 -0
  35. package/dist/use-command.d.ts.map +1 -0
  36. package/dist/use-command.js +33 -0
  37. package/dist/use-command.js.map +1 -0
  38. package/package.json +40 -0
  39. package/src/context.tsx +221 -0
  40. package/src/index.ts +22 -0
  41. package/src/match.ts +14 -0
  42. package/src/mode.tsx +38 -0
  43. package/src/parse.ts +71 -0
  44. package/src/sequence.ts +70 -0
  45. package/src/types.ts +46 -0
  46. package/src/use-actions.ts +47 -0
  47. package/src/use-command.ts +49 -0
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # @tooee/commands
2
+
3
+ Vim-inspired modal command system for Tooee.
4
+
5
+ Part of the [Tooee](https://github.com/gingerhendrix/tooee) monorepo. See the main repo for documentation.
@@ -0,0 +1,25 @@
1
+ import { type ReactNode } from "react";
2
+ import type { Command, CommandContext, CommandRegistry } from "./types.js";
3
+ import type { Mode } from "./mode.jsx";
4
+ type ContextGetter = () => Partial<CommandContext>;
5
+ interface CommandContextValue {
6
+ registry: CommandRegistry;
7
+ leaderKey?: string;
8
+ contextSources: Map<string, ContextGetter>;
9
+ }
10
+ declare const CommandContext: import("react").Context<CommandContextValue | null>;
11
+ export interface CommandProviderProps {
12
+ children: ReactNode;
13
+ leader?: string;
14
+ keymap?: Record<string, string>;
15
+ initialMode?: Mode;
16
+ }
17
+ export declare function CommandProvider({ children, leader, keymap, initialMode }: CommandProviderProps): ReactNode;
18
+ export declare function useCommandContext(): {
19
+ commands: Command[];
20
+ invoke: (id: string) => void;
21
+ };
22
+ export declare function useCommandRegistry(): CommandContextValue;
23
+ export declare function useProvideCommandContext(getter: () => Partial<CommandContext>): void;
24
+ export {};
25
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6D,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjG,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAgB,MAAM,YAAY,CAAA;AACxF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAQtC,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;CAC3C;AAED,QAAA,MAAM,cAAc,qDAAkD,CAAA;AAEtE,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;AAwID,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;AAID,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,CAuBpF"}
@@ -0,0 +1,161 @@
1
+ import { jsx as _jsx } from "@opentui/react/jsx-runtime";
2
+ import { createContext, useContext, useRef, useCallback, useEffect } from "react";
3
+ import { useKeyboard } from "@opentui/react";
4
+ import { ModeProvider, useMode, useSetMode } from "./mode.jsx";
5
+ import { parseHotkey } from "./parse.js";
6
+ import { matchStep } from "./match.js";
7
+ import { SequenceTracker } from "./sequence.js";
8
+ const DEFAULT_MODES = ["cursor"];
9
+ const CommandContext = createContext(null);
10
+ export function CommandProvider({ children, leader, keymap, initialMode }) {
11
+ return (_jsx(ModeProvider, { initialMode: initialMode, children: _jsx(CommandDispatcher, { leader: leader, keymap: keymap, children: children }) }));
12
+ }
13
+ function CommandDispatcher({ children, leader, keymap, }) {
14
+ const registryRef = useRef(null);
15
+ const contextSourcesRef = useRef(new Map());
16
+ const mode = useMode();
17
+ const modeRef = useRef(mode);
18
+ modeRef.current = mode;
19
+ const setMode = useSetMode();
20
+ const buildCtx = useCallback(() => {
21
+ const ctx = {
22
+ mode: modeRef.current,
23
+ setMode,
24
+ commands: {
25
+ invoke: (id) => registryRef.current?.invoke(id),
26
+ list: () => Array.from(registryRef.current?.commands.values() ?? []),
27
+ },
28
+ exit: () => { },
29
+ };
30
+ for (const getter of contextSourcesRef.current.values()) {
31
+ Object.assign(ctx, getter());
32
+ }
33
+ return ctx;
34
+ }, [setMode]);
35
+ if (registryRef.current === null) {
36
+ const commands = new Map();
37
+ registryRef.current = {
38
+ commands,
39
+ register(command) {
40
+ commands.set(command.id, command);
41
+ return () => {
42
+ commands.delete(command.id);
43
+ };
44
+ },
45
+ invoke(id) {
46
+ const ctx = buildCtx();
47
+ const cmd = commands.get(id);
48
+ if (cmd && (!cmd.when || cmd.when(ctx))) {
49
+ cmd.handler(ctx);
50
+ }
51
+ },
52
+ };
53
+ }
54
+ const trackerRef = useRef(new SequenceTracker());
55
+ const parseCacheRef = useRef(new Map());
56
+ const getParsedHotkey = useCallback((hotkey) => {
57
+ const cache = parseCacheRef.current;
58
+ const cacheKey = `${hotkey}:${leader ?? ""}`;
59
+ let parsed = cache.get(cacheKey);
60
+ if (!parsed) {
61
+ parsed = parseHotkey(hotkey, leader);
62
+ cache.set(cacheKey, parsed);
63
+ }
64
+ return parsed;
65
+ }, [leader]);
66
+ useKeyboard((event) => {
67
+ if (event.defaultPrevented)
68
+ return;
69
+ const registry = registryRef.current;
70
+ if (!registry)
71
+ return;
72
+ const currentMode = mode;
73
+ const ctx = buildCtx();
74
+ // Collect eligible commands with their parsed hotkeys
75
+ const singleStepCandidates = [];
76
+ const multiStepHotkeys = [];
77
+ const multiStepCommands = [];
78
+ for (const command of registry.commands.values()) {
79
+ const commandModes = command.modes ?? DEFAULT_MODES;
80
+ if (!commandModes.includes(currentMode))
81
+ continue;
82
+ if (command.when && !command.when(ctx))
83
+ continue;
84
+ const hotkey = keymap?.[command.id] ?? command.defaultHotkey;
85
+ if (!hotkey)
86
+ continue;
87
+ const parsed = getParsedHotkey(hotkey);
88
+ if (parsed.steps.length === 1) {
89
+ singleStepCandidates.push({ command, parsed });
90
+ }
91
+ else {
92
+ multiStepHotkeys.push(parsed);
93
+ multiStepCommands.push(command);
94
+ }
95
+ }
96
+ // Check multi-step sequences first (they consume buffer state)
97
+ if (multiStepHotkeys.length > 0) {
98
+ const idx = trackerRef.current.feed(event, multiStepHotkeys);
99
+ if (idx >= 0) {
100
+ event.preventDefault();
101
+ multiStepCommands[idx].handler(ctx);
102
+ return;
103
+ }
104
+ }
105
+ // Check single-step matches
106
+ for (const { command, parsed } of singleStepCandidates) {
107
+ if (matchStep(event, parsed.steps[0])) {
108
+ event.preventDefault();
109
+ command.handler(ctx);
110
+ return;
111
+ }
112
+ }
113
+ });
114
+ return (_jsx(CommandContext.Provider, { value: {
115
+ registry: registryRef.current,
116
+ leaderKey: leader,
117
+ contextSources: contextSourcesRef.current,
118
+ }, children: children }));
119
+ }
120
+ export function useCommandContext() {
121
+ const ctx = useContext(CommandContext);
122
+ if (!ctx) {
123
+ throw new Error("useCommandContext must be used within a CommandProvider");
124
+ }
125
+ const { registry } = ctx;
126
+ return {
127
+ get commands() {
128
+ return Array.from(registry.commands.values());
129
+ },
130
+ invoke: registry.invoke,
131
+ };
132
+ }
133
+ export function useCommandRegistry() {
134
+ const ctx = useContext(CommandContext);
135
+ if (!ctx) {
136
+ throw new Error("useCommandRegistry must be used within a CommandProvider");
137
+ }
138
+ return ctx;
139
+ }
140
+ let nextContextSourceId = 0;
141
+ export function useProvideCommandContext(getter) {
142
+ const ctx = useContext(CommandContext);
143
+ if (!ctx) {
144
+ throw new Error("useProvideCommandContext must be used within a CommandProvider");
145
+ }
146
+ const idRef = useRef(null);
147
+ if (idRef.current === null) {
148
+ idRef.current = `ctx-${nextContextSourceId++}`;
149
+ }
150
+ const getterRef = useRef(getter);
151
+ getterRef.current = getter;
152
+ const { contextSources } = ctx;
153
+ useEffect(() => {
154
+ const id = idRef.current;
155
+ contextSources.set(id, () => getterRef.current());
156
+ return () => {
157
+ contextSources.delete(id);
158
+ };
159
+ }, [contextSources]);
160
+ }
161
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAkB,MAAM,OAAO,CAAA;AACjG,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAG5C,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C,MAAM,aAAa,GAAW,CAAC,QAAQ,CAAC,CAAA;AAUxC,MAAM,cAAc,GAAG,aAAa,CAA6B,IAAI,CAAC,CAAA;AAStE,MAAM,UAAU,eAAe,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAwB;IAC7F,OAAO,CACL,KAAC,YAAY,IAAC,WAAW,EAAE,WAAW,YACpC,KAAC,iBAAiB,IAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAC9C,QAAQ,GACS,GACP,CAChB,CAAA;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,EACzB,QAAQ,EACR,MAAM,EACN,MAAM,GAKP;IACC,MAAM,WAAW,GAAG,MAAM,CAAyB,IAAI,CAAC,CAAA;IACxD,MAAM,iBAAiB,GAAG,MAAM,CAAC,IAAI,GAAG,EAAyB,CAAC,CAAA;IAClE,MAAM,IAAI,GAAG,OAAO,EAAE,CAAA;IACtB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;IAC5B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAA;IACtB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;IAE5B,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAmB,EAAE;QAChD,MAAM,GAAG,GAAwB;YAC/B,IAAI,EAAE,OAAO,CAAC,OAAO;YACrB,OAAO;YACP,QAAQ,EAAE;gBACR,MAAM,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC;gBACvD,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;aACrE;YACD,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;SACf,CAAA;QACD,KAAK,MAAM,MAAM,IAAI,iBAAiB,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YACxD,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAA;QAC9B,CAAC;QACD,OAAO,GAAqB,CAAA;IAC9B,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;IAEb,IAAI,WAAW,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAA;QAC3C,WAAW,CAAC,OAAO,GAAG;YACpB,QAAQ;YACR,QAAQ,CAAC,OAAgB;gBACvB,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;gBACjC,OAAO,GAAG,EAAE;oBACV,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;gBAC7B,CAAC,CAAA;YACH,CAAC;YACD,MAAM,CAAC,EAAU;gBACf,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAA;gBACtB,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gBAC5B,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;oBACxC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;gBAClB,CAAC;YACH,CAAC;SACF,CAAA;IACH,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,eAAe,EAAE,CAAC,CAAA;IAChD,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,GAAG,EAAwB,CAAC,CAAA;IAE7D,MAAM,eAAe,GAAG,WAAW,CACjC,CAAC,MAAc,EAAE,EAAE;QACjB,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAA;QACnC,MAAM,QAAQ,GAAG,GAAG,MAAM,IAAI,MAAM,IAAI,EAAE,EAAE,CAAA;QAC5C,IAAI,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QAChC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;YACpC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QAC7B,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC,EACD,CAAC,MAAM,CAAC,CACT,CAAA;IAED,WAAW,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,IAAI,KAAK,CAAC,gBAAgB;YAAE,OAAM;QAElC,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAA;QACpC,IAAI,CAAC,QAAQ;YAAE,OAAM;QAErB,MAAM,WAAW,GAAG,IAAI,CAAA;QACxB,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAA;QAEtB,sDAAsD;QACtD,MAAM,oBAAoB,GAAiD,EAAE,CAAA;QAC7E,MAAM,gBAAgB,GAAmB,EAAE,CAAA;QAC3C,MAAM,iBAAiB,GAAc,EAAE,CAAA;QAEvC,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YACjD,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAA;YACnD,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC;gBAAE,SAAQ;YACjD,IAAI,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,SAAQ;YAEhD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,aAAa,CAAA;YAC5D,IAAI,CAAC,MAAM;gBAAE,SAAQ;YAErB,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAAA;YAEtC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC9B,oBAAoB,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;YAChD,CAAC;iBAAM,CAAC;gBACN,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;gBAC7B,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACjC,CAAC;QACH,CAAC;QAED,+DAA+D;QAC/D,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAA;YAC5D,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;gBACb,KAAK,CAAC,cAAc,EAAE,CAAA;gBACtB,iBAAiB,CAAC,GAAG,CAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;gBACpC,OAAM;YACR,CAAC;QACH,CAAC;QAED,4BAA4B;QAC5B,KAAK,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,oBAAoB,EAAE,CAAC;YACvD,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC;gBACvC,KAAK,CAAC,cAAc,EAAE,CAAA;gBACtB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;gBACpB,OAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,CACL,KAAC,cAAc,CAAC,QAAQ,IACtB,KAAK,EAAE;YACL,QAAQ,EAAE,WAAW,CAAC,OAAO;YAC7B,SAAS,EAAE,MAAM;YACjB,cAAc,EAAE,iBAAiB,CAAC,OAAO;SAC1C,YAEA,QAAQ,GACe,CAC3B,CAAA;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,MAAM,GAAG,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IACtC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAA;IAC5E,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAA;IACxB,OAAO;QACL,IAAI,QAAQ;YACV,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;QAC/C,CAAC;QACD,MAAM,EAAE,QAAQ,CAAC,MAAM;KACxB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,MAAM,GAAG,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IACtC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;IAC7E,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,IAAI,mBAAmB,GAAG,CAAC,CAAA;AAE3B,MAAM,UAAU,wBAAwB,CAAC,MAAqC;IAC5E,MAAM,GAAG,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IACtC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAA;IACnF,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAgB,IAAI,CAAC,CAAA;IACzC,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC3B,KAAK,CAAC,OAAO,GAAG,OAAO,mBAAmB,EAAE,EAAE,CAAA;IAChD,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAA;IAChC,SAAS,CAAC,OAAO,GAAG,MAAM,CAAA;IAE1B,MAAM,EAAE,cAAc,EAAE,GAAG,GAAG,CAAA;IAE9B,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,EAAE,GAAG,KAAK,CAAC,OAAQ,CAAA;QACzB,cAAc,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAA;QACjD,OAAO,GAAG,EAAE;YACV,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAC3B,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAA;AACtB,CAAC"}
@@ -0,0 +1,15 @@
1
+ export type { Command, CommandContext, CommandContextBase, CommandHandler, CommandWhen, ParsedHotkey, ParsedStep, } from "./types.js";
2
+ export type { Mode } from "./mode.jsx";
3
+ export { ModeProvider, useMode, useSetMode } from "./mode.jsx";
4
+ export type { ModeProviderProps } from "./mode.jsx";
5
+ export { parseHotkey } from "./parse.js";
6
+ export { matchStep } from "./match.js";
7
+ export { SequenceTracker } from "./sequence.js";
8
+ export type { SequenceTrackerOptions } from "./sequence.js";
9
+ export { CommandProvider, useCommandContext, useProvideCommandContext } from "./context.jsx";
10
+ export type { CommandProviderProps } from "./context.jsx";
11
+ export { useCommand } from "./use-command.js";
12
+ export type { UseCommandOptions } from "./use-command.js";
13
+ export { useActions } from "./use-actions.js";
14
+ export type { ActionDefinition } from "./use-actions.js";
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,GACX,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC9D,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,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,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAA;AAC5F,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACzD,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 ADDED
@@ -0,0 +1,8 @@
1
+ export { ModeProvider, useMode, useSetMode } from "./mode.jsx";
2
+ export { parseHotkey } from "./parse.js";
3
+ export { matchStep } from "./match.js";
4
+ export { SequenceTracker } from "./sequence.js";
5
+ export { CommandProvider, useCommandContext, useProvideCommandContext } from "./context.jsx";
6
+ export { useCommand } from "./use-command.js";
7
+ export { useActions } from "./use-actions.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE9D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAA;AAE5F,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA"}
@@ -0,0 +1,7 @@
1
+ import type { KeyEvent } from "@opentui/core";
2
+ import type { ParsedStep } from "./types.js";
3
+ /**
4
+ * Check if a KeyEvent matches a ParsedStep.
5
+ */
6
+ export declare function matchStep(event: KeyEvent, step: ParsedStep): boolean;
7
+ //# sourceMappingURL=match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match.d.ts","sourceRoot":"","sources":["../src/match.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAOpE"}
package/dist/match.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Check if a KeyEvent matches a ParsedStep.
3
+ */
4
+ export function matchStep(event, step) {
5
+ if (event.name !== step.key)
6
+ return false;
7
+ if (event.ctrl !== step.ctrl)
8
+ return false;
9
+ if (event.meta !== step.meta)
10
+ return false;
11
+ if (event.shift !== step.shift)
12
+ return false;
13
+ if (event.option !== step.option)
14
+ return false;
15
+ return true;
16
+ }
17
+ //# sourceMappingURL=match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match.js","sourceRoot":"","sources":["../src/match.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,KAAe,EAAE,IAAgB;IACzD,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAA;IACzC,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IAC1C,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IAC1C,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IAC5C,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IAC9C,OAAO,IAAI,CAAA;AACb,CAAC"}
package/dist/mode.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { type ReactNode } from "react";
2
+ export type Mode = "cursor" | "insert" | "select";
3
+ export interface ModeProviderProps {
4
+ children: ReactNode;
5
+ initialMode?: Mode;
6
+ }
7
+ export declare function ModeProvider({ children, initialMode }: ModeProviderProps): ReactNode;
8
+ export declare function useMode(): Mode;
9
+ export declare function useSetMode(): (mode: Mode) => void;
10
+ //# sourceMappingURL=mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode.d.ts","sourceRoot":"","sources":["../src/mode.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAExF,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;AASjD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,SAAS,CAAA;IACnB,WAAW,CAAC,EAAE,IAAI,CAAA;CACnB;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,WAAsB,EAAE,EAAE,iBAAiB,aAKnF;AAED,wBAAgB,OAAO,IAAI,IAAI,CAM9B;AAED,wBAAgB,UAAU,IAAI,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAMjD"}
package/dist/mode.js ADDED
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx } from "@opentui/react/jsx-runtime";
2
+ import { createContext, useContext, useState, useCallback } from "react";
3
+ const ModeContext = createContext(null);
4
+ export function ModeProvider({ children, initialMode = "cursor" }) {
5
+ const [mode, setModeState] = useState(initialMode);
6
+ const setMode = useCallback((m) => setModeState(m), []);
7
+ return _jsx(ModeContext.Provider, { value: { mode, setMode }, children: children });
8
+ }
9
+ export function useMode() {
10
+ const ctx = useContext(ModeContext);
11
+ if (!ctx) {
12
+ throw new Error("useMode must be used within a ModeProvider");
13
+ }
14
+ return ctx.mode;
15
+ }
16
+ export function useSetMode() {
17
+ const ctx = useContext(ModeContext);
18
+ if (!ctx) {
19
+ throw new Error("useSetMode must be used within a ModeProvider");
20
+ }
21
+ return ctx.setMode;
22
+ }
23
+ //# sourceMappingURL=mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode.js","sourceRoot":"","sources":["../src/mode.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAkB,MAAM,OAAO,CAAA;AASxF,MAAM,WAAW,GAAG,aAAa,CAA0B,IAAI,CAAC,CAAA;AAOhE,MAAM,UAAU,YAAY,CAAC,EAAE,QAAQ,EAAE,WAAW,GAAG,QAAQ,EAAqB;IAClF,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAO,WAAW,CAAC,CAAA;IACxD,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAO,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IAE7D,OAAO,KAAC,WAAW,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,YAAG,QAAQ,GAAwB,CAAA;AAC1F,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,MAAM,GAAG,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;IACnC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;IAC/D,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAA;AACjB,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,GAAG,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;IACnC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;IAClE,CAAC;IACD,OAAO,GAAG,CAAC,OAAO,CAAA;AACpB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { ParsedHotkey } from "./types.js";
2
+ /**
3
+ * Parse a hotkey string into a ParsedHotkey.
4
+ *
5
+ * Supports:
6
+ * - Modifier combos: "ctrl+s", "ctrl+shift+p"
7
+ * - Sequences (space-separated): "g g", "d d"
8
+ * - Leader prefix: "<leader>n" expands to leaderKey + "n"
9
+ */
10
+ export declare function parseHotkey(hotkey: string, leaderKey?: string): ParsedHotkey;
11
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,YAAY,CAAA;AA6C1D;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,CAiB5E"}
package/dist/parse.js ADDED
@@ -0,0 +1,68 @@
1
+ const KEY_ALIASES = {
2
+ esc: "escape",
3
+ enter: "return",
4
+ cr: "return",
5
+ del: "delete",
6
+ space: " ",
7
+ tab: "tab",
8
+ backspace: "backspace",
9
+ };
10
+ function normalizeKey(key) {
11
+ const lower = key.toLowerCase();
12
+ return KEY_ALIASES[lower] ?? lower;
13
+ }
14
+ function parseStep(step) {
15
+ const parts = step.toLowerCase().split("+");
16
+ let key = "";
17
+ let ctrl = false;
18
+ let meta = false;
19
+ let shift = false;
20
+ let option = false;
21
+ for (const part of parts) {
22
+ const trimmed = part.trim();
23
+ if (trimmed === "ctrl" || trimmed === "control") {
24
+ ctrl = true;
25
+ }
26
+ else if (trimmed === "meta" || trimmed === "alt") {
27
+ meta = true;
28
+ }
29
+ else if (trimmed === "shift") {
30
+ shift = true;
31
+ }
32
+ else if (trimmed === "option") {
33
+ option = true;
34
+ }
35
+ else if (trimmed === "super") {
36
+ // super modifier — not tracked in ParsedStep currently
37
+ }
38
+ else {
39
+ key = normalizeKey(trimmed);
40
+ }
41
+ }
42
+ return { key, ctrl, meta, shift, option };
43
+ }
44
+ /**
45
+ * Parse a hotkey string into a ParsedHotkey.
46
+ *
47
+ * Supports:
48
+ * - Modifier combos: "ctrl+s", "ctrl+shift+p"
49
+ * - Sequences (space-separated): "g g", "d d"
50
+ * - Leader prefix: "<leader>n" expands to leaderKey + "n"
51
+ */
52
+ export function parseHotkey(hotkey, leaderKey) {
53
+ const trimmed = hotkey.trim();
54
+ // Handle leader prefix
55
+ const leaderMatch = trimmed.match(/^<leader>(.+)$/);
56
+ if (leaderMatch) {
57
+ const leaderStep = leaderKey
58
+ ? parseStep(leaderKey)
59
+ : { key: "x", ctrl: true, meta: false, shift: false, option: false };
60
+ const followStep = parseStep(leaderMatch[1]);
61
+ return { steps: [leaderStep, followStep] };
62
+ }
63
+ // Space-separated = sequence
64
+ const parts = trimmed.split(/\s+/);
65
+ const steps = parts.map(parseStep);
66
+ return { steps };
67
+ }
68
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAA2B;IAC1C,GAAG,EAAE,QAAQ;IACb,KAAK,EAAE,QAAQ;IACf,EAAE,EAAE,QAAQ;IACZ,GAAG,EAAE,QAAQ;IACb,KAAK,EAAE,GAAG;IACV,GAAG,EAAE,KAAK;IACV,SAAS,EAAE,WAAW;CACvB,CAAA;AAED,SAAS,YAAY,CAAC,GAAW;IAC/B,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAA;IAC/B,OAAO,WAAW,CAAC,KAAK,CAAC,IAAI,KAAK,CAAA;AACpC,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC3C,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,IAAI,IAAI,GAAG,KAAK,CAAA;IAChB,IAAI,IAAI,GAAG,KAAK,CAAA;IAChB,IAAI,KAAK,GAAG,KAAK,CAAA;IACjB,IAAI,MAAM,GAAG,KAAK,CAAA;IAElB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QAC3B,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAChD,IAAI,GAAG,IAAI,CAAA;QACb,CAAC;aAAM,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;YACnD,IAAI,GAAG,IAAI,CAAA;QACb,CAAC;aAAM,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;YAC/B,KAAK,GAAG,IAAI,CAAA;QACd,CAAC;aAAM,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,MAAM,GAAG,IAAI,CAAA;QACf,CAAC;aAAM,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;YAC/B,uDAAuD;QACzD,CAAC;aAAM,CAAC;YACN,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IAED,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;AAC3C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,SAAkB;IAC5D,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;IAE7B,uBAAuB;IACvB,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAA;IACnD,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,UAAU,GAAG,SAAS;YAC1B,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;YACtB,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;QACtE,MAAM,UAAU,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,CAAA;QAC7C,OAAO,EAAE,KAAK,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,CAAA;IAC5C,CAAC;IAED,6BAA6B;IAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAClC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IAClC,OAAO,EAAE,KAAK,EAAE,CAAA;AAClB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { KeyEvent } from "@opentui/core";
2
+ import type { ParsedHotkey } from "./types.js";
3
+ export interface SequenceTrackerOptions {
4
+ timeout?: number;
5
+ }
6
+ export declare class SequenceTracker {
7
+ private buffer;
8
+ private timer;
9
+ private timeout;
10
+ constructor(options?: SequenceTrackerOptions);
11
+ /**
12
+ * Feed a key event and check against registered hotkeys.
13
+ * Returns the index of the matched hotkey, or -1 if no match.
14
+ */
15
+ feed(event: KeyEvent, hotkeys: ParsedHotkey[]): number;
16
+ private matchesBuffer;
17
+ reset(): void;
18
+ private resetTimer;
19
+ private clearTimer;
20
+ }
21
+ //# sourceMappingURL=sequence.d.ts.map
@@ -0,0 +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;CACjB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,OAAO,CAAQ;gBAEX,OAAO,CAAC,EAAE,sBAAsB;IAI5C;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM;IAqBtD,OAAO,CAAC,aAAa;IAWrB,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,UAAU;CAMnB"}
@@ -0,0 +1,56 @@
1
+ import { matchStep } from "./match.js";
2
+ export class SequenceTracker {
3
+ buffer = [];
4
+ timer = null;
5
+ timeout;
6
+ constructor(options) {
7
+ this.timeout = options?.timeout ?? 500;
8
+ }
9
+ /**
10
+ * Feed a key event and check against registered hotkeys.
11
+ * Returns the index of the matched hotkey, or -1 if no match.
12
+ */
13
+ feed(event, hotkeys) {
14
+ this.buffer.push(event);
15
+ this.resetTimer();
16
+ for (let i = 0; i < hotkeys.length; i++) {
17
+ const hotkey = hotkeys[i];
18
+ if (this.matchesBuffer(hotkey)) {
19
+ this.reset();
20
+ return i;
21
+ }
22
+ }
23
+ // Prune buffer if no hotkey could possibly match
24
+ const maxLen = Math.max(...hotkeys.map((h) => h.steps.length));
25
+ if (this.buffer.length > maxLen) {
26
+ this.buffer.shift();
27
+ }
28
+ return -1;
29
+ }
30
+ matchesBuffer(hotkey) {
31
+ const { steps } = hotkey;
32
+ if (this.buffer.length < steps.length)
33
+ return false;
34
+ const start = this.buffer.length - steps.length;
35
+ for (let i = 0; i < steps.length; i++) {
36
+ if (!matchStep(this.buffer[start + i], steps[i]))
37
+ return false;
38
+ }
39
+ return true;
40
+ }
41
+ reset() {
42
+ this.buffer = [];
43
+ this.clearTimer();
44
+ }
45
+ resetTimer() {
46
+ this.clearTimer();
47
+ this.timer = setTimeout(() => this.reset(), this.timeout);
48
+ }
49
+ clearTimer() {
50
+ if (this.timer !== null) {
51
+ clearTimeout(this.timer);
52
+ this.timer = null;
53
+ }
54
+ }
55
+ }
56
+ //# sourceMappingURL=sequence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sequence.js","sourceRoot":"","sources":["../src/sequence.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAMtC,MAAM,OAAO,eAAe;IAClB,MAAM,GAAe,EAAE,CAAA;IACvB,KAAK,GAAyC,IAAI,CAAA;IAClD,OAAO,CAAQ;IAEvB,YAAY,OAAgC;QAC1C,IAAI,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,GAAG,CAAA;IACxC,CAAC;IAED;;;OAGG;IACH,IAAI,CAAC,KAAe,EAAE,OAAuB;QAC3C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACvB,IAAI,CAAC,UAAU,EAAE,CAAA;QAEjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAE,CAAA;YAC1B,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC/B,IAAI,CAAC,KAAK,EAAE,CAAA;gBACZ,OAAO,CAAC,CAAA;YACV,CAAC;QACH,CAAC;QAED,iDAAiD;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;QAC9D,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;QACrB,CAAC;QAED,OAAO,CAAC,CAAC,CAAA;IACX,CAAC;IAEO,aAAa,CAAC,MAAoB;QACxC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAA;QACxB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM;YAAE,OAAO,KAAK,CAAA;QAEnD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;QAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAE,EAAE,KAAK,CAAC,CAAC,CAAE,CAAC;gBAAE,OAAO,KAAK,CAAA;QAClE,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,EAAE,CAAA;QAChB,IAAI,CAAC,UAAU,EAAE,CAAA;IACnB,CAAC;IAEO,UAAU;QAChB,IAAI,CAAC,UAAU,EAAE,CAAA;QACjB,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IAC3D,CAAC;IAEO,UAAU;QAChB,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACxB,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACxB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACnB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,43 @@
1
+ import type { Mode } from "./mode.jsx";
2
+ export interface CommandContextBase {
3
+ mode: Mode;
4
+ setMode: (mode: Mode) => void;
5
+ commands: {
6
+ invoke: (id: string) => void;
7
+ list: () => Command[];
8
+ };
9
+ exit: () => void;
10
+ }
11
+ export interface CommandContext extends CommandContextBase {
12
+ [key: string]: any;
13
+ }
14
+ export type CommandHandler = (ctx: CommandContext) => void | Promise<void>;
15
+ export type CommandWhen = (ctx: CommandContext) => boolean;
16
+ export interface Command {
17
+ id: string;
18
+ title: string;
19
+ handler: CommandHandler;
20
+ defaultHotkey?: string;
21
+ modes?: Mode[];
22
+ when?: CommandWhen;
23
+ category?: string;
24
+ group?: string;
25
+ icon?: string;
26
+ hidden?: boolean;
27
+ }
28
+ export interface ParsedHotkey {
29
+ steps: ParsedStep[];
30
+ }
31
+ export interface ParsedStep {
32
+ key: string;
33
+ ctrl: boolean;
34
+ meta: boolean;
35
+ shift: boolean;
36
+ option: boolean;
37
+ }
38
+ export interface CommandRegistry {
39
+ commands: Map<string, Command>;
40
+ register: (command: Command) => () => void;
41
+ invoke: (id: string) => void;
42
+ }
43
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtC,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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import type { CommandHandler, CommandWhen } from "./types.js";
2
+ import type { Mode } from "./mode.jsx";
3
+ export interface ActionDefinition {
4
+ id: string;
5
+ title: string;
6
+ hotkey?: string;
7
+ modes?: Mode[];
8
+ handler: CommandHandler;
9
+ when?: CommandWhen;
10
+ }
11
+ export declare function useActions(actions: ActionDefinition[] | undefined): void;
12
+ //# sourceMappingURL=use-actions.d.ts.map
@@ -0,0 +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,YAAY,CAAA;AAEtC,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;CACnB;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,SAAS,GAAG,IAAI,CAgCxE"}
@@ -0,0 +1,30 @@
1
+ import { useEffect, useMemo, useRef } from "react";
2
+ import { useCommandRegistry } from "./context.jsx";
3
+ export function useActions(actions) {
4
+ const { registry } = useCommandRegistry();
5
+ const actionsRef = useRef(actions);
6
+ actionsRef.current = actions;
7
+ const key = useMemo(() => actions?.map((a) => `${a.id}:${a.hotkey ?? ""}`).join(",") ?? "", [actions]);
8
+ useEffect(() => {
9
+ const current = actionsRef.current;
10
+ if (!current || current.length === 0)
11
+ return;
12
+ const unregisters = current.map((action, i) => {
13
+ const command = {
14
+ id: action.id,
15
+ title: action.title,
16
+ defaultHotkey: action.hotkey,
17
+ modes: action.modes,
18
+ handler: (ctx) => actionsRef.current?.[i]?.handler(ctx),
19
+ when: action.when ? (ctx) => actionsRef.current?.[i]?.when?.(ctx) ?? false : undefined,
20
+ };
21
+ return registry.register(command);
22
+ });
23
+ return () => {
24
+ for (const unregister of unregisters) {
25
+ unregister();
26
+ }
27
+ };
28
+ }, [key, registry]);
29
+ }
30
+ //# sourceMappingURL=use-actions.js.map
@@ -0,0 +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,eAAe,CAAA;AAalD,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,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EACtE,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;aACvF,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"}
@@ -0,0 +1,16 @@
1
+ import type { CommandHandler, CommandWhen } from "./types.js";
2
+ import type { Mode } from "./mode.jsx";
3
+ export interface UseCommandOptions {
4
+ id: string;
5
+ title: string;
6
+ handler: CommandHandler;
7
+ hotkey?: string;
8
+ modes?: Mode[];
9
+ category?: string;
10
+ group?: string;
11
+ icon?: string;
12
+ when?: CommandWhen;
13
+ hidden?: boolean;
14
+ }
15
+ export declare function useCommand(options: UseCommandOptions): void;
16
+ //# sourceMappingURL=use-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-command.d.ts","sourceRoot":"","sources":["../src/use-command.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAW,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACtE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAGtC,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,cAAc,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,IAAI,EAAE,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CA8B3D"}