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