@mdxui/terminal 2.0.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 (191) hide show
  1. package/README.md +571 -0
  2. package/dist/ansi-css-Sk5mWtdK.d.ts +119 -0
  3. package/dist/ansi-css-V6JIHGsM.d.ts +119 -0
  4. package/dist/ansi-css-_3eSEU9d.d.ts +119 -0
  5. package/dist/chunk-3EFDH7PK.js +5235 -0
  6. package/dist/chunk-3RG5ZIWI.js +10 -0
  7. package/dist/chunk-3X5IR6WE.js +884 -0
  8. package/dist/chunk-4FV5ZDCE.js +5236 -0
  9. package/dist/chunk-4OVMSF2J.js +243 -0
  10. package/dist/chunk-63FEETIS.js +4048 -0
  11. package/dist/chunk-B43KP7XJ.js +884 -0
  12. package/dist/chunk-BMTJXWUV.js +655 -0
  13. package/dist/chunk-C3SVH4N7.js +882 -0
  14. package/dist/chunk-EVWR7Y47.js +874 -0
  15. package/dist/chunk-F6A5VWUC.js +1285 -0
  16. package/dist/chunk-FD7KW7GE.js +882 -0
  17. package/dist/chunk-GBQ6UD6I.js +655 -0
  18. package/dist/chunk-GMDD3M6U.js +5227 -0
  19. package/dist/chunk-JBHRXOXM.js +1058 -0
  20. package/dist/chunk-JFOO3EYO.js +1182 -0
  21. package/dist/chunk-JQ5H3WXL.js +1291 -0
  22. package/dist/chunk-JQD5NASE.js +234 -0
  23. package/dist/chunk-KRHJP5R7.js +592 -0
  24. package/dist/chunk-KWF6WVJE.js +962 -0
  25. package/dist/chunk-LHYQVN3H.js +1038 -0
  26. package/dist/chunk-M3TLQLGC.js +1032 -0
  27. package/dist/chunk-MVW4Q5OP.js +240 -0
  28. package/dist/chunk-NXCZSWLU.js +1294 -0
  29. package/dist/chunk-O25TNRO6.js +607 -0
  30. package/dist/chunk-PNECDA2I.js +884 -0
  31. package/dist/chunk-QIHWRLJR.js +962 -0
  32. package/dist/chunk-QW5YMQ7K.js +882 -0
  33. package/dist/chunk-R5U7XKVJ.js +16 -0
  34. package/dist/chunk-RP2MVQLR.js +962 -0
  35. package/dist/chunk-TP6RXGXA.js +1087 -0
  36. package/dist/chunk-TQQSTITZ.js +655 -0
  37. package/dist/chunk-X24GWXQV.js +1281 -0
  38. package/dist/components/index.d.ts +802 -0
  39. package/dist/components/index.js +149 -0
  40. package/dist/data/index.d.ts +2554 -0
  41. package/dist/data/index.js +51 -0
  42. package/dist/forms/index.d.ts +1596 -0
  43. package/dist/forms/index.js +464 -0
  44. package/dist/index-CQRFZntR.d.ts +867 -0
  45. package/dist/index.d.ts +579 -0
  46. package/dist/index.js +786 -0
  47. package/dist/interactive-D0JkWosD.d.ts +217 -0
  48. package/dist/keyboard/index.d.ts +2 -0
  49. package/dist/keyboard/index.js +43 -0
  50. package/dist/renderers/index.d.ts +546 -0
  51. package/dist/renderers/index.js +2157 -0
  52. package/dist/storybook/index.d.ts +396 -0
  53. package/dist/storybook/index.js +641 -0
  54. package/dist/theme/index.d.ts +1339 -0
  55. package/dist/theme/index.js +123 -0
  56. package/dist/types-Bxu5PAgA.d.ts +710 -0
  57. package/dist/types-CIlop5Ji.d.ts +701 -0
  58. package/dist/types-Ca8p_p5X.d.ts +710 -0
  59. package/package.json +90 -0
  60. package/src/__tests__/components/data/card.test.ts +458 -0
  61. package/src/__tests__/components/data/list.test.ts +473 -0
  62. package/src/__tests__/components/data/metrics.test.ts +541 -0
  63. package/src/__tests__/components/data/table.test.ts +448 -0
  64. package/src/__tests__/components/input/field.test.ts +555 -0
  65. package/src/__tests__/components/input/form.test.ts +870 -0
  66. package/src/__tests__/components/input/search.test.ts +1238 -0
  67. package/src/__tests__/components/input/select.test.ts +658 -0
  68. package/src/__tests__/components/navigation/breadcrumb.test.ts +923 -0
  69. package/src/__tests__/components/navigation/command-palette.test.ts +1095 -0
  70. package/src/__tests__/components/navigation/sidebar.test.ts +1018 -0
  71. package/src/__tests__/components/navigation/tabs.test.ts +995 -0
  72. package/src/__tests__/components.test.tsx +1197 -0
  73. package/src/__tests__/core/compiler.test.ts +986 -0
  74. package/src/__tests__/core/parser.test.ts +785 -0
  75. package/src/__tests__/core/tier-switcher.test.ts +1103 -0
  76. package/src/__tests__/core/types.test.ts +1398 -0
  77. package/src/__tests__/data/collections.test.ts +1337 -0
  78. package/src/__tests__/data/db.test.ts +1265 -0
  79. package/src/__tests__/data/reactive.test.ts +1010 -0
  80. package/src/__tests__/data/sync.test.ts +1614 -0
  81. package/src/__tests__/errors.test.ts +660 -0
  82. package/src/__tests__/forms/integration.test.ts +444 -0
  83. package/src/__tests__/integration.test.ts +905 -0
  84. package/src/__tests__/keyboard.test.ts +1791 -0
  85. package/src/__tests__/renderer.test.ts +489 -0
  86. package/src/__tests__/renderers/ansi-css.test.ts +948 -0
  87. package/src/__tests__/renderers/ansi.test.ts +1366 -0
  88. package/src/__tests__/renderers/ascii.test.ts +1360 -0
  89. package/src/__tests__/renderers/interactive.test.ts +2353 -0
  90. package/src/__tests__/renderers/markdown.test.ts +1483 -0
  91. package/src/__tests__/renderers/text.test.ts +1369 -0
  92. package/src/__tests__/renderers/unicode.test.ts +1307 -0
  93. package/src/__tests__/theme.test.ts +639 -0
  94. package/src/__tests__/utils/assertions.ts +685 -0
  95. package/src/__tests__/utils/index.ts +115 -0
  96. package/src/__tests__/utils/test-renderer.ts +381 -0
  97. package/src/__tests__/utils/utils.test.ts +560 -0
  98. package/src/components/containers/card.ts +56 -0
  99. package/src/components/containers/dialog.ts +53 -0
  100. package/src/components/containers/index.ts +9 -0
  101. package/src/components/containers/panel.ts +59 -0
  102. package/src/components/feedback/badge.ts +40 -0
  103. package/src/components/feedback/index.ts +8 -0
  104. package/src/components/feedback/spinner.ts +23 -0
  105. package/src/components/helpers.ts +81 -0
  106. package/src/components/index.ts +153 -0
  107. package/src/components/layout/breadcrumb.ts +31 -0
  108. package/src/components/layout/index.ts +10 -0
  109. package/src/components/layout/list.ts +29 -0
  110. package/src/components/layout/sidebar.ts +79 -0
  111. package/src/components/layout/table.ts +62 -0
  112. package/src/components/primitives/box.ts +95 -0
  113. package/src/components/primitives/button.ts +54 -0
  114. package/src/components/primitives/index.ts +11 -0
  115. package/src/components/primitives/input.ts +88 -0
  116. package/src/components/primitives/select.ts +97 -0
  117. package/src/components/primitives/text.ts +60 -0
  118. package/src/components/render.ts +155 -0
  119. package/src/components/templates/app.ts +43 -0
  120. package/src/components/templates/index.ts +8 -0
  121. package/src/components/templates/site.ts +54 -0
  122. package/src/components/types.ts +777 -0
  123. package/src/core/compiler.ts +718 -0
  124. package/src/core/parser.ts +127 -0
  125. package/src/core/tier-switcher.ts +607 -0
  126. package/src/core/types.ts +672 -0
  127. package/src/data/collection.ts +316 -0
  128. package/src/data/collections.ts +50 -0
  129. package/src/data/context.tsx +174 -0
  130. package/src/data/db.ts +127 -0
  131. package/src/data/hooks.ts +532 -0
  132. package/src/data/index.ts +138 -0
  133. package/src/data/reactive.ts +1225 -0
  134. package/src/data/saas-collections.ts +375 -0
  135. package/src/data/sync.ts +1213 -0
  136. package/src/data/types.ts +660 -0
  137. package/src/forms/converters.ts +512 -0
  138. package/src/forms/index.ts +133 -0
  139. package/src/forms/schemas.ts +403 -0
  140. package/src/forms/types.ts +476 -0
  141. package/src/index.ts +542 -0
  142. package/src/keyboard/focus.ts +748 -0
  143. package/src/keyboard/index.ts +96 -0
  144. package/src/keyboard/integration.ts +371 -0
  145. package/src/keyboard/manager.ts +377 -0
  146. package/src/keyboard/presets.ts +90 -0
  147. package/src/renderers/ansi-css.ts +576 -0
  148. package/src/renderers/ansi.ts +802 -0
  149. package/src/renderers/ascii.ts +680 -0
  150. package/src/renderers/breadcrumb.ts +480 -0
  151. package/src/renderers/command-palette.ts +802 -0
  152. package/src/renderers/components/field.ts +210 -0
  153. package/src/renderers/components/form.ts +327 -0
  154. package/src/renderers/components/index.ts +21 -0
  155. package/src/renderers/components/search.ts +449 -0
  156. package/src/renderers/components/select.ts +222 -0
  157. package/src/renderers/index.ts +101 -0
  158. package/src/renderers/interactive/component-handlers.ts +622 -0
  159. package/src/renderers/interactive/cursor-manager.ts +147 -0
  160. package/src/renderers/interactive/focus-manager.ts +279 -0
  161. package/src/renderers/interactive/index.ts +661 -0
  162. package/src/renderers/interactive/input-handler.ts +164 -0
  163. package/src/renderers/interactive/keyboard-handler.ts +212 -0
  164. package/src/renderers/interactive/mouse-handler.ts +167 -0
  165. package/src/renderers/interactive/state-manager.ts +109 -0
  166. package/src/renderers/interactive/types.ts +338 -0
  167. package/src/renderers/interactive-string.ts +299 -0
  168. package/src/renderers/interactive.ts +59 -0
  169. package/src/renderers/markdown.ts +950 -0
  170. package/src/renderers/sidebar.ts +549 -0
  171. package/src/renderers/tabs.ts +682 -0
  172. package/src/renderers/text.ts +791 -0
  173. package/src/renderers/unicode.ts +917 -0
  174. package/src/renderers/utils.ts +942 -0
  175. package/src/router/adapters.ts +383 -0
  176. package/src/router/types.ts +140 -0
  177. package/src/router/utils.ts +452 -0
  178. package/src/schemas.ts +205 -0
  179. package/src/storybook/index.ts +91 -0
  180. package/src/storybook/interactive-decorator.tsx +659 -0
  181. package/src/storybook/keyboard-simulator.ts +501 -0
  182. package/src/theme/ansi-codes.ts +80 -0
  183. package/src/theme/box-drawing.ts +132 -0
  184. package/src/theme/color-convert.ts +254 -0
  185. package/src/theme/color-support.ts +321 -0
  186. package/src/theme/index.ts +134 -0
  187. package/src/theme/strip-ansi.ts +50 -0
  188. package/src/theme/tailwind-map.ts +469 -0
  189. package/src/theme/text-styles.ts +206 -0
  190. package/src/theme/theme-system.ts +568 -0
  191. package/src/types.ts +103 -0
