@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,655 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-R5U7XKVJ.js";
4
+
5
+ // src/keyboard/manager.ts
6
+ var KEY = {
7
+ ENTER: "enter",
8
+ ESCAPE: "escape",
9
+ UP: "up",
10
+ DOWN: "down",
11
+ LEFT: "left",
12
+ RIGHT: "right",
13
+ TAB: "tab",
14
+ SPACE: "space",
15
+ BACKSPACE: "backspace",
16
+ DELETE: "delete"
17
+ };
18
+ function matchKey(key, expected) {
19
+ return key === expected;
20
+ }
21
+ function createKeyboardManager(options) {
22
+ let bindings = { ...options.bindings };
23
+ let enabled = options.enabled !== false;
24
+ let context = options.context ?? null;
25
+ const onAction = options.onAction;
26
+ const sequenceTimeout = options.sequenceTimeout ?? 1e3;
27
+ let pendingSequence = "";
28
+ let sequenceTimer = null;
29
+ function getSequenceBindings() {
30
+ return Object.keys(bindings).filter((key) => key.length > 1 && !key.includes("+"));
31
+ }
32
+ function startsSequence(key) {
33
+ const sequences = getSequenceBindings();
34
+ return sequences.some((seq) => seq.startsWith(key));
35
+ }
36
+ function continuesSequence(key) {
37
+ if (!pendingSequence) return false;
38
+ const potential = pendingSequence + key;
39
+ const sequences = getSequenceBindings();
40
+ return sequences.some((seq) => seq.startsWith(potential));
41
+ }
42
+ function clearSequence() {
43
+ pendingSequence = "";
44
+ if (sequenceTimer) {
45
+ clearTimeout(sequenceTimer);
46
+ sequenceTimer = null;
47
+ }
48
+ }
49
+ function buildActionContext(key) {
50
+ const actionContext = { key };
51
+ if (context !== null) {
52
+ actionContext.context = context;
53
+ }
54
+ return actionContext;
55
+ }
56
+ function handleSequenceTimeout() {
57
+ if (pendingSequence && bindings[pendingSequence]) {
58
+ const action = bindings[pendingSequence];
59
+ if (typeof action === "function") {
60
+ action();
61
+ } else if (onAction) {
62
+ onAction(action, buildActionContext(pendingSequence));
63
+ }
64
+ }
65
+ clearSequence();
66
+ }
67
+ const manager = {
68
+ getAction(key) {
69
+ return bindings[key];
70
+ },
71
+ handleKey(key, handler) {
72
+ if (!enabled) return false;
73
+ if (key === "escape") {
74
+ if (pendingSequence) {
75
+ clearSequence();
76
+ return true;
77
+ }
78
+ }
79
+ const potentialSequence = pendingSequence + key;
80
+ if (bindings[potentialSequence]) {
81
+ clearSequence();
82
+ const action2 = bindings[potentialSequence];
83
+ if (typeof action2 === "function") {
84
+ action2();
85
+ } else {
86
+ const effectiveHandler = handler || onAction;
87
+ if (effectiveHandler) {
88
+ effectiveHandler(action2, buildActionContext(potentialSequence));
89
+ }
90
+ }
91
+ return true;
92
+ }
93
+ if (continuesSequence(key) || !pendingSequence && startsSequence(key)) {
94
+ pendingSequence = potentialSequence;
95
+ if (sequenceTimer) clearTimeout(sequenceTimer);
96
+ sequenceTimer = setTimeout(handleSequenceTimeout, sequenceTimeout);
97
+ return true;
98
+ }
99
+ if (pendingSequence) {
100
+ clearSequence();
101
+ }
102
+ const action = bindings[key];
103
+ if (action === void 0) {
104
+ return false;
105
+ }
106
+ if (typeof action === "function") {
107
+ action();
108
+ } else {
109
+ const effectiveHandler = handler || onAction;
110
+ if (effectiveHandler) {
111
+ effectiveHandler(action, buildActionContext(key));
112
+ }
113
+ }
114
+ return true;
115
+ },
116
+ addBinding(key, action) {
117
+ bindings[key] = action;
118
+ },
119
+ removeBinding(key) {
120
+ delete bindings[key];
121
+ },
122
+ setBindings(newBindings) {
123
+ bindings = { ...newBindings };
124
+ },
125
+ getBindings() {
126
+ const result = {};
127
+ for (const [key, action] of Object.entries(bindings)) {
128
+ if (typeof action === "string") {
129
+ result[key] = action;
130
+ }
131
+ }
132
+ return result;
133
+ },
134
+ disable() {
135
+ enabled = false;
136
+ },
137
+ enable() {
138
+ enabled = true;
139
+ },
140
+ toggle() {
141
+ enabled = !enabled;
142
+ },
143
+ isEnabled() {
144
+ return enabled;
145
+ },
146
+ getPendingSequence() {
147
+ return pendingSequence;
148
+ },
149
+ setContext(newContext) {
150
+ context = newContext;
151
+ },
152
+ destroy() {
153
+ if (sequenceTimer) {
154
+ clearTimeout(sequenceTimer);
155
+ sequenceTimer = null;
156
+ }
157
+ pendingSequence = "";
158
+ }
159
+ };
160
+ return manager;
161
+ }
162
+
163
+ // src/keyboard/presets.ts
164
+ var VIM_BINDINGS = {
165
+ h: "move-left",
166
+ j: "move-down",
167
+ k: "move-up",
168
+ l: "move-right",
169
+ gg: "move-first",
170
+ G: "move-last",
171
+ "/": "search",
172
+ ":": "command",
173
+ i: "insert",
174
+ enter: "select",
175
+ escape: "back",
176
+ q: "quit"
177
+ };
178
+ var ARROW_BINDINGS = {
179
+ up: "move-up",
180
+ down: "move-down",
181
+ left: "move-left",
182
+ right: "move-right"
183
+ };
184
+ var COMMON_BINDINGS = {
185
+ enter: "select",
186
+ escape: "back",
187
+ tab: "focus-next",
188
+ "shift+tab": "focus-prev",
189
+ space: "toggle"
190
+ };
191
+ var EMACS_BINDINGS = {
192
+ "ctrl+f": "move-right",
193
+ "ctrl+b": "move-left",
194
+ "ctrl+n": "move-down",
195
+ "ctrl+p": "move-up",
196
+ "ctrl+a": "move-first",
197
+ "ctrl+e": "move-last",
198
+ "ctrl+g": "back"
199
+ };
200
+
201
+ // src/keyboard/focus.ts
202
+ import React, { useState, useCallback, useRef, useEffect, useContext, createContext } from "react";
203
+
204
+ // src/types.ts
205
+ var focusIdCounter = 0;
206
+ function createFocusId(id) {
207
+ return id ?? `focus-${++focusIdCounter}`;
208
+ }
209
+ function isFocusId(value) {
210
+ return typeof value === "string" && value.length > 0;
211
+ }
212
+ function __resetFocusIdCounter() {
213
+ focusIdCounter = 0;
214
+ }
215
+
216
+ // src/keyboard/focus.ts
217
+ function useKeyboard(handler, options) {
218
+ const [enabled, setEnabled] = useState(options?.enabled !== false);
219
+ const priority = options?.priority ?? 0;
220
+ const handlerRef = useRef(handler);
221
+ handlerRef.current = handler;
222
+ const cleanup = useCallback(() => {
223
+ }, []);
224
+ useEffect(() => {
225
+ return cleanup;
226
+ }, [cleanup]);
227
+ return {
228
+ enabled,
229
+ priority,
230
+ enable: () => setEnabled(true),
231
+ disable: () => setEnabled(false),
232
+ cleanup
233
+ };
234
+ }
235
+ function useFocus(options) {
236
+ const idRef = useRef(createFocusId(options?.id));
237
+ const [localFocused, setLocalFocused] = useState(options?.autoFocus ?? false);
238
+ const tabIndex = options?.tabIndex ?? 0;
239
+ const context = useContext(FocusContext);
240
+ const id = idRef.current;
241
+ useEffect(() => {
242
+ if (context?.registerFocusable) {
243
+ context.registerFocusable(id, tabIndex);
244
+ return () => context.unregisterFocusable?.(id);
245
+ }
246
+ }, [id, tabIndex, context]);
247
+ const focused = context ? context.focusedId === id : localFocused;
248
+ const focus = useCallback(() => {
249
+ if (context) {
250
+ context.focusById(id);
251
+ } else {
252
+ setLocalFocused(true);
253
+ }
254
+ options?.onFocus?.();
255
+ }, [context, id, options]);
256
+ const blur = useCallback(() => {
257
+ if (!context) {
258
+ setLocalFocused(false);
259
+ }
260
+ options?.onBlur?.();
261
+ }, [context, options]);
262
+ const focusNext = useCallback(() => {
263
+ context?.focusNext();
264
+ }, [context]);
265
+ const focusPrev = useCallback(() => {
266
+ context?.focusPrev();
267
+ }, [context]);
268
+ const focusFirst = useCallback(() => {
269
+ context?.focusFirst();
270
+ }, [context]);
271
+ const focusLast = useCallback(() => {
272
+ context?.focusLast();
273
+ }, [context]);
274
+ return {
275
+ id,
276
+ focused,
277
+ tabIndex,
278
+ focus,
279
+ blur,
280
+ focusNext,
281
+ focusPrev,
282
+ focusFirst,
283
+ focusLast
284
+ };
285
+ }
286
+ var FocusContext = createContext(null);
287
+ function useFocusManager() {
288
+ const context = useContext(FocusContext);
289
+ if (context) {
290
+ return context;
291
+ }
292
+ return {
293
+ focusableIds: [],
294
+ focusedId: null,
295
+ isTrapped: false,
296
+ focusById: () => {
297
+ },
298
+ focusNext: () => {
299
+ },
300
+ focusPrev: () => {
301
+ },
302
+ focusFirst: () => {
303
+ },
304
+ focusLast: () => {
305
+ },
306
+ registerFocusable: () => {
307
+ },
308
+ unregisterFocusable: () => {
309
+ }
310
+ };
311
+ }
312
+ function useNavigableList(options) {
313
+ const { items, initialIndex = 0, wrap = false, useKeyboard: keyboardEnabled = false } = options;
314
+ const [currentIndex, setCurrentIndex] = useState(initialIndex);
315
+ const moveUp = useCallback(() => {
316
+ setCurrentIndex((prev) => {
317
+ if (prev <= 0) {
318
+ return wrap ? items.length - 1 : 0;
319
+ }
320
+ return prev - 1;
321
+ });
322
+ }, [items.length, wrap]);
323
+ const moveDown = useCallback(() => {
324
+ setCurrentIndex((prev) => {
325
+ if (prev >= items.length - 1) {
326
+ return wrap ? 0 : items.length - 1;
327
+ }
328
+ return prev + 1;
329
+ });
330
+ }, [items.length, wrap]);
331
+ const moveToFirst = useCallback(() => {
332
+ setCurrentIndex(0);
333
+ }, []);
334
+ const moveToLast = useCallback(() => {
335
+ setCurrentIndex(items.length - 1);
336
+ }, [items.length]);
337
+ const setIndex = useCallback(
338
+ (index) => {
339
+ if (index >= 0 && index < items.length) {
340
+ setCurrentIndex(index);
341
+ }
342
+ },
343
+ [items.length]
344
+ );
345
+ return {
346
+ currentIndex,
347
+ currentItem: items[currentIndex],
348
+ wrap,
349
+ keyboardEnabled,
350
+ moveUp,
351
+ moveDown,
352
+ moveToFirst,
353
+ moveToLast,
354
+ setIndex
355
+ };
356
+ }
357
+ function useNavigableGrid(options) {
358
+ const {
359
+ rows,
360
+ cols,
361
+ initialRow = 0,
362
+ initialCol = 0,
363
+ wrapHorizontal = false,
364
+ wrapVertical = false,
365
+ useKeyboard: keyboardEnabled = false
366
+ } = options;
367
+ const [row, setRow] = useState(initialRow);
368
+ const [col, setCol] = useState(initialCol);
369
+ const moveUp = useCallback(() => {
370
+ setRow((prev) => {
371
+ if (prev <= 0) {
372
+ return wrapVertical ? rows - 1 : 0;
373
+ }
374
+ return prev - 1;
375
+ });
376
+ }, [rows, wrapVertical]);
377
+ const moveDown = useCallback(() => {
378
+ setRow((prev) => {
379
+ if (prev >= rows - 1) {
380
+ return wrapVertical ? 0 : rows - 1;
381
+ }
382
+ return prev + 1;
383
+ });
384
+ }, [rows, wrapVertical]);
385
+ const moveLeft = useCallback(() => {
386
+ setCol((prev) => {
387
+ if (prev <= 0) {
388
+ return wrapHorizontal ? cols - 1 : 0;
389
+ }
390
+ return prev - 1;
391
+ });
392
+ }, [cols, wrapHorizontal]);
393
+ const moveRight = useCallback(() => {
394
+ setCol((prev) => {
395
+ if (prev >= cols - 1) {
396
+ return wrapHorizontal ? 0 : cols - 1;
397
+ }
398
+ return prev + 1;
399
+ });
400
+ }, [cols, wrapHorizontal]);
401
+ const moveToCell = useCallback(
402
+ (newRow, newCol) => {
403
+ if (newRow >= 0 && newRow < rows) {
404
+ setRow(newRow);
405
+ }
406
+ if (newCol >= 0 && newCol < cols) {
407
+ setCol(newCol);
408
+ }
409
+ },
410
+ [rows, cols]
411
+ );
412
+ return {
413
+ row,
414
+ col,
415
+ wrapHorizontal,
416
+ wrapVertical,
417
+ keyboardEnabled,
418
+ moveUp,
419
+ moveDown,
420
+ moveLeft,
421
+ moveRight,
422
+ moveToCell
423
+ };
424
+ }
425
+ function FocusProvider(props) {
426
+ const { children, trap = false, wrapFocus = false, initialFocus, focusFirstOnMount = false, restoreFocus = false, group } = props;
427
+ const [focusableIds, setFocusableIds] = useState([]);
428
+ const [focusedId, setFocusedId] = useState(initialFocus ?? null);
429
+ const previousFocusRef = useRef(null);
430
+ const tabIndexMapRef = useRef(/* @__PURE__ */ new Map());
431
+ useEffect(() => {
432
+ if (focusFirstOnMount && focusableIds.length > 0 && !focusedId) {
433
+ setFocusedId(focusableIds[0]);
434
+ }
435
+ }, [focusFirstOnMount, focusableIds, focusedId]);
436
+ useEffect(() => {
437
+ if (restoreFocus) {
438
+ previousFocusRef.current = focusedId;
439
+ }
440
+ return () => {
441
+ if (restoreFocus && previousFocusRef.current) {
442
+ }
443
+ };
444
+ }, [restoreFocus, focusedId]);
445
+ const focusById = useCallback((id) => {
446
+ if (focusableIds.includes(id)) {
447
+ setFocusedId(id);
448
+ }
449
+ }, [focusableIds]);
450
+ const focusNext = useCallback(() => {
451
+ if (focusableIds.length === 0) return;
452
+ const currentIndex = focusedId ? focusableIds.indexOf(focusedId) : -1;
453
+ let nextIndex = currentIndex + 1;
454
+ if (nextIndex >= focusableIds.length) {
455
+ nextIndex = wrapFocus ? 0 : focusableIds.length - 1;
456
+ }
457
+ setFocusedId(focusableIds[nextIndex]);
458
+ }, [focusableIds, focusedId, wrapFocus]);
459
+ const focusPrev = useCallback(() => {
460
+ if (focusableIds.length === 0) return;
461
+ const currentIndex = focusedId ? focusableIds.indexOf(focusedId) : focusableIds.length;
462
+ let prevIndex = currentIndex - 1;
463
+ if (prevIndex < 0) {
464
+ prevIndex = wrapFocus ? focusableIds.length - 1 : 0;
465
+ }
466
+ setFocusedId(focusableIds[prevIndex]);
467
+ }, [focusableIds, focusedId, wrapFocus]);
468
+ const focusFirst = useCallback(() => {
469
+ if (focusableIds.length > 0) {
470
+ setFocusedId(focusableIds[0]);
471
+ }
472
+ }, [focusableIds]);
473
+ const focusLast = useCallback(() => {
474
+ if (focusableIds.length > 0) {
475
+ setFocusedId(focusableIds[focusableIds.length - 1]);
476
+ }
477
+ }, [focusableIds]);
478
+ const registerFocusable = useCallback((id, tabIndex) => {
479
+ tabIndexMapRef.current.set(id, tabIndex);
480
+ setFocusableIds((prev) => {
481
+ if (prev.includes(id)) return prev;
482
+ const newIds = [...prev, id];
483
+ newIds.sort((a, b) => {
484
+ const tabA = tabIndexMapRef.current.get(a) ?? 0;
485
+ const tabB = tabIndexMapRef.current.get(b) ?? 0;
486
+ return tabA - tabB;
487
+ });
488
+ return newIds;
489
+ });
490
+ }, []);
491
+ const unregisterFocusable = useCallback((id) => {
492
+ tabIndexMapRef.current.delete(id);
493
+ setFocusableIds((prev) => prev.filter((fid) => fid !== id));
494
+ }, []);
495
+ const contextValue = {
496
+ focusableIds,
497
+ focusedId,
498
+ isTrapped: trap,
499
+ focusById,
500
+ focusNext,
501
+ focusPrev,
502
+ focusFirst,
503
+ focusLast,
504
+ registerFocusable,
505
+ unregisterFocusable
506
+ };
507
+ return React.createElement(
508
+ FocusContext.Provider,
509
+ { value: contextValue },
510
+ React.createElement(
511
+ "div",
512
+ {
513
+ "data-focus-provider": true,
514
+ "data-focus-group": group,
515
+ "data-focus-trap": trap,
516
+ ...props,
517
+ children: void 0
518
+ // Remove children from div props to avoid duplication
519
+ },
520
+ children
521
+ )
522
+ );
523
+ }
524
+
525
+ // src/keyboard/integration.ts
526
+ function normalizeReadlineKey(str, key) {
527
+ if (!key && str) {
528
+ return {
529
+ name: str,
530
+ ctrl: false,
531
+ alt: false,
532
+ shift: str !== str.toLowerCase() && str === str.toUpperCase(),
533
+ meta: false,
534
+ sequence: str
535
+ };
536
+ }
537
+ const nameMap = {
538
+ return: "enter",
539
+ escape: "escape",
540
+ up: "up",
541
+ down: "down",
542
+ left: "left",
543
+ right: "right",
544
+ tab: "tab",
545
+ backspace: "backspace",
546
+ delete: "delete",
547
+ space: "space"
548
+ };
549
+ const keyName = key?.name ?? str ?? "";
550
+ const normalizedName = nameMap[keyName.toLowerCase()] ?? keyName.toLowerCase();
551
+ return {
552
+ name: normalizedName,
553
+ ctrl: key?.ctrl ?? false,
554
+ alt: false,
555
+ // readline doesn't distinguish alt reliably
556
+ shift: key?.shift ?? false,
557
+ meta: key?.meta ?? false,
558
+ sequence: key?.sequence ?? str
559
+ };
560
+ }
561
+ function keyToBindingString(key) {
562
+ const modifiers = [];
563
+ if (key.ctrl) modifiers.push("ctrl");
564
+ if (key.alt) modifiers.push("alt");
565
+ if (key.shift) modifiers.push("shift");
566
+ if (key.meta) modifiers.push("meta");
567
+ if (modifiers.length > 0) {
568
+ return `${modifiers.join("+")}+${key.name}`;
569
+ }
570
+ return key.name;
571
+ }
572
+ function attachKeyboardManager(manager, options = {}) {
573
+ const { input = process.stdin, exitOnCtrlC = true } = options;
574
+ if (!input.isTTY) {
575
+ throw new Error("attachKeyboardManager requires a TTY stdin. Cannot attach to non-TTY input.");
576
+ }
577
+ const readline = __require("readline");
578
+ readline.emitKeypressEvents(input);
579
+ const wasRaw = input.isRaw;
580
+ input.setRawMode(true);
581
+ const handleKeypress = (str, key) => {
582
+ if (exitOnCtrlC && key?.ctrl && key?.name === "c") {
583
+ input.setRawMode(wasRaw ?? false);
584
+ process.exit(0);
585
+ }
586
+ const normalized = normalizeReadlineKey(str, key);
587
+ const bindingKey = keyToBindingString(normalized);
588
+ manager.handleKey(bindingKey);
589
+ };
590
+ input.on("keypress", handleKeypress);
591
+ input.resume();
592
+ return () => {
593
+ input.off("keypress", handleKeypress);
594
+ input.setRawMode(wasRaw ?? false);
595
+ };
596
+ }
597
+ function normalizeOpenTUIKey(event) {
598
+ const nameMap = {
599
+ return: "enter",
600
+ escape: "escape",
601
+ up: "up",
602
+ down: "down",
603
+ left: "left",
604
+ right: "right",
605
+ tab: "tab",
606
+ backspace: "backspace",
607
+ delete: "delete",
608
+ space: "space"
609
+ };
610
+ const normalizedName = nameMap[event.name.toLowerCase()] ?? event.name.toLowerCase();
611
+ return {
612
+ name: normalizedName,
613
+ ctrl: event.ctrl,
614
+ alt: event.option,
615
+ // OpenTUI uses 'option' for Alt
616
+ shift: event.shift,
617
+ meta: event.meta,
618
+ sequence: event.sequence
619
+ };
620
+ }
621
+ function createOpenTUIKeyHandler(manager) {
622
+ return (event) => {
623
+ if (event.eventType === "release") {
624
+ return;
625
+ }
626
+ const normalized = normalizeOpenTUIKey(event);
627
+ const bindingKey = keyToBindingString(normalized);
628
+ manager.handleKey(bindingKey);
629
+ };
630
+ }
631
+
632
+ export {
633
+ KEY,
634
+ matchKey,
635
+ createKeyboardManager,
636
+ VIM_BINDINGS,
637
+ ARROW_BINDINGS,
638
+ COMMON_BINDINGS,
639
+ EMACS_BINDINGS,
640
+ createFocusId,
641
+ isFocusId,
642
+ __resetFocusIdCounter,
643
+ useKeyboard,
644
+ useFocus,
645
+ FocusContext,
646
+ useFocusManager,
647
+ useNavigableList,
648
+ useNavigableGrid,
649
+ FocusProvider,
650
+ normalizeReadlineKey,
651
+ keyToBindingString,
652
+ attachKeyboardManager,
653
+ normalizeOpenTUIKey,
654
+ createOpenTUIKeyHandler
655
+ };