@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/LICENSE +21 -0
- package/README.md +125 -0
- package/dist/cli/index.cjs +549 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +526 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/hooks/index.cjs +309 -0
- package/dist/hooks/index.cjs.map +1 -0
- package/dist/hooks/index.d.cts +4 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.js +280 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index-DAO84gkm.d.cts +272 -0
- package/dist/index-DAO84gkm.d.ts +272 -0
- package/dist/index.cjs +970 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +181 -0
- package/dist/index.d.ts +181 -0
- package/dist/index.js +917 -0
- package/dist/index.js.map +1 -0
- package/package.json +99 -0
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
|