@cli-use/tui 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.
package/dist/index.cjs ADDED
@@ -0,0 +1,970 @@
1
+ /**
2
+ * @license MIT
3
+ * cli-use - React-based Terminal UI Framework
4
+ * Inspired by Ratatui (https://ratatui.rs)
5
+ */
6
+ "use strict";
7
+ var __create = Object.create;
8
+ var __defProp = Object.defineProperty;
9
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
+ var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __getProtoOf = Object.getPrototypeOf;
12
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
13
+ var __export = (target, all) => {
14
+ for (var name in all)
15
+ __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
+ }
23
+ return to;
24
+ };
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
+ // If the importer is in node compatibility mode or this is not an ESM
27
+ // file that has been converted to a CommonJS file using a Babel-
28
+ // compatible transform (i.e. "__esModule" has not been set), then set
29
+ // "default" to the CommonJS "module.exports" for node compatibility.
30
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
+ mod
32
+ ));
33
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
34
+
35
+ // src/index.ts
36
+ var index_exports = {};
37
+ __export(index_exports, {
38
+ ANSI: () => ANSI,
39
+ Box: () => Box,
40
+ Buffer: () => Buffer2,
41
+ Button: () => Button,
42
+ Flex: () => Flex,
43
+ Grid: () => Grid,
44
+ Input: () => Input,
45
+ Progress: () => Progress,
46
+ React: () => import_react15.default,
47
+ Renderer: () => Renderer,
48
+ Terminal: () => Terminal,
49
+ Text: () => Text,
50
+ createRoot: () => createRoot,
51
+ render: () => render,
52
+ useApp: () => useApp,
53
+ useAppState: () => useAppState,
54
+ useFocus: () => useFocus,
55
+ useInput: () => useInput,
56
+ useInterval: () => useInterval,
57
+ useKey: () => useKey,
58
+ useList: () => useList,
59
+ useStdout: () => useStdout,
60
+ useStdoutDimensions: () => useStdoutDimensions,
61
+ useTimeout: () => useTimeout
62
+ });
63
+ module.exports = __toCommonJS(index_exports);
64
+
65
+ // src/renderer/types.ts
66
+ var Buffer2 = class _Buffer {
67
+ constructor(width, height, cells = []) {
68
+ this.width = width;
69
+ this.height = height;
70
+ this.cells = cells;
71
+ this.cells = Array.from(
72
+ { length: height },
73
+ () => Array.from({ length: width }, () => ({ char: " " }))
74
+ );
75
+ }
76
+ setCell(x, y, cell) {
77
+ if (y >= 0 && y < this.height && x >= 0 && x < this.width) {
78
+ this.cells[y][x] = cell;
79
+ }
80
+ }
81
+ getCell(x, y) {
82
+ if (y >= 0 && y < this.height && x >= 0 && x < this.width) {
83
+ return this.cells[y][x];
84
+ }
85
+ return void 0;
86
+ }
87
+ clear() {
88
+ this.cells = Array.from(
89
+ { length: this.height },
90
+ () => Array.from({ length: this.width }, () => ({ char: " " }))
91
+ );
92
+ }
93
+ resize(width, height) {
94
+ const newCells = Array.from(
95
+ { length: height },
96
+ () => Array.from({ length: width }, () => ({ char: " " }))
97
+ );
98
+ for (let y = 0; y < Math.min(this.height, height); y++) {
99
+ for (let x = 0; x < Math.min(this.width, width); x++) {
100
+ newCells[y][x] = this.cells[y][x];
101
+ }
102
+ }
103
+ this.cells = newCells;
104
+ this.width = width;
105
+ this.height = height;
106
+ }
107
+ clone() {
108
+ const newBuffer = new _Buffer(this.width, this.height);
109
+ newBuffer.cells = this.cells.map((row) => row.map((cell) => ({ ...cell })));
110
+ return newBuffer;
111
+ }
112
+ };
113
+
114
+ // src/renderer/terminal.ts
115
+ var readline = __toESM(require("readline"), 1);
116
+ var ANSI = {
117
+ // Screen control
118
+ CLEAR_SCREEN: "\x1B[2J",
119
+ RESET_CURSOR: "\x1B[H",
120
+ ALTERNATE_SCREEN_ENABLE: "\x1B[?1049h",
121
+ ALTERNATE_SCREEN_DISABLE: "\x1B[?1049l",
122
+ // Cursor control
123
+ HIDE_CURSOR: "\x1B[?25l",
124
+ SHOW_CURSOR: "\x1B[?25h",
125
+ MOVE_CURSOR: (x, y) => `\x1B[${y + 1};${x + 1}H`,
126
+ // Colors
127
+ RESET_STYLE: "\x1B[0m",
128
+ FG_COLOR_256: (color) => `\x1B[38;5;${color}m`,
129
+ BG_COLOR_256: (color) => `\x1B[48;5;${color}m`,
130
+ FG_COLOR_RGB: (r, g, b) => `\x1B[38;2;${r};${g};${b}m`,
131
+ BG_COLOR_RGB: (r, g, b) => `\x1B[48;2;${r};${g};${b}m`,
132
+ // Text styles
133
+ BOLD: "\x1B[1m",
134
+ DIM: "\x1B[2m",
135
+ ITALIC: "\x1B[3m",
136
+ UNDERLINE: "\x1B[4m",
137
+ STRIKETHROUGH: "\x1B[9m",
138
+ // Reset individual styles
139
+ BOLD_OFF: "\x1B[22m",
140
+ DIM_OFF: "\x1B[22m",
141
+ ITALIC_OFF: "\x1B[23m",
142
+ UNDERLINE_OFF: "\x1B[24m",
143
+ STRIKETHROUGH_OFF: "\x1B[29m"
144
+ };
145
+ var Terminal = class {
146
+ stdin;
147
+ stdout;
148
+ _size;
149
+ rawMode = false;
150
+ alternateScreen = false;
151
+ constructor(stdin = process.stdin, stdout = process.stdout) {
152
+ this.stdin = stdin;
153
+ this.stdout = stdout;
154
+ this._size = { cols: stdout.columns || 80, rows: stdout.rows || 24 };
155
+ }
156
+ get size() {
157
+ return this._size;
158
+ }
159
+ /**
160
+ * Enable raw mode for character-by-character input
161
+ */
162
+ enableRawMode() {
163
+ if (this.rawMode) return;
164
+ this.rawMode = true;
165
+ readline.emitKeypressEvents(this.stdin);
166
+ this.stdin.setRawMode(true);
167
+ }
168
+ /**
169
+ * Disable raw mode
170
+ */
171
+ disableRawMode() {
172
+ if (!this.rawMode) return;
173
+ this.rawMode = false;
174
+ this.stdin.setRawMode(false);
175
+ }
176
+ /**
177
+ * Enable alternate screen buffer
178
+ */
179
+ enableAlternateScreen() {
180
+ if (this.alternateScreen) return;
181
+ this.alternateScreen = true;
182
+ this.stdout.write(ANSI.ALTERNATE_SCREEN_ENABLE);
183
+ }
184
+ /**
185
+ * Disable alternate screen buffer
186
+ */
187
+ disableAlternateScreen() {
188
+ if (!this.alternateScreen) return;
189
+ this.alternateScreen = false;
190
+ this.stdout.write(ANSI.ALTERNATE_SCREEN_DISABLE);
191
+ }
192
+ /**
193
+ * Hide cursor
194
+ */
195
+ hideCursor() {
196
+ this.stdout.write(ANSI.HIDE_CURSOR);
197
+ }
198
+ /**
199
+ * Show cursor
200
+ */
201
+ showCursor() {
202
+ this.stdout.write(ANSI.SHOW_CURSOR);
203
+ }
204
+ /**
205
+ * Clear the entire screen
206
+ */
207
+ clear() {
208
+ this.stdout.write(ANSI.CLEAR_SCREEN + ANSI.RESET_CURSOR);
209
+ }
210
+ /**
211
+ * Write buffer to terminal
212
+ */
213
+ write(buffer) {
214
+ let output = ANSI.RESET_STYLE;
215
+ let lastStyle = null;
216
+ for (let y = 0; y < buffer.height; y++) {
217
+ for (let x = 0; x < buffer.width; x++) {
218
+ const cell = buffer.getCell(x, y);
219
+ if (!cell) continue;
220
+ const style = this.buildStyleString(cell);
221
+ if (style !== lastStyle) {
222
+ output += style;
223
+ lastStyle = style;
224
+ }
225
+ output += cell.char;
226
+ }
227
+ output += "\r\n";
228
+ }
229
+ this.stdout.write(output);
230
+ }
231
+ /**
232
+ * Build ANSI style string from cell
233
+ */
234
+ buildStyleString(cell) {
235
+ let style = "";
236
+ if (cell.fg !== void 0) {
237
+ style += ANSI.FG_COLOR_256(cell.fg);
238
+ }
239
+ if (cell.bg !== void 0) {
240
+ style += ANSI.BG_COLOR_256(cell.bg);
241
+ }
242
+ if (cell.bold) {
243
+ style += ANSI.BOLD;
244
+ }
245
+ if (cell.dim) {
246
+ style += ANSI.DIM;
247
+ }
248
+ return style;
249
+ }
250
+ /**
251
+ * Set up SIGWINCH handler for terminal resize
252
+ */
253
+ onResize(callback) {
254
+ const handler = () => {
255
+ this._size = { cols: this.stdout.columns || 80, rows: this.stdout.rows || 24 };
256
+ callback(this._size);
257
+ };
258
+ process.on("SIGWINCH", handler);
259
+ return () => {
260
+ process.off("SIGWINCH", handler);
261
+ };
262
+ }
263
+ /**
264
+ * Set up input handler
265
+ */
266
+ onInput(callback) {
267
+ const handler = (chunk, key) => {
268
+ callback(chunk, key);
269
+ };
270
+ this.stdin.on("keypress", handler);
271
+ return () => {
272
+ this.stdin.off("keypress", handler);
273
+ };
274
+ }
275
+ /**
276
+ * Clean up terminal state
277
+ */
278
+ restore() {
279
+ this.showCursor();
280
+ this.disableAlternateScreen();
281
+ this.disableRawMode();
282
+ this.stdout.write(ANSI.RESET_STYLE);
283
+ }
284
+ };
285
+
286
+ // src/renderer/renderer.ts
287
+ var Renderer = class {
288
+ terminal;
289
+ buffer;
290
+ previousBuffer;
291
+ currentSize;
292
+ running = false;
293
+ constructor(terminal = new Terminal()) {
294
+ this.terminal = terminal;
295
+ this.currentSize = terminal.size;
296
+ }
297
+ /**
298
+ * Start the renderer
299
+ */
300
+ start() {
301
+ if (this.running) return;
302
+ this.running = true;
303
+ this.terminal.enableRawMode();
304
+ this.terminal.enableAlternateScreen();
305
+ this.terminal.hideCursor();
306
+ this.terminal.clear();
307
+ this.buffer = new Buffer2(this.currentSize.cols, this.currentSize.rows);
308
+ this.terminal.onResize((size) => {
309
+ this.currentSize = size;
310
+ this.buffer?.resize(size.cols, size.rows);
311
+ });
312
+ }
313
+ /**
314
+ * Stop the renderer
315
+ */
316
+ stop() {
317
+ if (!this.running) return;
318
+ this.running = false;
319
+ this.terminal.restore();
320
+ }
321
+ /**
322
+ * Get a writable buffer for the current frame
323
+ */
324
+ getBuffer() {
325
+ if (!this.buffer) {
326
+ throw new Error("Renderer not started");
327
+ }
328
+ return this.buffer;
329
+ }
330
+ /**
331
+ * Present the current buffer to the terminal
332
+ */
333
+ present() {
334
+ if (!this.buffer) return;
335
+ this.terminal.write(this.buffer);
336
+ this.previousBuffer = this.buffer.clone();
337
+ this.buffer.clear();
338
+ }
339
+ /**
340
+ * Get the current terminal size
341
+ */
342
+ getSize() {
343
+ return this.currentSize;
344
+ }
345
+ /**
346
+ * Get the terminal instance for direct access
347
+ */
348
+ getTerminal() {
349
+ return this.terminal;
350
+ }
351
+ };
352
+
353
+ // src/reconciler/index.ts
354
+ var import_react_reconciler = __toESM(require("react-reconciler"), 1);
355
+
356
+ // src/reconciler/host-config.ts
357
+ var hostConfig = {
358
+ supportsPersistence: false,
359
+ supportsMutation: true,
360
+ createInstance(type, props) {
361
+ return { type, props: { ...props }, children: [], parent: null, node: { type, props: { ...props }, children: [] } };
362
+ },
363
+ appendInitialChild(parentInstance, child) {
364
+ parentInstance.children.push(child);
365
+ child.parent = parentInstance;
366
+ },
367
+ finalizeInitialChildren() {
368
+ return false;
369
+ },
370
+ prepareUpdate() {
371
+ return {};
372
+ },
373
+ shouldSetTextContent(type) {
374
+ return type === "TEXT";
375
+ },
376
+ createTextInstance(text) {
377
+ return { type: "TEXT", props: { children: text }, children: [], parent: null, node: { type: "TEXT", props: { children: text }, children: [] } };
378
+ },
379
+ appendChildToContainer(container, child) {
380
+ container.root = child;
381
+ child.parent = null;
382
+ },
383
+ appendChild(parentInstance, child) {
384
+ parentInstance.children.push(child);
385
+ child.parent = parentInstance;
386
+ },
387
+ removeChild(parentInstance, child) {
388
+ const index = parentInstance.children.indexOf(child);
389
+ if (index !== -1) {
390
+ parentInstance.children.splice(index, 1);
391
+ child.parent = null;
392
+ }
393
+ },
394
+ removeChildFromContainer(_container, child) {
395
+ child.parent = null;
396
+ },
397
+ insertBefore(parentInstance, child, beforeChild) {
398
+ const index = parentInstance.children.indexOf(beforeChild);
399
+ if (index !== -1) {
400
+ parentInstance.children.splice(index, 0, child);
401
+ } else {
402
+ parentInstance.children.push(child);
403
+ }
404
+ child.parent = parentInstance;
405
+ },
406
+ insertInContainerBefore() {
407
+ },
408
+ commitUpdate(instance, _updatePayload, _type, _oldProps, newProps) {
409
+ instance.props = { ...newProps };
410
+ instance.node.props = { ...newProps };
411
+ },
412
+ commitTextUpdate(textInstance, _oldText, newText) {
413
+ textInstance.props.children = newText;
414
+ textInstance.node.props.children = newText;
415
+ },
416
+ resetTextContent(instance) {
417
+ if (instance.type === "TEXT") {
418
+ instance.props.children = "";
419
+ instance.node.props.children = "";
420
+ }
421
+ },
422
+ getPublicInstance(instance) {
423
+ return instance;
424
+ },
425
+ getRootHostContext(rootContainer) {
426
+ return rootContainer;
427
+ },
428
+ getChildHostContext(parentHostContext) {
429
+ return parentHostContext;
430
+ },
431
+ prepareForCommit() {
432
+ return null;
433
+ },
434
+ resetAfterCommit(container) {
435
+ if (container.root) {
436
+ renderToBuffer(container.root, container.buffer);
437
+ }
438
+ },
439
+ shouldAttemptEagerTransition() {
440
+ return false;
441
+ },
442
+ scheduleTimeout: setTimeout,
443
+ cancelTimeout: clearTimeout,
444
+ noTimeout: -1,
445
+ getCurrentEventPriority() {
446
+ return 0;
447
+ },
448
+ getInstanceFromNode(node) {
449
+ return node;
450
+ },
451
+ beforeActiveInstanceBlur() {
452
+ },
453
+ afterActiveInstanceBlur() {
454
+ },
455
+ preparePortalMount() {
456
+ },
457
+ prepareScopeUpdate() {
458
+ },
459
+ getInstanceFromScope() {
460
+ return null;
461
+ },
462
+ detachDeletedInstance() {
463
+ },
464
+ isPrimaryRenderer: true,
465
+ supportsHydration: false
466
+ };
467
+ function renderToBuffer(instance, buffer, x = 0, y = 0) {
468
+ if (!instance) return;
469
+ const { type, props, children } = instance;
470
+ const currentX = props.x ?? x;
471
+ const currentY = props.y ?? y;
472
+ if (type === "TEXT" || typeof props.children === "string") {
473
+ const text = String(props.children || "");
474
+ const style = props.style || {};
475
+ for (let i = 0; i < text.length; i++) {
476
+ const char = text[i];
477
+ if (char === "\n") continue;
478
+ buffer.setCell(currentX + i, currentY, { char, ...style });
479
+ }
480
+ }
481
+ if (Array.isArray(children)) {
482
+ for (const child of children) {
483
+ renderToBuffer(child, buffer, currentX, currentY);
484
+ }
485
+ } else if (children) {
486
+ renderToBuffer(children, buffer, currentX, currentY);
487
+ }
488
+ }
489
+
490
+ // src/reconciler/index.ts
491
+ var createReconciler = () => (0, import_react_reconciler.default)(hostConfig);
492
+ var createRoot = (renderer) => {
493
+ const buffer = renderer.getBuffer();
494
+ return {
495
+ buffer,
496
+ root: null,
497
+ listeners: {}
498
+ };
499
+ };
500
+ var render = (element, container, renderer) => {
501
+ const reconciler = createReconciler();
502
+ reconciler.updateContainer(element, container, null, () => {
503
+ renderer.present();
504
+ });
505
+ };
506
+
507
+ // src/components/Box.tsx
508
+ var import_react = __toESM(require("react"), 1);
509
+ var Box = ({
510
+ children,
511
+ style,
512
+ x = 0,
513
+ y = 0,
514
+ width,
515
+ height,
516
+ border = false,
517
+ flexDirection = "column",
518
+ justifyContent = "flex-start",
519
+ alignItems = "flex-start",
520
+ padding = 0
521
+ }) => {
522
+ const props = {
523
+ children,
524
+ style,
525
+ x,
526
+ y,
527
+ width,
528
+ height,
529
+ border,
530
+ flexDirection,
531
+ justifyContent,
532
+ alignItems,
533
+ padding
534
+ };
535
+ return import_react.default.createElement("BOX", props, children);
536
+ };
537
+ Box.displayName = "Box";
538
+
539
+ // src/components/Text.tsx
540
+ var import_react2 = __toESM(require("react"), 1);
541
+ var Text = ({ children, style, x = 0, y = 0 }) => {
542
+ const props = {
543
+ children: String(children),
544
+ style,
545
+ x,
546
+ y
547
+ };
548
+ return import_react2.default.createElement("TEXT", props);
549
+ };
550
+ Text.displayName = "Text";
551
+
552
+ // src/components/Flex.tsx
553
+ var import_react3 = __toESM(require("react"), 1);
554
+ var Flex = ({
555
+ children,
556
+ grow = 0,
557
+ shrink = 1,
558
+ basis = "auto",
559
+ ...boxProps
560
+ }) => {
561
+ const props = {
562
+ ...boxProps,
563
+ children
564
+ };
565
+ return import_react3.default.createElement("FLEX", props);
566
+ };
567
+ Flex.displayName = "Flex";
568
+
569
+ // src/components/Grid.tsx
570
+ var import_react4 = __toESM(require("react"), 1);
571
+ var Grid = ({
572
+ children,
573
+ columns = 2,
574
+ rows,
575
+ gap = 1,
576
+ style,
577
+ x = 0,
578
+ y = 0,
579
+ width,
580
+ height
581
+ }) => {
582
+ const props = {
583
+ children,
584
+ columns,
585
+ rows,
586
+ gap,
587
+ style,
588
+ x,
589
+ y,
590
+ width,
591
+ height
592
+ };
593
+ return import_react4.default.createElement("GRID", props, children);
594
+ };
595
+ Grid.displayName = "Grid";
596
+
597
+ // src/components/Button.tsx
598
+ var import_react5 = __toESM(require("react"), 1);
599
+ var Button = ({
600
+ children,
601
+ onClick,
602
+ focused = false,
603
+ style,
604
+ x = 0,
605
+ y = 0
606
+ }) => {
607
+ const activeStyle = focused ? {
608
+ ...style,
609
+ fg: style?.focusedFg || style?.fg || 0,
610
+ bg: style?.focusedBg || style?.bg || 7
611
+ } : style;
612
+ const props = {
613
+ children: `[ ${children} ]`,
614
+ onClick,
615
+ focused,
616
+ style: activeStyle,
617
+ x,
618
+ y
619
+ };
620
+ return import_react5.default.createElement("BUTTON", props);
621
+ };
622
+ Button.displayName = "Button";
623
+
624
+ // src/components/Input.tsx
625
+ var import_react6 = __toESM(require("react"), 1);
626
+ var Input = ({
627
+ value,
628
+ onChange,
629
+ placeholder = "",
630
+ focused = false,
631
+ mask = false,
632
+ style,
633
+ x = 0,
634
+ y = 0,
635
+ width = 20,
636
+ maxLength
637
+ }) => {
638
+ const displayValue = mask ? "*".repeat(value.length) : value;
639
+ const displayText = value.length > 0 ? displayValue : focused ? placeholder : "";
640
+ const props = {
641
+ value: displayText,
642
+ onChange,
643
+ placeholder,
644
+ focused,
645
+ mask,
646
+ style: focused ? style : {
647
+ ...style,
648
+ fg: style?.placeholderFg || 8
649
+ },
650
+ x,
651
+ y,
652
+ width,
653
+ maxLength
654
+ };
655
+ return import_react6.default.createElement("INPUT", props);
656
+ };
657
+ Input.displayName = "Input";
658
+
659
+ // src/components/Progress.tsx
660
+ var import_react7 = __toESM(require("react"), 1);
661
+ var Progress = ({
662
+ value,
663
+ max = 100,
664
+ style,
665
+ x = 0,
666
+ y = 0,
667
+ width = 20,
668
+ label
669
+ }) => {
670
+ const percentage = Math.min(Math.max(value / max, 0), 1);
671
+ const filledWidth = Math.floor(percentage * width);
672
+ const props = {
673
+ value,
674
+ max,
675
+ style,
676
+ x,
677
+ y,
678
+ width,
679
+ label
680
+ };
681
+ return import_react7.default.createElement("PROGRESS", props);
682
+ };
683
+ Progress.displayName = "Progress";
684
+
685
+ // src/hooks/useApp.ts
686
+ var import_react8 = require("react");
687
+ var useApp = (options = {}) => {
688
+ const rendererRef = (0, import_react8.useRef)(null);
689
+ const [size, setSize] = (0, import_react8.useState)({ cols: 80, rows: 24 });
690
+ const [running, setRunning] = (0, import_react8.useState)(false);
691
+ (0, import_react8.useEffect)(() => {
692
+ const cleanupResize = () => {
693
+ };
694
+ setRunning(true);
695
+ return () => {
696
+ setRunning(false);
697
+ cleanupResize();
698
+ };
699
+ }, []);
700
+ const exit = () => {
701
+ if (rendererRef.current) {
702
+ setRunning(false);
703
+ }
704
+ };
705
+ return {
706
+ renderer: rendererRef.current,
707
+ size,
708
+ running,
709
+ exit
710
+ };
711
+ };
712
+
713
+ // src/hooks/useInput.ts
714
+ var import_react9 = require("react");
715
+ var useInput = (callback, deps = []) => {
716
+ (0, import_react9.useEffect)(() => {
717
+ const readline2 = require("readline");
718
+ const handler = (chunk, key) => {
719
+ if (!key) return;
720
+ callback({
721
+ key: key.name || "",
722
+ name: key.name || "",
723
+ ctrl: key.ctrl || false,
724
+ meta: key.meta || false,
725
+ shift: key.shift || false,
726
+ sequence: key.sequence || ""
727
+ });
728
+ };
729
+ process.stdin.setRawMode(true);
730
+ readline2.emitKeypressEvents(process.stdin);
731
+ process.stdin.on("keypress", handler);
732
+ return () => {
733
+ process.stdin.off("keypress", handler);
734
+ };
735
+ }, [callback, ...deps]);
736
+ };
737
+ var useKey = (keyName, callback, deps = []) => {
738
+ const keys = Array.isArray(keyName) ? keyName : [keyName];
739
+ useInput(
740
+ (0, import_react9.useCallback)(
741
+ ({ key, ctrl, meta }) => {
742
+ if (ctrl || meta) return;
743
+ if (keys.includes(key)) {
744
+ callback();
745
+ }
746
+ },
747
+ [callback, keys]
748
+ ),
749
+ deps
750
+ );
751
+ };
752
+
753
+ // src/hooks/useFocus.ts
754
+ var import_react10 = require("react");
755
+ var useFocus = (initialFocus = 0, itemCount) => {
756
+ const [focusedIndex, setFocusedIndex] = (0, import_react10.useState)(initialFocus);
757
+ const focusableRefs = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
758
+ const focusNext = (0, import_react10.useCallback)(() => {
759
+ setFocusedIndex((current) => (current + 1) % itemCount);
760
+ }, [itemCount]);
761
+ const focusPrevious = (0, import_react10.useCallback)(() => {
762
+ setFocusedIndex((current) => (current - 1 + itemCount) % itemCount);
763
+ }, [itemCount]);
764
+ const setFocus = (0, import_react10.useCallback)((index) => {
765
+ if (index >= 0 && index < itemCount) {
766
+ setFocusedIndex(index);
767
+ }
768
+ }, [itemCount]);
769
+ const isFocused = (0, import_react10.useCallback)(
770
+ (index) => index === focusedIndex,
771
+ [focusedIndex]
772
+ );
773
+ (0, import_react10.useEffect)(() => {
774
+ const readline2 = require("readline");
775
+ readline2.emitKeypressEvents(process.stdin);
776
+ process.stdin.setRawMode(true);
777
+ const handleKeyPress = (_chunk, key) => {
778
+ if (key.name === "tab" || key.name === "right") {
779
+ focusNext();
780
+ } else if (key.name === "left") {
781
+ focusPrevious();
782
+ }
783
+ };
784
+ process.stdin.on("keypress", handleKeyPress);
785
+ return () => {
786
+ process.stdin.off("keypress", handleKeyPress);
787
+ };
788
+ }, [focusNext, focusPrevious]);
789
+ return {
790
+ focusedIndex,
791
+ focusNext,
792
+ focusPrevious,
793
+ setFocus,
794
+ isFocused
795
+ };
796
+ };
797
+
798
+ // src/hooks/useStdout.ts
799
+ var import_react11 = require("react");
800
+ var useStdoutDimensions = () => {
801
+ const [dimensions, setDimensions] = (0, import_react11.useState)(() => ({
802
+ columns: process.stdout.columns || 80,
803
+ rows: process.stdout.rows || 24
804
+ }));
805
+ (0, import_react11.useEffect)(() => {
806
+ const handleResize = () => {
807
+ setDimensions({
808
+ columns: process.stdout.columns || 80,
809
+ rows: process.stdout.rows || 24
810
+ });
811
+ };
812
+ process.stdout.on("resize", handleResize);
813
+ return () => {
814
+ process.stdout.off("resize", handleResize);
815
+ };
816
+ }, []);
817
+ return dimensions;
818
+ };
819
+ var useStdout = () => {
820
+ const write = (0, import_react11.useCallback)((data) => {
821
+ process.stdout.write(data);
822
+ }, []);
823
+ return { write };
824
+ };
825
+
826
+ // src/hooks/useInterval.ts
827
+ var import_react12 = require("react");
828
+ var useInterval = (callback, delay) => {
829
+ const savedCallback = (0, import_react12.useRef)(callback);
830
+ (0, import_react12.useEffect)(() => {
831
+ savedCallback.current = callback;
832
+ }, [callback]);
833
+ (0, import_react12.useEffect)(() => {
834
+ if (delay === null) return;
835
+ const tick = () => savedCallback.current();
836
+ const id = setInterval(tick, delay);
837
+ return () => clearInterval(id);
838
+ }, [delay]);
839
+ };
840
+ var useTimeout = (callback, delay) => {
841
+ const savedCallback = (0, import_react12.useRef)(callback);
842
+ (0, import_react12.useEffect)(() => {
843
+ savedCallback.current = callback;
844
+ }, [callback]);
845
+ (0, import_react12.useEffect)(() => {
846
+ if (delay === null) return;
847
+ const tick = () => savedCallback.current();
848
+ const id = setTimeout(tick, delay);
849
+ return () => clearTimeout(id);
850
+ }, [delay]);
851
+ };
852
+
853
+ // src/hooks/useAppState.ts
854
+ var import_react13 = require("react");
855
+ var useAppState = (initialState = "idle") => {
856
+ const [state, setState] = (0, import_react13.useState)(initialState);
857
+ const [error, setError] = (0, import_react13.useState)(null);
858
+ const setLoading = (0, import_react13.useCallback)(() => {
859
+ setState("loading");
860
+ setError(null);
861
+ }, []);
862
+ const setSuccess = (0, import_react13.useCallback)(() => {
863
+ setState("success");
864
+ setError(null);
865
+ }, []);
866
+ const setErrorState = (0, import_react13.useCallback)((err) => {
867
+ setState("error");
868
+ setError(err);
869
+ }, []);
870
+ const setIdle = (0, import_react13.useCallback)(() => {
871
+ setState("idle");
872
+ setError(null);
873
+ }, []);
874
+ const isLoading = state === "loading";
875
+ const isSuccess = state === "success";
876
+ const isError = state === "error";
877
+ const isIdle = state === "idle";
878
+ return {
879
+ state,
880
+ error,
881
+ isLoading,
882
+ isSuccess,
883
+ isError,
884
+ isIdle,
885
+ setLoading,
886
+ setSuccess,
887
+ setError: setErrorState,
888
+ setIdle
889
+ };
890
+ };
891
+
892
+ // src/hooks/useList.ts
893
+ var import_react14 = require("react");
894
+ var useList = ({
895
+ initialItems = [],
896
+ initialIndex = 0,
897
+ loop = true
898
+ } = {}) => {
899
+ const [items, setItems] = (0, import_react14.useState)(initialItems);
900
+ const [index, setIndex] = (0, import_react14.useState)(initialIndex);
901
+ const next = (0, import_react14.useCallback)(() => {
902
+ setIndex((current) => {
903
+ if (items.length === 0) return 0;
904
+ const nextIndex = current + 1;
905
+ if (nextIndex >= items.length) {
906
+ return loop ? 0 : items.length - 1;
907
+ }
908
+ return nextIndex;
909
+ });
910
+ }, [items.length, loop]);
911
+ const previous = (0, import_react14.useCallback)(() => {
912
+ setIndex((current) => {
913
+ if (items.length === 0) return 0;
914
+ const prevIndex = current - 1;
915
+ if (prevIndex < 0) {
916
+ return loop ? items.length - 1 : 0;
917
+ }
918
+ return prevIndex;
919
+ });
920
+ }, [items.length, loop]);
921
+ const select = (0, import_react14.useCallback)((item) => {
922
+ const newIndex = items.indexOf(item);
923
+ if (newIndex !== -1) {
924
+ setIndex(newIndex);
925
+ }
926
+ }, [items]);
927
+ const selectedIndex = index;
928
+ const selectedItem = items[index] ?? null;
929
+ return {
930
+ items,
931
+ setItems,
932
+ index: selectedIndex,
933
+ setIndex,
934
+ selectedItem,
935
+ next,
936
+ previous,
937
+ select
938
+ };
939
+ };
940
+
941
+ // src/index.ts
942
+ var import_react15 = __toESM(require("react"), 1);
943
+ // Annotate the CommonJS export names for ESM import in node:
944
+ 0 && (module.exports = {
945
+ ANSI,
946
+ Box,
947
+ Buffer,
948
+ Button,
949
+ Flex,
950
+ Grid,
951
+ Input,
952
+ Progress,
953
+ React,
954
+ Renderer,
955
+ Terminal,
956
+ Text,
957
+ createRoot,
958
+ render,
959
+ useApp,
960
+ useAppState,
961
+ useFocus,
962
+ useInput,
963
+ useInterval,
964
+ useKey,
965
+ useList,
966
+ useStdout,
967
+ useStdoutDimensions,
968
+ useTimeout
969
+ });
970
+ //# sourceMappingURL=index.cjs.map