@boba-cli/dsl 0.1.0-alpha.1
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 +443 -0
- package/dist/index.cjs +528 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +738 -0
- package/dist/index.d.ts +738 -0
- package/dist/index.js +473 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
import { Cmd, Msg, Model } from '@boba-cli/tea';
|
|
2
|
+
import { Style } from '@boba-cli/chapstick';
|
|
3
|
+
export { Style } from '@boba-cli/chapstick';
|
|
4
|
+
import { Spinner, SpinnerModel } from '@boba-cli/spinner';
|
|
5
|
+
export { Spinner, dot, ellipsis, line, meter, miniDot, moon, points, pulse } from '@boba-cli/spinner';
|
|
6
|
+
import { EchoMode, ValidateFunc, TextInputModel } from '@boba-cli/textinput';
|
|
7
|
+
export { CursorMode, EchoMode, TextInputModel, ValidateFunc } from '@boba-cli/textinput';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* View node types for the view DSL.
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
type ViewNode = string | TextNode | LayoutNode | ComponentView;
|
|
14
|
+
/**
|
|
15
|
+
* Text node with chainable style methods.
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* Text nodes provide a fluent API for applying terminal styling to text content.
|
|
19
|
+
* All styling methods return a new {@link TextNode} instance, allowing for method chaining.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* text('Hello').bold().foreground('#ff79c6')
|
|
24
|
+
* text('Warning').dim().italic()
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
interface TextNode {
|
|
30
|
+
/** Internal discriminator for type narrowing. */
|
|
31
|
+
readonly _type: 'text';
|
|
32
|
+
/** The text content to display. */
|
|
33
|
+
readonly content: string;
|
|
34
|
+
/** Whether bold styling is applied. */
|
|
35
|
+
readonly _bold: boolean;
|
|
36
|
+
/** Whether dim styling is applied. */
|
|
37
|
+
readonly _dim: boolean;
|
|
38
|
+
/** Whether italic styling is applied. */
|
|
39
|
+
readonly _italic: boolean;
|
|
40
|
+
/** Foreground color (hex or named color). */
|
|
41
|
+
readonly _foreground: string | undefined;
|
|
42
|
+
/** Background color (hex or named color). */
|
|
43
|
+
readonly _background: string | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Apply bold styling to the text.
|
|
46
|
+
* @returns A new {@link TextNode} with bold styling applied.
|
|
47
|
+
*/
|
|
48
|
+
bold(): TextNode;
|
|
49
|
+
/**
|
|
50
|
+
* Apply dim styling to the text.
|
|
51
|
+
* @returns A new {@link TextNode} with dim styling applied.
|
|
52
|
+
*/
|
|
53
|
+
dim(): TextNode;
|
|
54
|
+
/**
|
|
55
|
+
* Apply italic styling to the text.
|
|
56
|
+
* @returns A new {@link TextNode} with italic styling applied.
|
|
57
|
+
*/
|
|
58
|
+
italic(): TextNode;
|
|
59
|
+
/**
|
|
60
|
+
* Set the foreground color.
|
|
61
|
+
* @param color - Hex color (e.g., '#ff79c6') or named color
|
|
62
|
+
* @returns A new {@link TextNode} with the foreground color applied.
|
|
63
|
+
*/
|
|
64
|
+
foreground(color: string): TextNode;
|
|
65
|
+
/**
|
|
66
|
+
* Set the background color.
|
|
67
|
+
* @param color - Hex color (e.g., '#282a36') or named color
|
|
68
|
+
* @returns A new {@link TextNode} with the background color applied.
|
|
69
|
+
*/
|
|
70
|
+
background(color: string): TextNode;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Layout node for stacking views vertically or horizontally.
|
|
74
|
+
*
|
|
75
|
+
* @remarks
|
|
76
|
+
* Layout nodes arrange child views in either a vertical stack ({@link vstack})
|
|
77
|
+
* or horizontal stack ({@link hstack}). Children are rendered with optional
|
|
78
|
+
* spacing between them.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* vstack(
|
|
83
|
+
* text('Line 1'),
|
|
84
|
+
* text('Line 2')
|
|
85
|
+
* )
|
|
86
|
+
*
|
|
87
|
+
* hstack(
|
|
88
|
+
* text('Left'),
|
|
89
|
+
* text('Right')
|
|
90
|
+
* )
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @public
|
|
94
|
+
*/
|
|
95
|
+
interface LayoutNode {
|
|
96
|
+
/** Layout direction: 'vstack' for vertical, 'hstack' for horizontal. */
|
|
97
|
+
readonly _type: 'vstack' | 'hstack';
|
|
98
|
+
/** Child view nodes to arrange. */
|
|
99
|
+
readonly children: ViewNode[];
|
|
100
|
+
/** Spacing between children (in newlines for vstack, spaces for hstack). */
|
|
101
|
+
readonly spacing: number;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Component view wrapper containing rendered output from a TEA component.
|
|
105
|
+
*
|
|
106
|
+
* @remarks
|
|
107
|
+
* Component views are returned by the builder when registering components
|
|
108
|
+
* via {@link AppBuilder.component}. They contain the pre-rendered string
|
|
109
|
+
* output from the component's `view()` method.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* createApp()
|
|
114
|
+
* .component('spinner', spinner())
|
|
115
|
+
* .view(({ components }) => components.spinner)
|
|
116
|
+
* // ^^^^^^^^^^^^^^^^^ ComponentView
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* @public
|
|
120
|
+
*/
|
|
121
|
+
interface ComponentView {
|
|
122
|
+
/** Internal discriminator for type narrowing. */
|
|
123
|
+
readonly _type: 'component';
|
|
124
|
+
/** The rendered view string from the component. */
|
|
125
|
+
readonly view: string;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Component builder interface for wrapping TEA components.
|
|
129
|
+
*
|
|
130
|
+
* @remarks
|
|
131
|
+
* Component builders provide an adapter between TEA components (which follow
|
|
132
|
+
* the Model-Update-View pattern) and the DSL. When you register a component
|
|
133
|
+
* via {@link AppBuilder.component}, you provide a `ComponentBuilder` that
|
|
134
|
+
* handles initialization, updates, and rendering.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const spinnerBuilder: ComponentBuilder<SpinnerModel> = {
|
|
139
|
+
* init: () => {
|
|
140
|
+
* const model = new SpinnerModel()
|
|
141
|
+
* return [model, model.tick()]
|
|
142
|
+
* },
|
|
143
|
+
* update: (model, msg) => model.update(msg),
|
|
144
|
+
* view: (model) => model.view(),
|
|
145
|
+
* }
|
|
146
|
+
* ```
|
|
147
|
+
*
|
|
148
|
+
* @typeParam M - The model type managed by this component
|
|
149
|
+
*
|
|
150
|
+
* @public
|
|
151
|
+
*/
|
|
152
|
+
interface ComponentBuilder<M> {
|
|
153
|
+
/**
|
|
154
|
+
* Initialize the component and return the initial model and command.
|
|
155
|
+
* @returns A tuple of [initial model, initial command]
|
|
156
|
+
*/
|
|
157
|
+
init(): [M, Cmd<Msg>];
|
|
158
|
+
/**
|
|
159
|
+
* Update the component with a message.
|
|
160
|
+
* @param model - The current component model
|
|
161
|
+
* @param msg - The message to process
|
|
162
|
+
* @returns A tuple of [next model, next command]
|
|
163
|
+
*/
|
|
164
|
+
update(model: M, msg: Msg): [M, Cmd<Msg>];
|
|
165
|
+
/**
|
|
166
|
+
* Render the component to a string.
|
|
167
|
+
* @param model - The current component model
|
|
168
|
+
* @returns The rendered view string
|
|
169
|
+
*/
|
|
170
|
+
view(model: M): string;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Event context passed to key event handlers.
|
|
174
|
+
*
|
|
175
|
+
* @remarks
|
|
176
|
+
* The event context provides access to the current application state and
|
|
177
|
+
* component views, along with methods to update state or quit the application.
|
|
178
|
+
* It's passed to handlers registered via {@link AppBuilder.onKey}.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* createApp()
|
|
183
|
+
* .state({ count: 0 })
|
|
184
|
+
* .onKey('up', ({ state, update }) => {
|
|
185
|
+
* update({ count: state.count + 1 })
|
|
186
|
+
* })
|
|
187
|
+
* .onKey('q', ({ quit }) => quit())
|
|
188
|
+
* ```
|
|
189
|
+
*
|
|
190
|
+
* @typeParam State - The application state type
|
|
191
|
+
* @typeParam Components - Record of registered components
|
|
192
|
+
*
|
|
193
|
+
* @public
|
|
194
|
+
*/
|
|
195
|
+
interface EventContext<State, Components extends Record<string, unknown>> {
|
|
196
|
+
/** Current application state. */
|
|
197
|
+
readonly state: State;
|
|
198
|
+
/** Current component views (rendered strings). */
|
|
199
|
+
readonly components: {
|
|
200
|
+
[K in keyof Components]: ComponentView;
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* Update state with a partial patch (shallow merge).
|
|
204
|
+
* @param patch - Partial state object to merge with current state
|
|
205
|
+
*/
|
|
206
|
+
update(patch: Partial<State>): void;
|
|
207
|
+
/**
|
|
208
|
+
* Replace entire state with a new value.
|
|
209
|
+
* @param newState - The new complete state
|
|
210
|
+
*/
|
|
211
|
+
setState(newState: State): void;
|
|
212
|
+
/**
|
|
213
|
+
* Quit the application gracefully.
|
|
214
|
+
*/
|
|
215
|
+
quit(): void;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Key handler function type.
|
|
219
|
+
*
|
|
220
|
+
* @remarks
|
|
221
|
+
* Key handlers are registered via {@link AppBuilder.onKey} and receive an
|
|
222
|
+
* {@link EventContext} with the current state and component views. Handlers
|
|
223
|
+
* can update state or quit the application.
|
|
224
|
+
*
|
|
225
|
+
* @typeParam State - The application state type
|
|
226
|
+
* @typeParam Components - Record of registered components
|
|
227
|
+
*
|
|
228
|
+
* @public
|
|
229
|
+
*/
|
|
230
|
+
type KeyHandler<State, Components extends Record<string, unknown>> = (ctx: EventContext<State, Components>) => void;
|
|
231
|
+
/**
|
|
232
|
+
* View function type.
|
|
233
|
+
*
|
|
234
|
+
* @remarks
|
|
235
|
+
* The view function is registered via {@link AppBuilder.view} and is called
|
|
236
|
+
* on every render cycle. It receives the current state and component views,
|
|
237
|
+
* and returns a {@link ViewNode} tree to display.
|
|
238
|
+
*
|
|
239
|
+
* @typeParam State - The application state type
|
|
240
|
+
* @typeParam Components - Record of registered components
|
|
241
|
+
*
|
|
242
|
+
* @public
|
|
243
|
+
*/
|
|
244
|
+
type ViewFunction<State, Components extends Record<string, unknown>> = (ctx: {
|
|
245
|
+
state: State;
|
|
246
|
+
components: {
|
|
247
|
+
[K in keyof Components]: ComponentView;
|
|
248
|
+
};
|
|
249
|
+
}) => ViewNode;
|
|
250
|
+
/**
|
|
251
|
+
* Built application ready to run.
|
|
252
|
+
*
|
|
253
|
+
* @remarks
|
|
254
|
+
* An `App` is created by calling {@link AppBuilder.build}. It provides a
|
|
255
|
+
* `run()` method to start the application event loop and a `getModel()` escape
|
|
256
|
+
* hatch to access the underlying TEA model for advanced use cases.
|
|
257
|
+
*
|
|
258
|
+
* @typeParam State - The application state type
|
|
259
|
+
* @typeParam _Components - Record of registered components (phantom type)
|
|
260
|
+
*
|
|
261
|
+
* @public
|
|
262
|
+
*/
|
|
263
|
+
interface App<State, _Components extends Record<string, unknown>> {
|
|
264
|
+
/**
|
|
265
|
+
* Run the application and block until it quits.
|
|
266
|
+
* @returns A promise resolving to the final application state
|
|
267
|
+
*/
|
|
268
|
+
run(): Promise<{
|
|
269
|
+
state: State;
|
|
270
|
+
}>;
|
|
271
|
+
/**
|
|
272
|
+
* Get the underlying TEA model (escape hatch for advanced use cases).
|
|
273
|
+
* @returns The generated TEA Model from `@boba-cli/tea`
|
|
274
|
+
*/
|
|
275
|
+
getModel(): Model<Msg>;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Builder for creating declarative CLI applications.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* const app = createApp()
|
|
284
|
+
* .state({ count: 0 })
|
|
285
|
+
* .component('spinner', spinner())
|
|
286
|
+
* .onKey('q', ({ quit }) => quit())
|
|
287
|
+
* .view(({ state, components }) => vstack(
|
|
288
|
+
* text('Count: ' + state.count),
|
|
289
|
+
* components.spinner
|
|
290
|
+
* ))
|
|
291
|
+
* .build()
|
|
292
|
+
*
|
|
293
|
+
* await app.run()
|
|
294
|
+
* ```
|
|
295
|
+
*
|
|
296
|
+
* @public
|
|
297
|
+
*/
|
|
298
|
+
declare class AppBuilder<State = undefined, Components extends Record<string, unknown> = Record<string, never>> {
|
|
299
|
+
#private;
|
|
300
|
+
private constructor();
|
|
301
|
+
/**
|
|
302
|
+
* Create a new AppBuilder instance.
|
|
303
|
+
* @internal
|
|
304
|
+
*/
|
|
305
|
+
static create(): AppBuilder<undefined, Record<string, never>>;
|
|
306
|
+
/**
|
|
307
|
+
* Set the initial application state.
|
|
308
|
+
*
|
|
309
|
+
* @remarks
|
|
310
|
+
* This should typically be called early in the builder chain. If called after
|
|
311
|
+
* registering key handlers or a view function, those will be preserved but
|
|
312
|
+
* their type information will be updated to reflect the new state type.
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```typescript
|
|
316
|
+
* createApp()
|
|
317
|
+
* .state({ count: 0, name: 'World' })
|
|
318
|
+
* ```
|
|
319
|
+
*
|
|
320
|
+
* @typeParam S - The application state type
|
|
321
|
+
* @param initial - The initial state object
|
|
322
|
+
* @returns A new {@link AppBuilder} with the state type parameter set
|
|
323
|
+
*
|
|
324
|
+
* @public
|
|
325
|
+
*/
|
|
326
|
+
state<S>(initial: S): AppBuilder<S, Components>;
|
|
327
|
+
/**
|
|
328
|
+
* Register a component with a unique key.
|
|
329
|
+
*
|
|
330
|
+
* @remarks
|
|
331
|
+
* Components are TEA models wrapped in a {@link ComponentBuilder} that
|
|
332
|
+
* manages their lifecycle. The component's rendered view is available in
|
|
333
|
+
* the view function via `components[key]`.
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* createApp()
|
|
338
|
+
* .component('loading', spinner())
|
|
339
|
+
* .component('input', textInput())
|
|
340
|
+
* ```
|
|
341
|
+
*
|
|
342
|
+
* @typeParam K - The component key (string literal type)
|
|
343
|
+
* @typeParam M - The component model type
|
|
344
|
+
* @param key - Unique identifier for this component
|
|
345
|
+
* @param builder - Component builder implementing init/update/view
|
|
346
|
+
* @returns A new {@link AppBuilder} with the component registered
|
|
347
|
+
*
|
|
348
|
+
* @public
|
|
349
|
+
*/
|
|
350
|
+
component<K extends string, M>(key: K, builder: ComponentBuilder<M>): AppBuilder<State, Components & Record<K, M>>;
|
|
351
|
+
/**
|
|
352
|
+
* Register a key handler.
|
|
353
|
+
*
|
|
354
|
+
* @remarks
|
|
355
|
+
* Key handlers receive an {@link EventContext} with the current state and
|
|
356
|
+
* components. Multiple keys can be bound to the same handler by passing an
|
|
357
|
+
* array of key strings. Key strings support modifiers like 'ctrl+c', 'alt+enter'.
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```typescript
|
|
361
|
+
* createApp()
|
|
362
|
+
* .onKey('q', ({ quit }) => quit())
|
|
363
|
+
* .onKey(['up', 'k'], ({ state, update }) => update({ index: state.index - 1 }))
|
|
364
|
+
* .onKey('ctrl+c', ({ quit }) => quit())
|
|
365
|
+
* ```
|
|
366
|
+
*
|
|
367
|
+
* @param keys - Single key string or array of key strings
|
|
368
|
+
* @param handler - Function to call when any of the keys are pressed
|
|
369
|
+
* @returns A new {@link AppBuilder} with the key handler registered
|
|
370
|
+
*
|
|
371
|
+
* @public
|
|
372
|
+
*/
|
|
373
|
+
onKey(keys: string | string[], handler: KeyHandler<State, Components>): AppBuilder<State, Components>;
|
|
374
|
+
/**
|
|
375
|
+
* Set the view function.
|
|
376
|
+
*
|
|
377
|
+
* @remarks
|
|
378
|
+
* The view function is called on every render cycle and receives the current
|
|
379
|
+
* state and component views. It should return a {@link ViewNode} tree
|
|
380
|
+
* describing the UI to display.
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* ```typescript
|
|
384
|
+
* createApp()
|
|
385
|
+
* .view(({ state, components }) => vstack(
|
|
386
|
+
* text('Hello ' + state.name),
|
|
387
|
+
* components.spinner
|
|
388
|
+
* ))
|
|
389
|
+
* ```
|
|
390
|
+
*
|
|
391
|
+
* @param fn - Function that returns a {@link ViewNode} tree
|
|
392
|
+
* @returns A new {@link AppBuilder} with the view function set
|
|
393
|
+
*
|
|
394
|
+
* @public
|
|
395
|
+
*/
|
|
396
|
+
view(fn: ViewFunction<State, Components>): AppBuilder<State, Components>;
|
|
397
|
+
/**
|
|
398
|
+
* Build the application.
|
|
399
|
+
*
|
|
400
|
+
* @remarks
|
|
401
|
+
* Finalizes the builder chain and creates an {@link App} instance ready
|
|
402
|
+
* to run. This method must be called after setting a view function via
|
|
403
|
+
* {@link AppBuilder.view}.
|
|
404
|
+
*
|
|
405
|
+
* @throws Error if no view function has been set
|
|
406
|
+
*
|
|
407
|
+
* @returns The built {@link App} ready to run
|
|
408
|
+
*
|
|
409
|
+
* @public
|
|
410
|
+
*/
|
|
411
|
+
build(): App<State, Components>;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Create a new application builder.
|
|
415
|
+
*
|
|
416
|
+
* @example
|
|
417
|
+
* ```typescript
|
|
418
|
+
* const app = createApp()
|
|
419
|
+
* .state({ count: 0 })
|
|
420
|
+
* .onKey('q', ({ quit }) => quit())
|
|
421
|
+
* .view(({ state }) => text('Count: ' + state.count))
|
|
422
|
+
* .build()
|
|
423
|
+
* ```
|
|
424
|
+
*
|
|
425
|
+
* @public
|
|
426
|
+
*/
|
|
427
|
+
declare function createApp(): AppBuilder<undefined, Record<string, never>>;
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Create a text node with chainable style methods.
|
|
431
|
+
*
|
|
432
|
+
* @remarks
|
|
433
|
+
* Text nodes support fluent styling via methods like `bold()`,
|
|
434
|
+
* `dim()`, `italic()`, `foreground()`, and `background()`.
|
|
435
|
+
*
|
|
436
|
+
* @example
|
|
437
|
+
* ```typescript
|
|
438
|
+
* text('Hello').bold().foreground('#ff79c6')
|
|
439
|
+
* text('Warning').dim()
|
|
440
|
+
* ```
|
|
441
|
+
*
|
|
442
|
+
* @param content - The text content to display
|
|
443
|
+
* @returns A new {@link TextNode}
|
|
444
|
+
*
|
|
445
|
+
* @public
|
|
446
|
+
*/
|
|
447
|
+
declare function text(content: string): TextNode;
|
|
448
|
+
/**
|
|
449
|
+
* Create a vertical stack layout.
|
|
450
|
+
*
|
|
451
|
+
* @remarks
|
|
452
|
+
* Arranges child views vertically with newlines between them. Children are
|
|
453
|
+
* rendered in order from top to bottom.
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* ```typescript
|
|
457
|
+
* vstack(
|
|
458
|
+
* text('Line 1'),
|
|
459
|
+
* text('Line 2'),
|
|
460
|
+
* text('Line 3')
|
|
461
|
+
* )
|
|
462
|
+
* ```
|
|
463
|
+
*
|
|
464
|
+
* @param children - View nodes to stack vertically
|
|
465
|
+
* @returns A new {@link LayoutNode} with vertical stacking
|
|
466
|
+
*
|
|
467
|
+
* @public
|
|
468
|
+
*/
|
|
469
|
+
declare function vstack(...children: ViewNode[]): LayoutNode;
|
|
470
|
+
/**
|
|
471
|
+
* Create a horizontal stack layout.
|
|
472
|
+
*
|
|
473
|
+
* @remarks
|
|
474
|
+
* Arranges child views horizontally on the same line. Children are
|
|
475
|
+
* rendered in order from left to right.
|
|
476
|
+
*
|
|
477
|
+
* @example
|
|
478
|
+
* ```typescript
|
|
479
|
+
* hstack(
|
|
480
|
+
* text('Left'),
|
|
481
|
+
* text(' | '),
|
|
482
|
+
* text('Right')
|
|
483
|
+
* )
|
|
484
|
+
* ```
|
|
485
|
+
*
|
|
486
|
+
* @param children - View nodes to stack horizontally
|
|
487
|
+
* @returns A new {@link LayoutNode} with horizontal stacking
|
|
488
|
+
*
|
|
489
|
+
* @public
|
|
490
|
+
*/
|
|
491
|
+
declare function hstack(...children: ViewNode[]): LayoutNode;
|
|
492
|
+
/**
|
|
493
|
+
* Create empty vertical space.
|
|
494
|
+
*
|
|
495
|
+
* @remarks
|
|
496
|
+
* Useful for adding vertical spacing between sections of your UI.
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* ```typescript
|
|
500
|
+
* vstack(
|
|
501
|
+
* text('Header'),
|
|
502
|
+
* spacer(2),
|
|
503
|
+
* text('Content')
|
|
504
|
+
* )
|
|
505
|
+
* ```
|
|
506
|
+
*
|
|
507
|
+
* @param height - Number of blank lines to insert (default: 1)
|
|
508
|
+
* @returns A string containing the specified number of newlines
|
|
509
|
+
*
|
|
510
|
+
* @public
|
|
511
|
+
*/
|
|
512
|
+
declare function spacer(height?: number): string;
|
|
513
|
+
/**
|
|
514
|
+
* Create a divider line.
|
|
515
|
+
*
|
|
516
|
+
* @remarks
|
|
517
|
+
* Renders a horizontal line using a repeated character.
|
|
518
|
+
*
|
|
519
|
+
* @example
|
|
520
|
+
* ```typescript
|
|
521
|
+
* vstack(
|
|
522
|
+
* text('Section 1'),
|
|
523
|
+
* divider(),
|
|
524
|
+
* text('Section 2'),
|
|
525
|
+
* divider('=', 50)
|
|
526
|
+
* )
|
|
527
|
+
* ```
|
|
528
|
+
*
|
|
529
|
+
* @param char - Character to repeat (default: '─')
|
|
530
|
+
* @param width - Number of times to repeat the character (default: 40)
|
|
531
|
+
* @returns A string containing the divider line
|
|
532
|
+
*
|
|
533
|
+
* @public
|
|
534
|
+
*/
|
|
535
|
+
declare function divider(char?: string, width?: number): string;
|
|
536
|
+
/**
|
|
537
|
+
* Conditionally render a node.
|
|
538
|
+
*
|
|
539
|
+
* @remarks
|
|
540
|
+
* Returns the node if the condition is true, otherwise returns an empty string.
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* ```typescript
|
|
544
|
+
* vstack(
|
|
545
|
+
* text('Always visible'),
|
|
546
|
+
* when(state.showHelp, text('Help text'))
|
|
547
|
+
* )
|
|
548
|
+
* ```
|
|
549
|
+
*
|
|
550
|
+
* @param condition - Boolean condition to test
|
|
551
|
+
* @param node - View node to render if condition is true
|
|
552
|
+
* @returns The node if condition is true, empty string otherwise
|
|
553
|
+
*
|
|
554
|
+
* @public
|
|
555
|
+
*/
|
|
556
|
+
declare function when(condition: boolean, node: ViewNode): ViewNode;
|
|
557
|
+
/**
|
|
558
|
+
* Choose between two nodes based on condition.
|
|
559
|
+
*
|
|
560
|
+
* @remarks
|
|
561
|
+
* Returns one node if the condition is true, another if false.
|
|
562
|
+
*
|
|
563
|
+
* @example
|
|
564
|
+
* ```typescript
|
|
565
|
+
* vstack(
|
|
566
|
+
* choose(
|
|
567
|
+
* state.isLoading,
|
|
568
|
+
* text('Loading...').dim(),
|
|
569
|
+
* text('Ready!').bold()
|
|
570
|
+
* )
|
|
571
|
+
* )
|
|
572
|
+
* ```
|
|
573
|
+
*
|
|
574
|
+
* @param condition - Boolean condition to test
|
|
575
|
+
* @param ifTrue - View node to render if condition is true
|
|
576
|
+
* @param ifFalse - View node to render if condition is false
|
|
577
|
+
* @returns Either ifTrue or ifFalse depending on condition
|
|
578
|
+
*
|
|
579
|
+
* @public
|
|
580
|
+
*/
|
|
581
|
+
declare function choose(condition: boolean, ifTrue: ViewNode, ifFalse: ViewNode): ViewNode;
|
|
582
|
+
/**
|
|
583
|
+
* Map items to view nodes.
|
|
584
|
+
*
|
|
585
|
+
* @remarks
|
|
586
|
+
* Transforms an array of items into an array of view nodes. The render
|
|
587
|
+
* function receives each item and its index.
|
|
588
|
+
*
|
|
589
|
+
* @example
|
|
590
|
+
* ```typescript
|
|
591
|
+
* vstack(
|
|
592
|
+
* ...map(state.items, (item, index) =>
|
|
593
|
+
* text(`${index + 1}. ${item.name}`)
|
|
594
|
+
* )
|
|
595
|
+
* )
|
|
596
|
+
* ```
|
|
597
|
+
*
|
|
598
|
+
* @typeParam T - The type of items in the array
|
|
599
|
+
* @param items - Array of items to map
|
|
600
|
+
* @param render - Function to transform each item into a view node
|
|
601
|
+
* @returns Array of view nodes
|
|
602
|
+
*
|
|
603
|
+
* @public
|
|
604
|
+
*/
|
|
605
|
+
declare function map<T>(items: T[], render: (item: T, index: number) => ViewNode): ViewNode[];
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Render a view node tree to a string.
|
|
609
|
+
*
|
|
610
|
+
* @remarks
|
|
611
|
+
* Recursively renders a {@link ViewNode} tree into a terminal-ready string.
|
|
612
|
+
* Handles text styling, layout stacking, and component views. This function
|
|
613
|
+
* is typically called internally by the DSL, but can be used directly for
|
|
614
|
+
* testing or custom rendering.
|
|
615
|
+
*
|
|
616
|
+
* @example
|
|
617
|
+
* ```typescript
|
|
618
|
+
* const tree = vstack(
|
|
619
|
+
* text('Hello').bold(),
|
|
620
|
+
* text('World').foreground('#ff79c6')
|
|
621
|
+
* )
|
|
622
|
+
* const output = render(tree)
|
|
623
|
+
* console.log(output)
|
|
624
|
+
* ```
|
|
625
|
+
*
|
|
626
|
+
* @param node - The view node tree to render
|
|
627
|
+
* @returns A string ready to display in the terminal
|
|
628
|
+
*
|
|
629
|
+
* @public
|
|
630
|
+
*/
|
|
631
|
+
declare function render(node: ViewNode): string;
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Options for the spinner component builder.
|
|
635
|
+
*
|
|
636
|
+
* @remarks
|
|
637
|
+
* Configure the spinner animation and styling when creating a spinner component.
|
|
638
|
+
*
|
|
639
|
+
* @public
|
|
640
|
+
*/
|
|
641
|
+
interface SpinnerBuilderOptions {
|
|
642
|
+
/**
|
|
643
|
+
* Spinner animation to use (default: `line`).
|
|
644
|
+
*
|
|
645
|
+
* @remarks
|
|
646
|
+
* Available spinners include `line`, `dot`, `miniDot`,
|
|
647
|
+
* `pulse`, `points`, `moon`, `meter`, and `ellipsis`.
|
|
648
|
+
*/
|
|
649
|
+
spinner?: Spinner;
|
|
650
|
+
/**
|
|
651
|
+
* Style for rendering the spinner.
|
|
652
|
+
*
|
|
653
|
+
* @remarks
|
|
654
|
+
* Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.
|
|
655
|
+
*/
|
|
656
|
+
style?: Style;
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Create a spinner component builder.
|
|
660
|
+
*
|
|
661
|
+
* @remarks
|
|
662
|
+
* Creates a {@link ComponentBuilder} wrapping the `@boba-cli/spinner` package.
|
|
663
|
+
* The spinner automatically animates and can be styled with custom colors.
|
|
664
|
+
*
|
|
665
|
+
* @example
|
|
666
|
+
* Basic usage:
|
|
667
|
+
* ```typescript
|
|
668
|
+
* const app = createApp()
|
|
669
|
+
* .component('loading', spinner())
|
|
670
|
+
* .view(({ components }) => components.loading)
|
|
671
|
+
* .build()
|
|
672
|
+
* ```
|
|
673
|
+
*
|
|
674
|
+
* @example
|
|
675
|
+
* With custom styling:
|
|
676
|
+
* ```typescript
|
|
677
|
+
* const app = createApp()
|
|
678
|
+
* .component('loading', spinner({
|
|
679
|
+
* style: new Style().foreground('#50fa7b')
|
|
680
|
+
* }))
|
|
681
|
+
* .view(({ components }) => hstack(
|
|
682
|
+
* components.loading,
|
|
683
|
+
* text('Loading...')
|
|
684
|
+
* ))
|
|
685
|
+
* .build()
|
|
686
|
+
* ```
|
|
687
|
+
*
|
|
688
|
+
* @param options - Configuration options for the spinner
|
|
689
|
+
* @returns A {@link ComponentBuilder} ready to use with {@link AppBuilder.component}
|
|
690
|
+
*
|
|
691
|
+
* @public
|
|
692
|
+
*/
|
|
693
|
+
declare function spinner(options?: SpinnerBuilderOptions): ComponentBuilder<SpinnerModel>;
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Options for the textInput component builder.
|
|
697
|
+
* @public
|
|
698
|
+
*/
|
|
699
|
+
interface TextInputBuilderOptions {
|
|
700
|
+
/** Placeholder text shown when input is empty. */
|
|
701
|
+
placeholder?: string;
|
|
702
|
+
/** Width constraint for the input field. */
|
|
703
|
+
width?: number;
|
|
704
|
+
/** Echo mode for input display (Normal, Password, or None). */
|
|
705
|
+
echoMode?: EchoMode;
|
|
706
|
+
/** Character limit for input. */
|
|
707
|
+
charLimit?: number;
|
|
708
|
+
/** Prompt string shown before the input. */
|
|
709
|
+
prompt?: string;
|
|
710
|
+
/** Style for the prompt. */
|
|
711
|
+
promptStyle?: Style;
|
|
712
|
+
/** Style for the input text. */
|
|
713
|
+
textStyle?: Style;
|
|
714
|
+
/** Style for the placeholder text. */
|
|
715
|
+
placeholderStyle?: Style;
|
|
716
|
+
/** Validation function. */
|
|
717
|
+
validate?: ValidateFunc;
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Create a textInput component builder.
|
|
721
|
+
*
|
|
722
|
+
* @example
|
|
723
|
+
* ```typescript
|
|
724
|
+
* const app = createApp()
|
|
725
|
+
* .component('nameInput', textInput({
|
|
726
|
+
* placeholder: 'Enter your name...',
|
|
727
|
+
* width: 40,
|
|
728
|
+
* validate: (value) => value.length < 3 ? new Error('Too short') : null
|
|
729
|
+
* }))
|
|
730
|
+
* .view(({ components }) => components.nameInput)
|
|
731
|
+
* .build()
|
|
732
|
+
* ```
|
|
733
|
+
*
|
|
734
|
+
* @public
|
|
735
|
+
*/
|
|
736
|
+
declare function textInput(options?: TextInputBuilderOptions): ComponentBuilder<TextInputModel>;
|
|
737
|
+
|
|
738
|
+
export { type App, AppBuilder, type ComponentBuilder, type ComponentView, type EventContext, type KeyHandler, type LayoutNode, type SpinnerBuilderOptions, type TextInputBuilderOptions, type TextNode, type ViewFunction, type ViewNode, choose, createApp, divider, hstack, map, render, spacer, spinner, text, textInput, vstack, when };
|