@vertz/tui 0.2.3
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/README.md +124 -0
- package/dist/index.d.ts +720 -0
- package/dist/index.js +2374 -0
- package/dist/jsx-runtime/index.d.ts +111 -0
- package/dist/jsx-runtime/index.js +134 -0
- package/dist/shared/chunk-5x9fb9b2.js +52 -0
- package/dist/shared/chunk-6y95cgnx.js +137 -0
- package/dist/test/index.d.ts +109 -0
- package/dist/test/index.js +40 -0
- package/package.json +61 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
/** Style attributes for a single terminal cell. */
|
|
2
|
+
interface CellStyle {
|
|
3
|
+
color?: string;
|
|
4
|
+
bgColor?: string;
|
|
5
|
+
bold?: boolean;
|
|
6
|
+
dim?: boolean;
|
|
7
|
+
italic?: boolean;
|
|
8
|
+
underline?: boolean;
|
|
9
|
+
strikethrough?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** A single terminal cell: one character with style. */
|
|
12
|
+
interface Cell {
|
|
13
|
+
char: string;
|
|
14
|
+
width: number;
|
|
15
|
+
style: CellStyle;
|
|
16
|
+
}
|
|
17
|
+
/** Computed layout box — the result of layout computation. */
|
|
18
|
+
interface LayoutBox {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
}
|
|
24
|
+
/** Border style options. */
|
|
25
|
+
type BorderStyle = "single" | "double" | "round" | "bold" | "none";
|
|
26
|
+
/** Layout properties for a node. */
|
|
27
|
+
interface LayoutProps {
|
|
28
|
+
direction: "row" | "column";
|
|
29
|
+
padding: number;
|
|
30
|
+
paddingX: number;
|
|
31
|
+
paddingY: number;
|
|
32
|
+
gap: number;
|
|
33
|
+
width: number | "full" | undefined;
|
|
34
|
+
height: number | undefined;
|
|
35
|
+
grow: number;
|
|
36
|
+
align: "start" | "center" | "end";
|
|
37
|
+
justify: "start" | "center" | "end" | "between";
|
|
38
|
+
border: BorderStyle;
|
|
39
|
+
}
|
|
40
|
+
/** A persistent TUI element (like <Box> or <Text>). Built once, mutated by effects. */
|
|
41
|
+
interface TuiElement {
|
|
42
|
+
_tuiElement: true;
|
|
43
|
+
tag: string;
|
|
44
|
+
props: Record<string, unknown>;
|
|
45
|
+
style: CellStyle;
|
|
46
|
+
layoutProps: LayoutProps;
|
|
47
|
+
children: TuiChild[];
|
|
48
|
+
parent: TuiElement | null;
|
|
49
|
+
dirty: boolean;
|
|
50
|
+
/** Computed layout box. */
|
|
51
|
+
box: LayoutBox;
|
|
52
|
+
}
|
|
53
|
+
/** A persistent text node. Updated in-place by reactive effects. */
|
|
54
|
+
interface TuiTextNode {
|
|
55
|
+
_tuiText: true;
|
|
56
|
+
text: string;
|
|
57
|
+
style: CellStyle;
|
|
58
|
+
dirty: boolean;
|
|
59
|
+
box: LayoutBox;
|
|
60
|
+
}
|
|
61
|
+
/** A conditional node that swaps between branches. */
|
|
62
|
+
interface TuiConditionalNode {
|
|
63
|
+
_tuiConditional: true;
|
|
64
|
+
current: TuiElement | TuiTextNode | null;
|
|
65
|
+
dirty: boolean;
|
|
66
|
+
}
|
|
67
|
+
/** A list node that manages keyed items. */
|
|
68
|
+
interface TuiListNode {
|
|
69
|
+
_tuiList: true;
|
|
70
|
+
items: TuiElement[];
|
|
71
|
+
dirty: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** Any child in a persistent TUI tree. */
|
|
74
|
+
type TuiChild = TuiElement | TuiTextNode | TuiConditionalNode | TuiListNode;
|
|
75
|
+
/** Any renderable TUI content. */
|
|
76
|
+
type TuiNode = TuiElement2 | TuiTextNode2 | TuiConditionalNode | TuiListNode | null | undefined | false | TuiNode[];
|
|
77
|
+
/** A TUI element (like <Box> or <Text>). */
|
|
78
|
+
interface TuiElement2 {
|
|
79
|
+
_tuiElement: true;
|
|
80
|
+
tag: string;
|
|
81
|
+
props: Record<string, unknown>;
|
|
82
|
+
style: CellStyle;
|
|
83
|
+
layoutProps: LayoutProps;
|
|
84
|
+
children: TuiNode[];
|
|
85
|
+
/** Computed layout box. */
|
|
86
|
+
box: LayoutBox;
|
|
87
|
+
/** Component function, if this is a component element. */
|
|
88
|
+
component?: (props: Record<string, unknown>) => TuiNode;
|
|
89
|
+
}
|
|
90
|
+
/** A text node (raw string or number). */
|
|
91
|
+
interface TuiTextNode2 {
|
|
92
|
+
_tuiText: true;
|
|
93
|
+
text: string;
|
|
94
|
+
style: CellStyle;
|
|
95
|
+
box: LayoutBox;
|
|
96
|
+
}
|
|
97
|
+
/** Color type for Text components. */
|
|
98
|
+
type Color = "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright" | `#${string}`;
|
|
99
|
+
import { Computed, DisposeFn as DisposeFn2, ReadonlySignal, Signal as Signal3 } from "@vertz/ui";
|
|
100
|
+
import { batch, computed, createContext, onMount, signal, useContext } from "@vertz/ui";
|
|
101
|
+
/**
|
|
102
|
+
* Output adapter interface. Abstracts stdout for testing.
|
|
103
|
+
*/
|
|
104
|
+
interface OutputAdapter {
|
|
105
|
+
/** Write a string to the output. */
|
|
106
|
+
write(data: string): void;
|
|
107
|
+
/** Get terminal width in columns. */
|
|
108
|
+
readonly columns: number;
|
|
109
|
+
/** Get terminal height in rows. */
|
|
110
|
+
readonly rows: number;
|
|
111
|
+
}
|
|
112
|
+
/** A region of cells that changed between two buffers. */
|
|
113
|
+
interface DirtyRegion {
|
|
114
|
+
row: number;
|
|
115
|
+
col: number;
|
|
116
|
+
cells: Cell[];
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 2D grid of cells representing the terminal screen.
|
|
120
|
+
* Double-buffered: write to current, diff against previous.
|
|
121
|
+
*/
|
|
122
|
+
declare class TerminalBuffer {
|
|
123
|
+
readonly width: number;
|
|
124
|
+
readonly height: number;
|
|
125
|
+
private _cells;
|
|
126
|
+
constructor(width: number, height: number);
|
|
127
|
+
private static _createGrid;
|
|
128
|
+
/** Get a cell at the given position. Returns undefined if out of bounds. */
|
|
129
|
+
get(row: number, col: number): Cell | undefined;
|
|
130
|
+
/** Set a cell at the given position. No-op if out of bounds. */
|
|
131
|
+
set(row: number, col: number, char: string, style: CellStyle): void;
|
|
132
|
+
/** Write a string starting at the given position. */
|
|
133
|
+
writeString(row: number, col: number, text: string, style: CellStyle): void;
|
|
134
|
+
/** Fill the entire buffer with empty cells. */
|
|
135
|
+
clear(): void;
|
|
136
|
+
/**
|
|
137
|
+
* Diff this buffer against another, returning regions that differ.
|
|
138
|
+
* Each dirty region is a contiguous run of changed cells on one row.
|
|
139
|
+
*/
|
|
140
|
+
diff(previous: TerminalBuffer): DirtyRegion[];
|
|
141
|
+
/** Create a deep copy of this buffer. */
|
|
142
|
+
clone(): TerminalBuffer;
|
|
143
|
+
/** Get the text content of a specific row (no styling). */
|
|
144
|
+
getRowText(row: number): string;
|
|
145
|
+
/** Get all text content (no styling), rows joined by newlines. */
|
|
146
|
+
getText(): string;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Test output adapter that captures rendered output for assertions.
|
|
150
|
+
* Instead of writing to stdout, maintains a TerminalBuffer that can be inspected.
|
|
151
|
+
*/
|
|
152
|
+
declare class TestAdapter implements OutputAdapter {
|
|
153
|
+
readonly columns: number;
|
|
154
|
+
readonly rows: number;
|
|
155
|
+
/** The current terminal buffer (public for test inspection). */
|
|
156
|
+
buffer: TerminalBuffer;
|
|
157
|
+
/** Raw ANSI output captured from write calls. */
|
|
158
|
+
rawOutput: string[];
|
|
159
|
+
constructor(columns?: number, rows?: number);
|
|
160
|
+
write(data: string): void;
|
|
161
|
+
/** Get the text content of a specific row. */
|
|
162
|
+
textAt(row: number): string;
|
|
163
|
+
/** Get all text content, rows joined by newlines. */
|
|
164
|
+
text(): string;
|
|
165
|
+
/** Reset captured output. */
|
|
166
|
+
reset(): void;
|
|
167
|
+
}
|
|
168
|
+
/** Parsed key event from terminal stdin. */
|
|
169
|
+
interface KeyEvent {
|
|
170
|
+
/** Key name: 'a', 'return', 'up', 'down', 'tab', 'escape', 'space', etc. */
|
|
171
|
+
name: string;
|
|
172
|
+
/** Printable character or empty string. */
|
|
173
|
+
char: string;
|
|
174
|
+
ctrl: boolean;
|
|
175
|
+
shift: boolean;
|
|
176
|
+
meta: boolean;
|
|
177
|
+
}
|
|
178
|
+
type KeyListener = (key: KeyEvent) => void;
|
|
179
|
+
/**
|
|
180
|
+
* Test stdin that allows programmatic key injection for testing.
|
|
181
|
+
*/
|
|
182
|
+
declare class TestStdin {
|
|
183
|
+
private _listeners;
|
|
184
|
+
/** Register a key listener. Returns a cleanup function. */
|
|
185
|
+
onKey(listener: KeyListener): () => void;
|
|
186
|
+
/** Simulate a key press. */
|
|
187
|
+
pressKey(name: string, options?: Partial<Omit<KeyEvent, "name">>): void;
|
|
188
|
+
/** Simulate typing a string character by character. */
|
|
189
|
+
type(text: string): void;
|
|
190
|
+
/** Remove all listeners. */
|
|
191
|
+
dispose(): void;
|
|
192
|
+
}
|
|
193
|
+
interface TuiMountOptions {
|
|
194
|
+
mode?: "inline" | "fullscreen" | "alternate";
|
|
195
|
+
stdout?: NodeJS.WriteStream;
|
|
196
|
+
stdin?: NodeJS.ReadStream;
|
|
197
|
+
/** Test adapter for headless rendering. */
|
|
198
|
+
adapter?: TestAdapter;
|
|
199
|
+
/** Test stdin for key injection. */
|
|
200
|
+
testStdin?: TestStdin;
|
|
201
|
+
}
|
|
202
|
+
interface TuiHandle {
|
|
203
|
+
unmount(): void;
|
|
204
|
+
waitUntilExit(): Promise<void>;
|
|
205
|
+
/** The output adapter (for testing). */
|
|
206
|
+
output: OutputAdapter & {
|
|
207
|
+
textAt?(row: number): string;
|
|
208
|
+
text?(): string;
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Mount a TUI application.
|
|
213
|
+
*
|
|
214
|
+
* The component function is called ONCE to build the persistent tree.
|
|
215
|
+
* Reactive updates happen through effects created by the internals
|
|
216
|
+
* (__child, __attr, __conditional, __list), which call scheduleRender()
|
|
217
|
+
* to trigger re-renders.
|
|
218
|
+
*
|
|
219
|
+
* For backward compatibility, old-style components (using useComponentState
|
|
220
|
+
* and jsx()) are still supported via an effect wrapper.
|
|
221
|
+
*/
|
|
222
|
+
declare function mount(app: () => TuiNode, options?: TuiMountOptions): TuiHandle;
|
|
223
|
+
/** Exit the current TUI app. */
|
|
224
|
+
declare function exit(): void;
|
|
225
|
+
declare const tui: {
|
|
226
|
+
mount: typeof mount;
|
|
227
|
+
exit: typeof exit;
|
|
228
|
+
};
|
|
229
|
+
/** Configuration for the device code auth flow. */
|
|
230
|
+
interface AuthConfig {
|
|
231
|
+
/** OAuth client ID. */
|
|
232
|
+
clientId: string;
|
|
233
|
+
/** URL for the device authorization endpoint (POST). */
|
|
234
|
+
deviceCodeUrl: string;
|
|
235
|
+
/** URL for the token endpoint (POST). */
|
|
236
|
+
tokenUrl: string;
|
|
237
|
+
/** OAuth scopes to request. */
|
|
238
|
+
scopes?: string[];
|
|
239
|
+
/** Callback invoked when tokens are successfully obtained. */
|
|
240
|
+
onTokens?: (tokens: AuthTokens) => void | Promise<void>;
|
|
241
|
+
/** Custom fetch function. Defaults to globalThis.fetch. */
|
|
242
|
+
fetcher?: (url: string, init: RequestInit) => Promise<Response>;
|
|
243
|
+
/** Title displayed in the auth box. Defaults to "Authenticate". */
|
|
244
|
+
title?: string;
|
|
245
|
+
}
|
|
246
|
+
/** RFC 8628 device authorization response. */
|
|
247
|
+
interface DeviceCodeResponse {
|
|
248
|
+
device_code: string;
|
|
249
|
+
user_code: string;
|
|
250
|
+
verification_uri: string;
|
|
251
|
+
verification_uri_complete?: string;
|
|
252
|
+
expires_in: number;
|
|
253
|
+
interval: number;
|
|
254
|
+
}
|
|
255
|
+
/** OAuth 2.0 token response. */
|
|
256
|
+
interface TokenResponse {
|
|
257
|
+
access_token: string;
|
|
258
|
+
token_type: string;
|
|
259
|
+
expires_in: number;
|
|
260
|
+
refresh_token?: string;
|
|
261
|
+
scope?: string;
|
|
262
|
+
}
|
|
263
|
+
/** Normalized tokens returned to the consumer. */
|
|
264
|
+
interface AuthTokens {
|
|
265
|
+
accessToken: string;
|
|
266
|
+
refreshToken?: string;
|
|
267
|
+
expiresIn: number;
|
|
268
|
+
}
|
|
269
|
+
/** Status of the device code flow. */
|
|
270
|
+
type AuthStatus = "idle" | "requesting-code" | "awaiting-approval" | "polling" | "success" | "error" | "expired" | "denied" | "cancelled";
|
|
271
|
+
/** Error thrown when the user denies authorization. */
|
|
272
|
+
declare class AuthDeniedError extends Error {
|
|
273
|
+
constructor();
|
|
274
|
+
}
|
|
275
|
+
/** Error thrown when the device code expires before approval. */
|
|
276
|
+
declare class AuthExpiredError extends Error {
|
|
277
|
+
constructor();
|
|
278
|
+
}
|
|
279
|
+
/** Error thrown when the user cancels the flow (Esc key). */
|
|
280
|
+
declare class AuthCancelledError extends Error {
|
|
281
|
+
constructor();
|
|
282
|
+
}
|
|
283
|
+
interface DeviceCodeAuthOptions extends AuthConfig {
|
|
284
|
+
/** Internal: test mount options. */
|
|
285
|
+
_mountOptions?: TuiMountOptions;
|
|
286
|
+
}
|
|
287
|
+
/** High-level imperative device code auth flow. */
|
|
288
|
+
declare function DeviceCodeAuth(config: DeviceCodeAuthOptions): Promise<AuthTokens>;
|
|
289
|
+
/** Request a device code from the authorization server. */
|
|
290
|
+
declare function requestDeviceCode(config: AuthConfig): Promise<DeviceCodeResponse>;
|
|
291
|
+
/**
|
|
292
|
+
* Poll the token endpoint until authorization completes, is denied, or expires.
|
|
293
|
+
* Handles `authorization_pending`, `slow_down`, `access_denied`, and `expired_token`.
|
|
294
|
+
*/
|
|
295
|
+
declare function pollTokenUntilComplete(config: AuthConfig, deviceCode: DeviceCodeResponse, signal?: AbortSignal): Promise<AuthTokens>;
|
|
296
|
+
interface BannerProps {
|
|
297
|
+
title: string;
|
|
298
|
+
subtitle?: string;
|
|
299
|
+
border?: "single" | "double" | "round" | "bold";
|
|
300
|
+
titleColor?: string;
|
|
301
|
+
}
|
|
302
|
+
declare function Banner({ title, subtitle, border, titleColor }: BannerProps): TuiElement;
|
|
303
|
+
interface BoxProps {
|
|
304
|
+
direction?: "row" | "column";
|
|
305
|
+
padding?: number;
|
|
306
|
+
paddingX?: number;
|
|
307
|
+
paddingY?: number;
|
|
308
|
+
gap?: number;
|
|
309
|
+
width?: number | "full";
|
|
310
|
+
height?: number;
|
|
311
|
+
grow?: number;
|
|
312
|
+
align?: "start" | "center" | "end";
|
|
313
|
+
justify?: "start" | "center" | "end" | "between";
|
|
314
|
+
border?: BorderStyle;
|
|
315
|
+
borderColor?: Color;
|
|
316
|
+
children?: TuiNode;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Box — the only layout container.
|
|
320
|
+
* Direction is a prop, not separate Row/Column components.
|
|
321
|
+
*/
|
|
322
|
+
declare function Box(_props: BoxProps): TuiNode;
|
|
323
|
+
interface ConfirmProps {
|
|
324
|
+
message: string;
|
|
325
|
+
onSubmit?: (confirmed: boolean) => void;
|
|
326
|
+
}
|
|
327
|
+
declare function Confirm(props: ConfirmProps): TuiElement;
|
|
328
|
+
interface DashboardProps {
|
|
329
|
+
/** Fixed element at the top of the dashboard. */
|
|
330
|
+
header?: TuiElement;
|
|
331
|
+
/** Fixed element at the bottom of the dashboard. */
|
|
332
|
+
footer?: TuiElement;
|
|
333
|
+
/** Content to display in the middle (scrollable area). */
|
|
334
|
+
children?: TuiChild;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Dashboard — three-region layout for long-running CLI tools.
|
|
338
|
+
*
|
|
339
|
+
* Divides the terminal into:
|
|
340
|
+
* - Header (fixed at top, 1 row minimum)
|
|
341
|
+
* - Content (fills remaining space, grows to fit)
|
|
342
|
+
* - Footer (fixed at bottom, 1 row minimum)
|
|
343
|
+
*
|
|
344
|
+
* Use with `tui.mount(App, { mode: 'alternate' })` for clean restore on exit.
|
|
345
|
+
*/
|
|
346
|
+
declare function Dashboard({ header, footer, children }: DashboardProps): TuiElement;
|
|
347
|
+
interface SourceLine {
|
|
348
|
+
number: number;
|
|
349
|
+
text: string;
|
|
350
|
+
}
|
|
351
|
+
interface DiagnosticItem {
|
|
352
|
+
severity: "error" | "warning" | "info";
|
|
353
|
+
code: string;
|
|
354
|
+
message: string;
|
|
355
|
+
file?: string;
|
|
356
|
+
line?: number;
|
|
357
|
+
column?: number;
|
|
358
|
+
sourceLines?: SourceLine[];
|
|
359
|
+
highlightStart?: number;
|
|
360
|
+
highlightLength?: number;
|
|
361
|
+
suggestion?: string;
|
|
362
|
+
}
|
|
363
|
+
interface DiagnosticViewProps {
|
|
364
|
+
diagnostics: DiagnosticItem[];
|
|
365
|
+
showSource?: boolean;
|
|
366
|
+
showSuggestions?: boolean;
|
|
367
|
+
}
|
|
368
|
+
declare function DiagnosticView({ diagnostics, showSource, showSuggestions }: DiagnosticViewProps): TuiElement;
|
|
369
|
+
interface DividerProps {
|
|
370
|
+
label?: string;
|
|
371
|
+
char?: string;
|
|
372
|
+
width?: number;
|
|
373
|
+
color?: string;
|
|
374
|
+
}
|
|
375
|
+
declare function Divider({ label, char, width, color }: DividerProps): TuiElement;
|
|
376
|
+
interface KeyValueEntry {
|
|
377
|
+
key: string;
|
|
378
|
+
value: string;
|
|
379
|
+
}
|
|
380
|
+
interface KeyValueProps {
|
|
381
|
+
entries: KeyValueEntry[];
|
|
382
|
+
separator?: string;
|
|
383
|
+
keyBold?: boolean;
|
|
384
|
+
}
|
|
385
|
+
declare function KeyValue({ entries, separator, keyBold }: KeyValueProps): TuiElement;
|
|
386
|
+
interface LogProps<T> {
|
|
387
|
+
items: T[];
|
|
388
|
+
children: (item: T) => TuiChild;
|
|
389
|
+
}
|
|
390
|
+
declare function Log<T>(props: LogProps<T>): TuiElement;
|
|
391
|
+
interface LogStreamProps<T> {
|
|
392
|
+
/** Log entries to display. */
|
|
393
|
+
entries: T[];
|
|
394
|
+
/** Render function for each entry. */
|
|
395
|
+
children: (entry: T) => TuiChild;
|
|
396
|
+
/** Maximum number of entries to keep visible. Older entries are trimmed. */
|
|
397
|
+
maxLines?: number;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* LogStream — scrollable log output for CLI tools.
|
|
401
|
+
*
|
|
402
|
+
* Renders a list of entries in a column layout. When `maxLines` is set,
|
|
403
|
+
* only the most recent entries are shown (tail behavior).
|
|
404
|
+
*/
|
|
405
|
+
declare function LogStream<T>({ entries, children: renderEntry, maxLines }: LogStreamProps<T>): TuiElement;
|
|
406
|
+
interface SelectOption<T = string> {
|
|
407
|
+
label: string;
|
|
408
|
+
value: T;
|
|
409
|
+
hint?: string;
|
|
410
|
+
}
|
|
411
|
+
interface SelectProps<T = string> {
|
|
412
|
+
message: string;
|
|
413
|
+
options: SelectOption<T>[];
|
|
414
|
+
onSubmit?: (value: T) => void;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Select prompt component.
|
|
418
|
+
*
|
|
419
|
+
* Uses `signal()` + internals API (`__element`, `__append`, `__child`) directly
|
|
420
|
+
* instead of JSX because framework components are pre-built JS that doesn't pass
|
|
421
|
+
* through the Vertz compiler. See the block comment at the top of `internals.ts`
|
|
422
|
+
* for the full explanation of this dual-usage pattern.
|
|
423
|
+
*/
|
|
424
|
+
declare function Select<T>(props: SelectProps<T>): TuiElement;
|
|
425
|
+
interface MultiSelectProps<T = string> {
|
|
426
|
+
message: string;
|
|
427
|
+
options: SelectOption<T>[];
|
|
428
|
+
defaultValue?: T[];
|
|
429
|
+
onSubmit?: (values: T[]) => void;
|
|
430
|
+
}
|
|
431
|
+
declare function MultiSelect<T>(props: MultiSelectProps<T>): TuiElement;
|
|
432
|
+
interface PasswordInputProps {
|
|
433
|
+
value?: string;
|
|
434
|
+
onChange?: (value: string) => void;
|
|
435
|
+
onSubmit?: (value: string) => void;
|
|
436
|
+
placeholder?: string;
|
|
437
|
+
}
|
|
438
|
+
declare function PasswordInput(props: PasswordInputProps): TuiElement;
|
|
439
|
+
interface ProgressBarProps {
|
|
440
|
+
value: number;
|
|
441
|
+
max: number;
|
|
442
|
+
label?: string;
|
|
443
|
+
width?: number;
|
|
444
|
+
}
|
|
445
|
+
declare function ProgressBar(props: ProgressBarProps): TuiElement;
|
|
446
|
+
/**
|
|
447
|
+
* Spacer — fills remaining space (grow: 1).
|
|
448
|
+
* Used in row layouts to push content to opposite ends.
|
|
449
|
+
*/
|
|
450
|
+
declare function Spacer(_props: Record<string, never>): TuiNode;
|
|
451
|
+
interface SpinnerProps {
|
|
452
|
+
label?: string;
|
|
453
|
+
}
|
|
454
|
+
declare function Spinner(props: SpinnerProps): TuiElement;
|
|
455
|
+
interface TableColumn<T> {
|
|
456
|
+
key: keyof T & string;
|
|
457
|
+
header: string;
|
|
458
|
+
width?: number;
|
|
459
|
+
align?: "left" | "right" | "center";
|
|
460
|
+
}
|
|
461
|
+
interface TableProps<T extends Record<string, unknown>> {
|
|
462
|
+
data: T[];
|
|
463
|
+
columns: TableColumn<T>[];
|
|
464
|
+
}
|
|
465
|
+
declare function Table<T extends Record<string, unknown>>(props: TableProps<T>): TuiElement;
|
|
466
|
+
type TaskStatus = "pending" | "running" | "success" | "error" | "skipped";
|
|
467
|
+
interface TaskConfig {
|
|
468
|
+
label: string;
|
|
469
|
+
run: () => unknown | Promise<unknown>;
|
|
470
|
+
}
|
|
471
|
+
interface TaskResult {
|
|
472
|
+
label: string;
|
|
473
|
+
status: TaskStatus;
|
|
474
|
+
value?: unknown;
|
|
475
|
+
error?: Error;
|
|
476
|
+
duration: number;
|
|
477
|
+
}
|
|
478
|
+
interface TaskRunnerConfig {
|
|
479
|
+
tasks: TaskConfig[];
|
|
480
|
+
}
|
|
481
|
+
interface TaskRunnerHandle {
|
|
482
|
+
run(): Promise<TaskResult[]>;
|
|
483
|
+
component(): TuiElement;
|
|
484
|
+
}
|
|
485
|
+
declare function TaskRunner(config: TaskRunnerConfig): TaskRunnerHandle;
|
|
486
|
+
interface TextProps {
|
|
487
|
+
color?: Color;
|
|
488
|
+
bgColor?: Color;
|
|
489
|
+
bold?: boolean;
|
|
490
|
+
dim?: boolean;
|
|
491
|
+
italic?: boolean;
|
|
492
|
+
underline?: boolean;
|
|
493
|
+
strikethrough?: boolean;
|
|
494
|
+
wrap?: "wrap" | "truncate" | "truncate-end";
|
|
495
|
+
children?: TuiNode;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Text — styled text component.
|
|
499
|
+
* Supports nesting for mixed styles.
|
|
500
|
+
*/
|
|
501
|
+
declare function Text(_props: TextProps): TuiNode;
|
|
502
|
+
interface TextInputProps {
|
|
503
|
+
value?: string;
|
|
504
|
+
onChange?: (value: string) => void;
|
|
505
|
+
onSubmit?: (value: string) => void;
|
|
506
|
+
placeholder?: string;
|
|
507
|
+
}
|
|
508
|
+
declare function TextInput(props: TextInputProps): TuiElement;
|
|
509
|
+
import { Context as Context_rri5bl } from "@vertz/ui";
|
|
510
|
+
interface FocusState {
|
|
511
|
+
focused: boolean;
|
|
512
|
+
}
|
|
513
|
+
/** Focus context for interactive components. */
|
|
514
|
+
declare const FocusContext: Context_rri5bl<FocusState>;
|
|
515
|
+
/**
|
|
516
|
+
* Hook to check if the current component has focus.
|
|
517
|
+
* Returns { focused: boolean }.
|
|
518
|
+
*/
|
|
519
|
+
declare function useFocus(): FocusState;
|
|
520
|
+
/**
|
|
521
|
+
* Register a keyboard handler for the current app.
|
|
522
|
+
* Calls the callback whenever a key is pressed.
|
|
523
|
+
* Safe to call inside effects — deduplicates registrations.
|
|
524
|
+
*/
|
|
525
|
+
declare function useKeyboard(callback: (key: KeyEvent) => void): void;
|
|
526
|
+
/** Maps key patterns (e.g. 'up', 'ctrl+c', 'shift+tab') to handler functions. */
|
|
527
|
+
type KeyMap = Record<string, (key: KeyEvent) => void>;
|
|
528
|
+
/**
|
|
529
|
+
* Create a keyboard handler that dispatches key events to pattern-matched handlers.
|
|
530
|
+
*
|
|
531
|
+
* Patterns are parsed once at call time, not per keystroke. Matching is exact:
|
|
532
|
+
* `'up'` won't match `ctrl+up`. First match wins. No match = no-op.
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* ```ts
|
|
536
|
+
* useKeyboard(match({
|
|
537
|
+
* up: () => selectedIndex--,
|
|
538
|
+
* down: () => selectedIndex++,
|
|
539
|
+
* return: () => onSubmit(),
|
|
540
|
+
* 'ctrl+c': () => tui.exit(),
|
|
541
|
+
* }));
|
|
542
|
+
* ```
|
|
543
|
+
*/
|
|
544
|
+
declare function match(keyMap: KeyMap): (key: KeyEvent) => void;
|
|
545
|
+
/**
|
|
546
|
+
* Detect whether the current environment is interactive (has a TTY).
|
|
547
|
+
* Returns false in CI, piped stdin, or non-TTY environments.
|
|
548
|
+
*/
|
|
549
|
+
declare function isInteractive(): boolean;
|
|
550
|
+
/** Error thrown when an interactive prompt is required but the environment is non-interactive. */
|
|
551
|
+
declare class NonInteractiveError extends Error {
|
|
552
|
+
constructor(promptType: string);
|
|
553
|
+
}
|
|
554
|
+
import { Signal as Signal2 } from "@vertz/ui";
|
|
555
|
+
type RenderFn = () => string;
|
|
556
|
+
interface Renderable {
|
|
557
|
+
render: RenderFn;
|
|
558
|
+
dispose?: () => void;
|
|
559
|
+
}
|
|
560
|
+
type MessageType = "info" | "error" | "warning" | "success";
|
|
561
|
+
interface MessageConfig {
|
|
562
|
+
type: MessageType;
|
|
563
|
+
children: (() => string) | string;
|
|
564
|
+
}
|
|
565
|
+
/** @deprecated Use `<Text>` with `symbols` instead. */
|
|
566
|
+
declare function Message({ type, children }: MessageConfig): Renderable;
|
|
567
|
+
interface Choice {
|
|
568
|
+
label: string;
|
|
569
|
+
value: string;
|
|
570
|
+
}
|
|
571
|
+
interface SelectListConfig {
|
|
572
|
+
title: () => string;
|
|
573
|
+
choices: () => readonly Choice[];
|
|
574
|
+
selectedIndex: Signal2<number>;
|
|
575
|
+
}
|
|
576
|
+
/** @deprecated Use `<Select>` instead. */
|
|
577
|
+
declare function SelectList({ title, choices, selectedIndex }: SelectListConfig): Renderable;
|
|
578
|
+
type TaskStatus2 = "pending" | "running" | "done" | "error";
|
|
579
|
+
interface TaskConfig2 {
|
|
580
|
+
name: () => string;
|
|
581
|
+
status: Signal2<TaskStatus2>;
|
|
582
|
+
detail?: Signal2<string | undefined>;
|
|
583
|
+
}
|
|
584
|
+
/** @deprecated Use custom `<Box>` + `<Text>` + `<Spinner>` instead. */
|
|
585
|
+
declare function Task({ name, status, detail }: TaskConfig2): Renderable;
|
|
586
|
+
interface TaskItem {
|
|
587
|
+
name: string;
|
|
588
|
+
status: TaskStatus2;
|
|
589
|
+
detail?: string;
|
|
590
|
+
}
|
|
591
|
+
interface TaskListConfig {
|
|
592
|
+
title: () => string;
|
|
593
|
+
tasks: Signal2<readonly TaskItem[]>;
|
|
594
|
+
}
|
|
595
|
+
/** @deprecated Use custom JSX components instead. */
|
|
596
|
+
declare function TaskList({ title, tasks }: TaskListConfig): Renderable;
|
|
597
|
+
interface TaskHandle {
|
|
598
|
+
update(message: string): void;
|
|
599
|
+
succeed(message?: string): void;
|
|
600
|
+
fail(message?: string): void;
|
|
601
|
+
}
|
|
602
|
+
interface TaskGroup {
|
|
603
|
+
task(name: string, fn: (handle: TaskHandle) => Promise<void>): Promise<void>;
|
|
604
|
+
dismiss(): void;
|
|
605
|
+
}
|
|
606
|
+
interface TaskRunner2 {
|
|
607
|
+
group(name: string): TaskGroup;
|
|
608
|
+
info(message: string): void;
|
|
609
|
+
warn(message: string): void;
|
|
610
|
+
error(message: string): void;
|
|
611
|
+
success(message: string): void;
|
|
612
|
+
cleanup(): void;
|
|
613
|
+
}
|
|
614
|
+
/** @deprecated Use `prompt.spinner()` or custom JSX instead. */
|
|
615
|
+
declare function createTaskRunner(): TaskRunner2;
|
|
616
|
+
interface TextPromptConfig {
|
|
617
|
+
message: string;
|
|
618
|
+
placeholder?: string;
|
|
619
|
+
default?: string;
|
|
620
|
+
validate?: (value: string) => string | undefined;
|
|
621
|
+
_mountOptions?: TuiMountOptions;
|
|
622
|
+
}
|
|
623
|
+
interface SelectPromptConfig<T> {
|
|
624
|
+
message: string;
|
|
625
|
+
options: SelectOption<T>[];
|
|
626
|
+
default?: T;
|
|
627
|
+
}
|
|
628
|
+
interface MultiSelectPromptConfig<T> {
|
|
629
|
+
message: string;
|
|
630
|
+
options: SelectOption<T>[];
|
|
631
|
+
defaultValue?: T[];
|
|
632
|
+
}
|
|
633
|
+
interface ConfirmPromptConfig {
|
|
634
|
+
message: string;
|
|
635
|
+
default?: boolean;
|
|
636
|
+
}
|
|
637
|
+
interface PasswordPromptConfig {
|
|
638
|
+
message: string;
|
|
639
|
+
placeholder?: string;
|
|
640
|
+
default?: string;
|
|
641
|
+
}
|
|
642
|
+
interface SpinnerHandle {
|
|
643
|
+
start(message: string): void;
|
|
644
|
+
stop(message: string): void;
|
|
645
|
+
}
|
|
646
|
+
interface LogMethods {
|
|
647
|
+
info(message: string): void;
|
|
648
|
+
warn(message: string): void;
|
|
649
|
+
error(message: string): void;
|
|
650
|
+
success(message: string): void;
|
|
651
|
+
}
|
|
652
|
+
interface PromptAPI {
|
|
653
|
+
text(config: TextPromptConfig): Promise<string>;
|
|
654
|
+
select<T>(config: SelectPromptConfig<T>): Promise<T>;
|
|
655
|
+
multiSelect<T>(config: MultiSelectPromptConfig<T>): Promise<T[]>;
|
|
656
|
+
confirm(config: ConfirmPromptConfig): Promise<boolean>;
|
|
657
|
+
password(config: PasswordPromptConfig): Promise<string>;
|
|
658
|
+
spinner(): SpinnerHandle;
|
|
659
|
+
intro(title: string): void;
|
|
660
|
+
outro(message: string): void;
|
|
661
|
+
log: LogMethods;
|
|
662
|
+
}
|
|
663
|
+
/** Imperative prompt API — sugar built on top of persistent tree components. */
|
|
664
|
+
declare const prompt: PromptAPI;
|
|
665
|
+
interface RenderToStringOptions {
|
|
666
|
+
/** Terminal width in columns. Defaults to 80. */
|
|
667
|
+
width?: number;
|
|
668
|
+
/** Terminal height in rows. Defaults to 24. */
|
|
669
|
+
height?: number;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Render a TUI component tree to an ANSI-formatted string.
|
|
673
|
+
* No terminal or TTY required — works entirely in memory.
|
|
674
|
+
*/
|
|
675
|
+
declare function renderToString(node: TuiNode, options?: RenderToStringOptions): string;
|
|
676
|
+
declare const symbols: Readonly<{
|
|
677
|
+
success: string;
|
|
678
|
+
error: string;
|
|
679
|
+
warning: string;
|
|
680
|
+
info: string;
|
|
681
|
+
arrow: string;
|
|
682
|
+
pointer: string;
|
|
683
|
+
bullet: string;
|
|
684
|
+
dash: string;
|
|
685
|
+
}>;
|
|
686
|
+
declare const colors: Readonly<{
|
|
687
|
+
success: string;
|
|
688
|
+
error: string;
|
|
689
|
+
warning: string;
|
|
690
|
+
info: string;
|
|
691
|
+
dim: string;
|
|
692
|
+
method: Readonly<{
|
|
693
|
+
GET: string;
|
|
694
|
+
POST: string;
|
|
695
|
+
PUT: string;
|
|
696
|
+
PATCH: string;
|
|
697
|
+
DELETE: string;
|
|
698
|
+
}>;
|
|
699
|
+
}>;
|
|
700
|
+
interface WizardStep<
|
|
701
|
+
TId extends string = string,
|
|
702
|
+
TValue = unknown
|
|
703
|
+
> {
|
|
704
|
+
id: TId;
|
|
705
|
+
prompt: (ctx: WizardContext) => Promise<TValue>;
|
|
706
|
+
}
|
|
707
|
+
interface WizardContext {
|
|
708
|
+
answers: Record<string, unknown>;
|
|
709
|
+
}
|
|
710
|
+
interface WizardConfig<TSteps extends readonly WizardStep[]> {
|
|
711
|
+
steps: TSteps;
|
|
712
|
+
onStep?: (info: {
|
|
713
|
+
current: number;
|
|
714
|
+
total: number;
|
|
715
|
+
id: string;
|
|
716
|
+
}) => void;
|
|
717
|
+
}
|
|
718
|
+
type WizardResult<TSteps extends readonly WizardStep[]> = { [K in TSteps[number] as K["id"]] : K extends WizardStep<string, infer V> ? V : never };
|
|
719
|
+
declare function wizard<const TSteps extends readonly WizardStep[]>(config: WizardConfig<TSteps>): Promise<WizardResult<TSteps>>;
|
|
720
|
+
export { wizard, useKeyboard, useFocus, useContext, tui, symbols, signal, requestDeviceCode, renderToString, prompt, pollTokenUntilComplete, onMount, match, isInteractive, createTaskRunner, createContext, computed, colors, batch, WizardStep, WizardResult, WizardContext, WizardConfig, TuiMountOptions, TuiHandle, TokenResponse, TextProps, TextPromptConfig, TextInputProps, TextInput, Text, TaskStatus, TaskRunnerHandle, TaskRunnerConfig, TaskRunner, TaskResult, TaskList, TaskHandle, TaskGroup, TaskConfig, Task, TableProps, TableColumn, Table, SpinnerProps, Spinner, Spacer, SourceLine, Signal3 as Signal, SelectProps, SelectPromptConfig, SelectOption, SelectList, Select, RenderToStringOptions, ReadonlySignal, PromptAPI, ProgressBarProps, ProgressBar, PasswordPromptConfig, PasswordInputProps, PasswordInput, NonInteractiveError, MultiSelectProps, MultiSelectPromptConfig, MultiSelect, Message, LogStreamProps, LogStream, LogProps, Log, TaskRunner2 as LegacyTaskRunner, KeyValueProps, KeyValueEntry, KeyValue, KeyMap, KeyEvent, FocusContext, DividerProps, Divider, DisposeFn2 as DisposeFn, DiagnosticViewProps, DiagnosticView, DiagnosticItem, DeviceCodeResponse, DeviceCodeAuthOptions, DeviceCodeAuth, DashboardProps, Dashboard, ConfirmProps, ConfirmPromptConfig, Confirm, Computed, Color, BoxProps, Box, BannerProps, Banner, AuthTokens, AuthStatus, AuthExpiredError, AuthDeniedError, AuthConfig, AuthCancelledError };
|