@@ -0,0 +1,641 @@
1
+ import {
2
+ createInteractiveRenderer
3
+ } from "../chunk-LHYQVN3H.js";
4
+ import "../chunk-3RG5ZIWI.js";
5
+
6
+ // src/storybook/keyboard-simulator.ts
7
+ var KeySequence = {
8
+ /** Navigate down one item */
9
+ NAVIGATE_DOWN: [{ key: "down" }],
10
+ /** Navigate down three items */
11
+ NAVIGATE_DOWN_3: [
12
+ { key: "down" },
13
+ { key: "down" },
14
+ { key: "down" }
15
+ ],
16
+ /** Navigate up one item */
17
+ NAVIGATE_UP: [{ key: "up" }],
18
+ /** Navigate up three items */
19
+ NAVIGATE_UP_3: [
20
+ { key: "up" },
21
+ { key: "up" },
22
+ { key: "up" }
23
+ ],
24
+ /** Tab forward through focusables */
25
+ TAB_FORWARD: [{ key: "tab" }],
26
+ /** Tab forward three times */
27
+ TAB_FORWARD_3: [
28
+ { key: "tab" },
29
+ { key: "tab" },
30
+ { key: "tab" }
31
+ ],
32
+ /** Tab backward through focusables */
33
+ TAB_BACKWARD: [{ key: "tab", modifiers: { shift: true } }],
34
+ /** Tab backward three times */
35
+ TAB_BACKWARD_3: [
36
+ { key: "tab", modifiers: { shift: true } },
37
+ { key: "tab", modifiers: { shift: true } },
38
+ { key: "tab", modifiers: { shift: true } }
39
+ ],
40
+ /** Select/activate focused element */
41
+ SELECT: [{ key: "enter" }],
42
+ /** Toggle focused element (e.g., checkbox) */
43
+ TOGGLE: [{ key: "space" }],
44
+ /** Cancel/escape current action */
45
+ CANCEL: [{ key: "escape" }],
46
+ /** Navigate to first item (vim: gg) */
47
+ GOTO_FIRST: [{ key: "g" }, { key: "g" }],
48
+ /** Navigate to last item (vim: G) */
49
+ GOTO_LAST: [{ key: "G", modifiers: { shift: true } }],
50
+ /** Open search mode (vim: /) */
51
+ SEARCH: [{ key: "/" }],
52
+ /** Navigate down and select */
53
+ NAVIGATE_DOWN_SELECT: [{ key: "down" }, { key: "enter" }],
54
+ /** Navigate to next item using j (vim) */
55
+ VIM_DOWN: [{ key: "j" }],
56
+ /** Navigate to previous item using k (vim) */
57
+ VIM_UP: [{ key: "k" }],
58
+ /** Navigate left using h (vim) */
59
+ VIM_LEFT: [{ key: "h" }],
60
+ /** Navigate right using l (vim) */
61
+ VIM_RIGHT: [{ key: "l" }],
62
+ /** Delete sequence (vim: dd) */
63
+ VIM_DELETE: [{ key: "d" }, { key: "d" }],
64
+ /** Home key - jump to beginning */
65
+ HOME: [{ key: "home" }],
66
+ /** End key - jump to end */
67
+ END: [{ key: "end" }],
68
+ /** Page down */
69
+ PAGE_DOWN: [{ key: "pagedown" }],
70
+ /** Page up */
71
+ PAGE_UP: [{ key: "pageup" }],
72
+ /** Ctrl+C - cancel/interrupt */
73
+ INTERRUPT: [{ key: "c", modifiers: { ctrl: true } }],
74
+ /** Ctrl+S - save */
75
+ SAVE: [{ key: "s", modifiers: { ctrl: true } }]
76
+ };
77
+ function toBindingString(key, modifiers) {
78
+ const parts = [];
79
+ if (modifiers?.ctrl) parts.push("ctrl");
80
+ if (modifiers?.alt) parts.push("alt");
81
+ if (modifiers?.shift) parts.push("shift");
82
+ if (modifiers?.meta) parts.push("meta");
83
+ parts.push(key.toLowerCase());
84
+ return parts.join("+");
85
+ }
86
+ function sleep(ms) {
87
+ return new Promise((resolve) => setTimeout(resolve, ms));
88
+ }
89
+ function createKeyboardSimulator(config) {
90
+ const { renderer, defaultDelay = 0, onKeyPress, onSequenceComplete } = config;
91
+ const pressedKeys = [];
92
+ const simulator = {
93
+ press(key, modifiers) {
94
+ const bindingString = toBindingString(key, modifiers);
95
+ pressedKeys.push(bindingString);
96
+ onKeyPress?.(key, modifiers ?? {});
97
+ renderer.emitKey(bindingString);
98
+ },
99
+ async pressAsync(key, modifiers) {
100
+ this.press(key, modifiers);
101
+ if (defaultDelay > 0) {
102
+ await sleep(defaultDelay);
103
+ }
104
+ },
105
+ sequence(keys) {
106
+ for (const keyPress of keys) {
107
+ this.press(keyPress.key, keyPress.modifiers);
108
+ }
109
+ onSequenceComplete?.(keys);
110
+ },
111
+ async sequenceAsync(keys) {
112
+ for (const keyPress of keys) {
113
+ const delay = keyPress.delay ?? defaultDelay;
114
+ if (delay > 0) {
115
+ await sleep(delay);
116
+ }
117
+ this.press(keyPress.key, keyPress.modifiers);
118
+ }
119
+ onSequenceComplete?.(keys);
120
+ },
121
+ type(text) {
122
+ for (const char of text) {
123
+ if (char === "\n") {
124
+ this.press("enter");
125
+ } else if (char === " ") {
126
+ this.press("tab");
127
+ } else if (char === " ") {
128
+ this.press("space");
129
+ } else {
130
+ const isUpperCase = char === char.toUpperCase() && char !== char.toLowerCase();
131
+ this.press(char, isUpperCase ? { shift: true } : void 0);
132
+ }
133
+ }
134
+ },
135
+ async typeAsync(text, charDelay = 50) {
136
+ for (const char of text) {
137
+ if (charDelay > 0) {
138
+ await sleep(charDelay);
139
+ }
140
+ if (char === "\n") {
141
+ this.press("enter");
142
+ } else if (char === " ") {
143
+ this.press("tab");
144
+ } else if (char === " ") {
145
+ this.press("space");
146
+ } else {
147
+ const isUpperCase = char === char.toUpperCase() && char !== char.toLowerCase();
148
+ this.press(char, isUpperCase ? { shift: true } : void 0);
149
+ }
150
+ }
151
+ },
152
+ reset() {
153
+ this.clearHistory();
154
+ },
155
+ getPressedKeys() {
156
+ return [...pressedKeys];
157
+ },
158
+ clearHistory() {
159
+ pressedKeys.length = 0;
160
+ }
161
+ };
162
+ return simulator;
163
+ }
164
+ function expectKeyPressed(simulator, key, modifiers) {
165
+ const bindingString = toBindingString(key, modifiers);
166
+ const pressedKeys = simulator.getPressedKeys();
167
+ if (!pressedKeys.includes(bindingString)) {
168
+ throw new Error(
169
+ `Expected key "${bindingString}" to be pressed, but it was not. Pressed keys: [${pressedKeys.join(", ")}]`
170
+ );
171
+ }
172
+ }
173
+ function expectSequencePressed(simulator, sequence) {
174
+ const pressedKeys = simulator.getPressedKeys();
175
+ const expectedKeys = sequence.map((kp) => toBindingString(kp.key, kp.modifiers));
176
+ let found = false;
177
+ for (let i = 0; i <= pressedKeys.length - expectedKeys.length; i++) {
178
+ let match = true;
179
+ for (let j = 0; j < expectedKeys.length; j++) {
180
+ if (pressedKeys[i + j] !== expectedKeys[j]) {
181
+ match = false;
182
+ break;
183
+ }
184
+ }
185
+ if (match) {
186
+ found = true;
187
+ break;
188
+ }
189
+ }
190
+ if (!found) {
191
+ throw new Error(
192
+ `Expected sequence [${expectedKeys.join(", ")}] to be pressed in order, but it was not. Pressed keys: [${pressedKeys.join(", ")}]`
193
+ );
194
+ }
195
+ }
196
+ function expectNoKeysPressed(simulator) {
197
+ const pressedKeys = simulator.getPressedKeys();
198
+ if (pressedKeys.length > 0) {
199
+ throw new Error(
200
+ `Expected no keys to be pressed, but found: [${pressedKeys.join(", ")}]`
201
+ );
202
+ }
203
+ }
204
+ function expectKeyPressCount(simulator, count) {
205
+ const pressedKeys = simulator.getPressedKeys();
206
+ if (pressedKeys.length !== count) {
207
+ throw new Error(
208
+ `Expected ${count} key press(es), but found ${pressedKeys.length}. Pressed keys: [${pressedKeys.join(", ")}]`
209
+ );
210
+ }
211
+ }
212
+ function mapDOMKeyEvent(event) {
213
+ let key = event.key.toLowerCase();
214
+ const keyMap = {
215
+ arrowup: "up",
216
+ arrowdown: "down",
217
+ arrowleft: "left",
218
+ arrowright: "right",
219
+ " ": "space",
220
+ return: "enter"
221
+ };
222
+ key = keyMap[key] ?? key;
223
+ return {
224
+ key,
225
+ modifiers: {
226
+ ctrl: event.ctrlKey,
227
+ alt: event.altKey,
228
+ shift: event.shiftKey,
229
+ meta: event.metaKey
230
+ }
231
+ };
232
+ }
233
+ function createDOMKeyHandler(simulator, options) {
234
+ const { preventDefault = true, stopPropagation = true, ignoreKeys = [] } = options ?? {};
235
+ return (event) => {
236
+ const keyPress = mapDOMKeyEvent(event);
237
+ if (ignoreKeys.includes(keyPress.key)) {
238
+ return;
239
+ }
240
+ if (preventDefault) {
241
+ event.preventDefault();
242
+ }
243
+ if (stopPropagation) {
244
+ event.stopPropagation();
245
+ }
246
+ simulator.press(keyPress.key, keyPress.modifiers);
247
+ };
248
+ }
249
+
250
+ // src/storybook/interactive-decorator.tsx
251
+ import React, { useEffect, useState, useCallback, useRef } from "react";
252
+ import { jsx, jsxs } from "react/jsx-runtime";
253
+ var InteractiveContext = React.createContext({
254
+ renderer: null,
255
+ simulator: null,
256
+ keyLog: [],
257
+ focusedId: null,
258
+ clearKeyLog: () => {
259
+ }
260
+ });
261
+ function useInteractiveContext() {
262
+ return React.useContext(InteractiveContext);
263
+ }
264
+ function KeyLogPanel({ keyLog, focusedId, onClear }) {
265
+ return /* @__PURE__ */ jsxs(
266
+ "div",
267
+ {
268
+ style: {
269
+ position: "fixed",
270
+ bottom: "16px",
271
+ right: "16px",
272
+ width: "240px",
273
+ maxHeight: "200px",
274
+ backgroundColor: "rgba(0, 0, 0, 0.85)",
275
+ color: "#e5e5e5",
276
+ borderRadius: "8px",
277
+ padding: "12px",
278
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
279
+ fontSize: "12px",
280
+ zIndex: 9999,
281
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.3)"
282
+ },
283
+ children: [
284
+ /* @__PURE__ */ jsxs(
285
+ "div",
286
+ {
287
+ style: {
288
+ display: "flex",
289
+ justifyContent: "space-between",
290
+ alignItems: "center",
291
+ marginBottom: "8px",
292
+ paddingBottom: "8px",
293
+ borderBottom: "1px solid rgba(255, 255, 255, 0.1)"
294
+ },
295
+ children: [
296
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, color: "#a3e635" }, children: "Keyboard Log" }),
297
+ /* @__PURE__ */ jsx(
298
+ "button",
299
+ {
300
+ onClick: onClear,
301
+ style: {
302
+ background: "transparent",
303
+ border: "none",
304
+ color: "#737373",
305
+ cursor: "pointer",
306
+ fontSize: "11px",
307
+ padding: "2px 6px"
308
+ },
309
+ children: "Clear"
310
+ }
311
+ )
312
+ ]
313
+ }
314
+ ),
315
+ focusedId && /* @__PURE__ */ jsxs(
316
+ "div",
317
+ {
318
+ style: {
319
+ marginBottom: "8px",
320
+ padding: "4px 8px",
321
+ backgroundColor: "rgba(163, 230, 53, 0.15)",
322
+ borderRadius: "4px",
323
+ color: "#a3e635"
324
+ },
325
+ children: [
326
+ "Focus: ",
327
+ /* @__PURE__ */ jsx("code", { style: { color: "#fde047" }, children: focusedId })
328
+ ]
329
+ }
330
+ ),
331
+ /* @__PURE__ */ jsx(
332
+ "div",
333
+ {
334
+ style: {
335
+ maxHeight: "120px",
336
+ overflowY: "auto"
337
+ },
338
+ children: keyLog.length === 0 ? /* @__PURE__ */ jsx("div", { style: { color: "#737373", fontStyle: "italic" }, children: "Press keys to see log..." }) : keyLog.map((key, index) => /* @__PURE__ */ jsxs(
339
+ "div",
340
+ {
341
+ style: {
342
+ padding: "2px 0",
343
+ display: "flex",
344
+ alignItems: "center",
345
+ gap: "8px"
346
+ },
347
+ children: [
348
+ /* @__PURE__ */ jsxs("span", { style: { color: "#737373" }, children: [
349
+ index + 1,
350
+ "."
351
+ ] }),
352
+ /* @__PURE__ */ jsx(
353
+ "code",
354
+ {
355
+ style: {
356
+ backgroundColor: "rgba(255, 255, 255, 0.1)",
357
+ padding: "1px 6px",
358
+ borderRadius: "3px",
359
+ color: "#60a5fa"
360
+ },
361
+ children: key
362
+ }
363
+ )
364
+ ]
365
+ },
366
+ `${key}-${index}`
367
+ ))
368
+ }
369
+ ),
370
+ /* @__PURE__ */ jsx(
371
+ "div",
372
+ {
373
+ style: {
374
+ marginTop: "8px",
375
+ paddingTop: "8px",
376
+ borderTop: "1px solid rgba(255, 255, 255, 0.1)",
377
+ color: "#737373",
378
+ fontSize: "10px"
379
+ },
380
+ children: "Click container to focus. Use Tab, arrows, vim keys."
381
+ }
382
+ )
383
+ ]
384
+ }
385
+ );
386
+ }
387
+ function InteractiveContainer({
388
+ children,
389
+ config = {}
390
+ }) {
391
+ const containerRef = useRef(null);
392
+ const [renderer, setRenderer] = useState(null);
393
+ const [simulator, setSimulator] = useState(null);
394
+ const [keyLog, setKeyLog] = useState([]);
395
+ const [focusedId, setFocusedId] = useState(null);
396
+ const {
397
+ showKeyLog = true,
398
+ maxKeyLogEntries = 20,
399
+ showFocusOverlay = false,
400
+ ignoreKeys = ["f5", "f12"],
401
+ autoFocus = true,
402
+ ...rendererConfig
403
+ } = config;
404
+ useEffect(() => {
405
+ let mounted = true;
406
+ async function init() {
407
+ const newRenderer = await createInteractiveRenderer(rendererConfig);
408
+ if (!mounted) {
409
+ newRenderer.destroy();
410
+ return;
411
+ }
412
+ const newSimulator = createKeyboardSimulator({
413
+ renderer: newRenderer,
414
+ onKeyPress: (key, modifiers) => {
415
+ const parts = [];
416
+ if (modifiers.ctrl) parts.push("Ctrl");
417
+ if (modifiers.alt) parts.push("Alt");
418
+ if (modifiers.shift) parts.push("Shift");
419
+ if (modifiers.meta) parts.push("Meta");
420
+ parts.push(key);
421
+ setKeyLog((prev) => {
422
+ const newLog = [...prev, parts.join("+")];
423
+ return newLog.slice(-maxKeyLogEntries);
424
+ });
425
+ }
426
+ });
427
+ setRenderer(newRenderer);
428
+ setSimulator(newSimulator);
429
+ }
430
+ init();
431
+ return () => {
432
+ mounted = false;
433
+ renderer?.destroy();
434
+ };
435
+ }, []);
436
+ useEffect(() => {
437
+ if (!renderer) return;
438
+ const interval = setInterval(() => {
439
+ const currentFocusedId = renderer.getFocusedId();
440
+ if (currentFocusedId !== focusedId) {
441
+ setFocusedId(currentFocusedId);
442
+ }
443
+ }, 100);
444
+ return () => clearInterval(interval);
445
+ }, [renderer, focusedId]);
446
+ const handleKeyDown = useCallback(
447
+ (event) => {
448
+ if (!simulator) return;
449
+ const handler = createDOMKeyHandler(simulator, {
450
+ preventDefault: true,
451
+ stopPropagation: true,
452
+ ignoreKeys
453
+ });
454
+ handler(event.nativeEvent);
455
+ },
456
+ [simulator, ignoreKeys]
457
+ );
458
+ const handleContainerFocus = useCallback(() => {
459
+ if (renderer && !renderer.getFocusedId()) {
460
+ renderer.focusNext();
461
+ }
462
+ }, [renderer]);
463
+ useEffect(() => {
464
+ if (autoFocus && containerRef.current) {
465
+ containerRef.current.focus();
466
+ }
467
+ }, [autoFocus, renderer]);
468
+ const clearKeyLog = useCallback(() => {
469
+ setKeyLog([]);
470
+ simulator?.clearHistory();
471
+ }, [simulator]);
472
+ const contextValue = {
473
+ renderer,
474
+ simulator,
475
+ keyLog,
476
+ focusedId,
477
+ clearKeyLog
478
+ };
479
+ return /* @__PURE__ */ jsx(InteractiveContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs(
480
+ "div",
481
+ {
482
+ ref: containerRef,
483
+ tabIndex: 0,
484
+ onKeyDown: handleKeyDown,
485
+ onFocus: handleContainerFocus,
486
+ style: {
487
+ outline: "none",
488
+ minHeight: "100%",
489
+ position: "relative"
490
+ },
491
+ "data-interactive-container": "true",
492
+ children: [
493
+ children,
494
+ showKeyLog && /* @__PURE__ */ jsx(
495
+ KeyLogPanel,
496
+ {
497
+ keyLog,
498
+ focusedId,
499
+ onClear: clearKeyLog
500
+ }
501
+ ),
502
+ showFocusOverlay && focusedId && /* @__PURE__ */ jsxs(
503
+ "div",
504
+ {
505
+ style: {
506
+ position: "fixed",
507
+ top: "16px",
508
+ left: "16px",
509
+ backgroundColor: "rgba(163, 230, 53, 0.9)",
510
+ color: "#000",
511
+ padding: "4px 8px",
512
+ borderRadius: "4px",
513
+ fontFamily: "ui-monospace, monospace",
514
+ fontSize: "11px",
515
+ fontWeight: 600,
516
+ zIndex: 9998
517
+ },
518
+ children: [
519
+ "Focused: ",
520
+ focusedId
521
+ ]
522
+ }
523
+ )
524
+ ]
525
+ }
526
+ ) });
527
+ }
528
+ var InteractiveDecorator = (Story, context) => {
529
+ const interactiveParams = context.parameters?.interactive;
530
+ return /* @__PURE__ */ jsx(InteractiveContainer, { config: interactiveParams, children: /* @__PURE__ */ jsx(Story, {}) });
531
+ };
532
+ function useKeyboardNavigation(config) {
533
+ const containerRef = useRef(null);
534
+ const [renderer, setRenderer] = useState(null);
535
+ const [simulator, setSimulator] = useState(null);
536
+ const [keyLog, setKeyLog] = useState([]);
537
+ const [focusedId, setFocusedId] = useState(null);
538
+ useEffect(() => {
539
+ let mounted = true;
540
+ async function init() {
541
+ const newRenderer = await createInteractiveRenderer(config);
542
+ if (!mounted) {
543
+ newRenderer.destroy();
544
+ return;
545
+ }
546
+ const newSimulator = createKeyboardSimulator({
547
+ renderer: newRenderer,
548
+ onKeyPress: (key, modifiers) => {
549
+ const parts = [];
550
+ if (modifiers.ctrl) parts.push("Ctrl");
551
+ if (modifiers.alt) parts.push("Alt");
552
+ if (modifiers.shift) parts.push("Shift");
553
+ if (modifiers.meta) parts.push("Meta");
554
+ parts.push(key);
555
+ setKeyLog((prev) => [...prev.slice(-19), parts.join("+")]);
556
+ }
557
+ });
558
+ setRenderer(newRenderer);
559
+ setSimulator(newSimulator);
560
+ }
561
+ init();
562
+ return () => {
563
+ mounted = false;
564
+ renderer?.destroy();
565
+ };
566
+ }, []);
567
+ useEffect(() => {
568
+ if (!renderer) return;
569
+ const interval = setInterval(() => {
570
+ setFocusedId(renderer.getFocusedId());
571
+ }, 100);
572
+ return () => clearInterval(interval);
573
+ }, [renderer]);
574
+ const handleKeyDown = useCallback(
575
+ (event) => {
576
+ if (!simulator) return;
577
+ const handler = createDOMKeyHandler(simulator, {
578
+ preventDefault: true,
579
+ stopPropagation: true
580
+ });
581
+ handler(event.nativeEvent);
582
+ },
583
+ [simulator]
584
+ );
585
+ const handleFocus = useCallback(() => {
586
+ if (renderer && !renderer.getFocusedId()) {
587
+ renderer.focusNext();
588
+ }
589
+ }, [renderer]);
590
+ const clearKeyLog = useCallback(() => {
591
+ setKeyLog([]);
592
+ simulator?.clearHistory();
593
+ }, [simulator]);
594
+ return {
595
+ renderer,
596
+ simulator,
597
+ keyLog,
598
+ focusedId,
599
+ clearKeyLog,
600
+ containerProps: {
601
+ ref: containerRef,
602
+ tabIndex: 0,
603
+ onKeyDown: handleKeyDown,
604
+ onFocus: handleFocus,
605
+ style: { outline: "none" }
606
+ }
607
+ };
608
+ }
609
+ function simulateKeyboardDemo(simulator, steps) {
610
+ let cancelled = false;
611
+ const timeouts = [];
612
+ let cumulativeDelay = 0;
613
+ for (const step of steps) {
614
+ cumulativeDelay += step.delay;
615
+ const timeout = setTimeout(() => {
616
+ if (!cancelled) {
617
+ simulator.sequence(step.keys);
618
+ }
619
+ }, cumulativeDelay);
620
+ timeouts.push(timeout);
621
+ }
622
+ return () => {
623
+ cancelled = true;
624
+ timeouts.forEach((t) => clearTimeout(t));
625
+ };
626
+ }
627
+ export {
628
+ InteractiveContext,
629
+ InteractiveDecorator,
630
+ KeySequence,
631
+ createDOMKeyHandler,
632
+ createKeyboardSimulator,
633
+ expectKeyPressCount,
634
+ expectKeyPressed,
635
+ expectNoKeysPressed,
636
+ expectSequencePressed,
637
+ mapDOMKeyEvent,
638
+ simulateKeyboardDemo,
639
+ useInteractiveContext,
640
+ useKeyboardNavigation
641
+ };