@silvery/tea 0.3.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/src/types.ts ADDED
@@ -0,0 +1,670 @@
1
+ /**
2
+ * Silvery Types
3
+ *
4
+ * Core types for the Silvery renderer architecture.
5
+ */
6
+
7
+ import type { FocusEventProps } from "./focus-events"
8
+ import type { LayoutNode } from "@silvery/term/layout-engine"
9
+ import type { MouseEventProps } from "@silvery/term/mouse-events"
10
+
11
+ // ============================================================================
12
+ // Layout Types
13
+ // ============================================================================
14
+
15
+ /**
16
+ * A rectangle with position and size.
17
+ * All values are in terminal columns/rows (integers).
18
+ */
19
+ export interface Rect {
20
+ /** X position (0-indexed terminal column) */
21
+ x: number
22
+ /** Y position (0-indexed terminal row) */
23
+ y: number
24
+ /** Width in terminal columns */
25
+ width: number
26
+ /** Height in terminal rows */
27
+ height: number
28
+ }
29
+
30
+ /**
31
+ * Check if two rects are equal (same position and size).
32
+ */
33
+ export function rectEqual(a: Rect | null, b: Rect | null): boolean {
34
+ if (a === b) return true
35
+ if (!a || !b) return false
36
+ return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height
37
+ }
38
+
39
+ // ============================================================================
40
+ // Node Types
41
+ // ============================================================================
42
+
43
+ /**
44
+ * Silvery node types - the primitive elements in the render tree.
45
+ */
46
+ export type TeaNodeType = "silvery-root" | "silvery-box" | "silvery-text"
47
+
48
+ /**
49
+ * Flexbox properties that can be applied to Box nodes.
50
+ */
51
+ export interface FlexboxProps {
52
+ // Size
53
+ width?: number | string
54
+ height?: number | string
55
+ minWidth?: number | string
56
+ minHeight?: number | string
57
+ maxWidth?: number | string
58
+ maxHeight?: number | string
59
+
60
+ // Flex
61
+ flexGrow?: number
62
+ flexShrink?: number
63
+ flexBasis?: number | string
64
+ flexDirection?: "row" | "column" | "row-reverse" | "column-reverse"
65
+ flexWrap?: "nowrap" | "wrap" | "wrap-reverse"
66
+
67
+ // Alignment
68
+ alignItems?: "flex-start" | "flex-end" | "center" | "stretch" | "baseline"
69
+ alignSelf?: "auto" | "flex-start" | "flex-end" | "center" | "stretch" | "baseline"
70
+ alignContent?: "flex-start" | "flex-end" | "center" | "stretch" | "space-between" | "space-around" | "space-evenly"
71
+ justifyContent?: "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "space-evenly"
72
+
73
+ // Spacing
74
+ padding?: number
75
+ paddingTop?: number
76
+ paddingBottom?: number
77
+ paddingLeft?: number
78
+ paddingRight?: number
79
+ paddingX?: number
80
+ paddingY?: number
81
+ margin?: number
82
+ marginTop?: number
83
+ marginBottom?: number
84
+ marginLeft?: number
85
+ marginRight?: number
86
+ marginX?: number
87
+ marginY?: number
88
+ gap?: number
89
+ columnGap?: number
90
+ rowGap?: number
91
+
92
+ // Position
93
+ position?: "relative" | "absolute" | "sticky" | "static"
94
+
95
+ // Position offsets (used with position='absolute' or position='relative')
96
+ top?: number | string
97
+ left?: number | string
98
+ bottom?: number | string
99
+ right?: number | string
100
+
101
+ // Sticky offsets (only used when position='sticky')
102
+ // The element will "stick" when it reaches this offset from the container edge
103
+ stickyTop?: number
104
+ stickyBottom?: number
105
+
106
+ // Aspect ratio
107
+ aspectRatio?: number
108
+
109
+ // Display
110
+ display?: "flex" | "none"
111
+
112
+ // Overflow
113
+ overflow?: "visible" | "hidden" | "scroll"
114
+ overflowX?: "visible" | "hidden"
115
+ overflowY?: "visible" | "hidden"
116
+
117
+ // Scroll control (only used when overflow='scroll')
118
+ /** Child index to ensure visible (edge-based: only scrolls if off-screen) */
119
+ scrollTo?: number
120
+ /** Explicit scroll offset in rows (used when scrollTo is undefined for frozen scroll state) */
121
+ scrollOffset?: number
122
+ }
123
+
124
+ /**
125
+ * Props for testing and identification.
126
+ * These props are stored in the node for DOM query access.
127
+ */
128
+ export interface TestProps {
129
+ /** Element ID for DOM queries and visual debugging */
130
+ id?: string
131
+ /** Test ID for querying nodes (like Playwright's data-testid) */
132
+ testID?: string
133
+ /** Allow arbitrary data-* attributes for testing */
134
+ [key: `data-${string}`]: unknown
135
+ }
136
+
137
+ /**
138
+ * Underline style variants (SGR 4:x codes).
139
+ * - false: no underline
140
+ * - 'single': standard underline (SGR 4 or 4:1)
141
+ * - 'double': double underline (SGR 4:2)
142
+ * - 'curly': curly/wavy underline (SGR 4:3)
143
+ * - 'dotted': dotted underline (SGR 4:4)
144
+ * - 'dashed': dashed underline (SGR 4:5)
145
+ */
146
+ export type UnderlineStyle = false | "single" | "double" | "curly" | "dotted" | "dashed"
147
+
148
+ /**
149
+ * Style properties for text rendering.
150
+ */
151
+ export interface StyleProps {
152
+ color?: string
153
+ backgroundColor?: string
154
+ bold?: boolean
155
+ dim?: boolean
156
+ /** Alias for dim (Ink compatibility) */
157
+ dimColor?: boolean
158
+ italic?: boolean
159
+ /** Enable underline. Use underlineStyle for style variants. */
160
+ underline?: boolean
161
+ /**
162
+ * Underline style variant: 'single' | 'double' | 'curly' | 'dotted' | 'dashed'.
163
+ * Setting this implies underline=true. Takes precedence over underline prop.
164
+ */
165
+ underlineStyle?: UnderlineStyle
166
+ /**
167
+ * Underline color (independent of text color).
168
+ * Uses SGR 58 (underline color). Falls back to text color if not specified.
169
+ */
170
+ underlineColor?: string
171
+ strikethrough?: boolean
172
+ inverse?: boolean
173
+ }
174
+
175
+ /**
176
+ * Props for Box component.
177
+ */
178
+ export interface BoxProps extends FlexboxProps, StyleProps, TestProps, MouseEventProps, FocusEventProps {
179
+ borderStyle?: "single" | "double" | "round" | "bold" | "singleDouble" | "doubleSingle" | "classic"
180
+ borderColor?: string
181
+ borderTop?: boolean
182
+ borderBottom?: boolean
183
+ borderLeft?: boolean
184
+ borderRight?: boolean
185
+
186
+ /**
187
+ * Outline style — renders border characters at the box edges without affecting layout.
188
+ *
189
+ * Unlike `borderStyle` which adds border dimensions to the layout (making the content
190
+ * area smaller), `outlineStyle` draws border characters that OVERLAP the content area.
191
+ * The layout engine sees no border at all — outline is purely visual.
192
+ *
193
+ * Use cases: selection indicators, hover highlights, focus rings — anything that
194
+ * should visually frame a box without shifting content.
195
+ */
196
+ outlineStyle?: "single" | "double" | "round" | "bold" | "singleDouble" | "doubleSingle" | "classic"
197
+ /** Foreground color for the outline */
198
+ outlineColor?: string
199
+ /** Apply dim styling to the outline */
200
+ outlineDimColor?: boolean
201
+ /** Show top outline edge (default: true) */
202
+ outlineTop?: boolean
203
+ /** Show bottom outline edge (default: true) */
204
+ outlineBottom?: boolean
205
+ /** Show left outline edge (default: true) */
206
+ outlineLeft?: boolean
207
+ /** Show right outline edge (default: true) */
208
+ outlineRight?: boolean
209
+
210
+ /**
211
+ * Override theme for this subtree — $token colors resolve against this theme.
212
+ * Pushed onto the context theme stack during content phase tree walk.
213
+ */
214
+ theme?: import("@silvery/theme").Theme
215
+
216
+ /** CSS pointer-events equivalent. "none" makes this node and its subtree invisible to hit testing. */
217
+ pointerEvents?: "auto" | "none"
218
+
219
+ onLayout?: (layout: Rect) => void
220
+
221
+ /**
222
+ * Show scroll overflow indicators (▲N / ▼N) for scrollable containers.
223
+ *
224
+ * For bordered containers, indicators appear on the border.
225
+ * For borderless containers, indicators overlay the content at top-right/bottom-right.
226
+ *
227
+ * Only applies when overflow='scroll'.
228
+ */
229
+ overflowIndicator?: boolean
230
+ }
231
+
232
+ /**
233
+ * Props for Text component.
234
+ */
235
+ export interface TextProps extends StyleProps, TestProps, MouseEventProps {
236
+ children?: React.ReactNode
237
+ wrap?: "wrap" | "truncate" | "truncate-start" | "truncate-middle" | "truncate-end" | "clip" | boolean
238
+ /** Internal transform function applied to each rendered line. Used by Transform component. */
239
+ internal_transform?: (line: string, index: number) => string
240
+ }
241
+
242
+ /**
243
+ * The core Silvery node - represents an element in the render tree.
244
+ *
245
+ * Each node has:
246
+ * - A Yoga node for layout calculation
247
+ * - Computed layout after Yoga runs
248
+ * - Subscribers that get notified when layout changes
249
+ * - Dirty flags for incremental updates
250
+ */
251
+ export interface TeaNode {
252
+ /** Node type */
253
+ type: TeaNodeType
254
+
255
+ /** Props passed to this node */
256
+ props: BoxProps | TextProps | Record<string, unknown>
257
+
258
+ /** Child nodes */
259
+ children: TeaNode[]
260
+
261
+ /** Parent node (null for root) */
262
+ parent: TeaNode | null
263
+
264
+ /** The layout node for layout calculation (null for raw text nodes) */
265
+ layoutNode: LayoutNode | null
266
+
267
+ /** Computed layout from previous render (for change detection) */
268
+ prevLayout: Rect | null
269
+
270
+ /**
271
+ * Content-relative position (like CSS offsetTop/offsetLeft).
272
+ * Position within the scrollable content, ignoring scroll offsets.
273
+ * Set after layout phase.
274
+ */
275
+ contentRect: Rect | null
276
+
277
+ /**
278
+ * Screen-relative position (like CSS getBoundingClientRect).
279
+ * Actual position on the terminal screen, accounting for scroll offsets.
280
+ * Set after screen rect phase.
281
+ *
282
+ * Note: For sticky children, this reflects the node's layout position
283
+ * adjusted for scroll offsets, NOT the actual render position. Use
284
+ * `renderRect` for the actual pixel position on screen.
285
+ */
286
+ screenRect: Rect | null
287
+
288
+ /** Previous screen rect (for change detection in notifyLayoutSubscribers) */
289
+ prevScreenRect: Rect | null
290
+
291
+ /**
292
+ * Actual render position on the terminal screen.
293
+ * For non-sticky nodes, this equals `screenRect`.
294
+ * For sticky nodes (position="sticky"), this accounts for sticky render
295
+ * offsets — the position where pixels are actually painted.
296
+ *
297
+ * Use this for hit testing, cursor positioning, and any feature that
298
+ * needs to know where a node visually appears on screen.
299
+ * Set after screen rect phase.
300
+ */
301
+ renderRect: Rect | null
302
+
303
+ /** Previous render rect (for change detection) */
304
+ prevRenderRect: Rect | null
305
+
306
+ /** True if layout changed THIS frame (position or size).
307
+ * Set by propagateLayout in layout phase. Cleared by content phase.
308
+ * This is the authoritative signal for "did layout change?" — unlike
309
+ * !rectEqual(prevLayout, contentRect) which becomes stale when layout
310
+ * phase skips (no dirty nodes). */
311
+ layoutChangedThisFrame: boolean
312
+
313
+ /** True if layout-affecting props changed and Yoga needs recalculation.
314
+ * Set by reconciler on prop changes. Cleared after layout phase. */
315
+ layoutDirty: boolean
316
+
317
+ /** True if content changed but layout didn't (e.g., text content update).
318
+ * Set by reconciler. Cleared by content phase after rendering.
319
+ * NOTE: measure phase may clear this for its text-collection cache —
320
+ * paintDirty acts as the surviving witness for style changes. */
321
+ contentDirty: boolean
322
+
323
+ /** True if visual props changed (color, backgroundColor, borderStyle, etc.).
324
+ * Set by reconciler alongside contentDirty. Survives measure phase clearing
325
+ * of contentDirty, ensuring content phase still detects style changes.
326
+ * Cleared by content phase after rendering. */
327
+ paintDirty: boolean
328
+
329
+ /** True if backgroundColor specifically changed (added, modified, or removed).
330
+ * Set by reconciler when backgroundColor prop changes. Used by content phase
331
+ * to avoid cascading re-renders for border-only paint changes (borderColor
332
+ * doesn't affect the content area). Cleared by content phase. */
333
+ bgDirty: boolean
334
+
335
+ /** True if this node or any descendant has dirty content/layout.
336
+ * Propagated upward by reconciler when any descendant is dirtied.
337
+ * When only subtreeDirty (no other flags), the node's OWN rendering is
338
+ * skipped — only descendants are traversed. Cleared by content phase. */
339
+ subtreeDirty: boolean
340
+
341
+ /** True if direct children were added, removed, or reordered.
342
+ * Set by reconciler on child list changes. Triggers own repaint
343
+ * (gap regions may need clearing) and forces child re-render.
344
+ * Cleared by content phase. */
345
+ childrenDirty: boolean
346
+
347
+ /** Callbacks subscribed to layout changes (used by useContentRect) */
348
+ layoutSubscribers: Set<() => void>
349
+
350
+ /** Text content for text nodes */
351
+ textContent?: string
352
+
353
+ /** True if this is a raw text node (created by createTextInstance) */
354
+ isRawText?: boolean
355
+
356
+ /** True if this node is hidden (for Suspense support) */
357
+ hidden?: boolean
358
+
359
+ /** Sticky children with computed render positions (for non-scroll containers).
360
+ * When a parent has sticky children but is NOT a scroll container, this array
361
+ * holds the computed render offsets. Same shape as scrollState.stickyChildren. */
362
+ stickyChildren?: Array<{
363
+ /** Index of the sticky child */
364
+ index: number
365
+ /** Computed Y offset to render at (relative to parent content area) */
366
+ renderOffset: number
367
+ /** Original natural Y position (relative to parent content area) */
368
+ naturalTop: number
369
+ /** Height of the sticky element */
370
+ height: number
371
+ }>
372
+
373
+ /** Scroll state for overflow='scroll' containers */
374
+ scrollState?: {
375
+ /** Current scroll offset (in terminal rows) */
376
+ offset: number
377
+ /** Previous scroll offset from last render (for incremental rendering) */
378
+ prevOffset: number
379
+ /** Total content height (all children) */
380
+ contentHeight: number
381
+ /** Visible height (container height minus borders/padding) */
382
+ viewportHeight: number
383
+ /** Index of first visible child */
384
+ firstVisibleChild: number
385
+ /** Index of last visible child */
386
+ lastVisibleChild: number
387
+ /** Previous first visible child from last render (for incremental rendering) */
388
+ prevFirstVisibleChild: number
389
+ /** Previous last visible child from last render (for incremental rendering) */
390
+ prevLastVisibleChild: number
391
+ /** Count of items hidden above viewport */
392
+ hiddenAbove: number
393
+ /** Count of items hidden below viewport */
394
+ hiddenBelow: number
395
+ /** Sticky children with their computed render positions */
396
+ stickyChildren?: Array<{
397
+ /** Index of the sticky child */
398
+ index: number
399
+ /** Computed Y offset to render at (relative to viewport, not content) */
400
+ renderOffset: number
401
+ /** Original natural Y position (before sticky adjustment) */
402
+ naturalTop: number
403
+ /** Height of the sticky element */
404
+ height: number
405
+ }>
406
+ }
407
+ }
408
+
409
+ // ============================================================================
410
+ // Terminal Buffer Types
411
+ // ============================================================================
412
+
413
+ /**
414
+ * Text attributes that can be applied to a cell.
415
+ */
416
+ export interface CellAttrs {
417
+ bold?: boolean
418
+ dim?: boolean
419
+ italic?: boolean
420
+ /** Simple underline flag (for backwards compatibility) */
421
+ underline?: boolean
422
+ /**
423
+ * Underline style: 'single' | 'double' | 'curly' | 'dotted' | 'dashed'.
424
+ * When set, takes precedence over the underline boolean.
425
+ */
426
+ underlineStyle?: UnderlineStyle
427
+ strikethrough?: boolean
428
+ inverse?: boolean
429
+ }
430
+
431
+ /**
432
+ * A single cell in the terminal buffer.
433
+ */
434
+ export interface Cell {
435
+ /** The character (grapheme cluster) in this cell */
436
+ char: string
437
+ /** Foreground color (ANSI code or RGB) */
438
+ fg: string | null
439
+ /** Background color (ANSI code or RGB) */
440
+ bg: string | null
441
+ /** Text attributes */
442
+ attrs: CellAttrs
443
+ /** True if this is a wide character (CJK) that takes 2 cells */
444
+ wide: boolean
445
+ /** True if this cell is the continuation of a wide character */
446
+ continuation: boolean
447
+ }
448
+
449
+ /**
450
+ * Interface for the terminal buffer.
451
+ */
452
+ export interface TerminalBuffer {
453
+ readonly width: number
454
+ readonly height: number
455
+ getCell(x: number, y: number): Cell
456
+ setCell(x: number, y: number, cell: Cell): void
457
+ clear(): void
458
+ }
459
+
460
+ // ============================================================================
461
+ // Event Types
462
+ // ============================================================================
463
+
464
+ /**
465
+ * Keyboard event with key information and modifiers.
466
+ */
467
+ export interface KeyEvent {
468
+ type: "key"
469
+ /** The key pressed (character or key name like 'ArrowUp') */
470
+ key: string
471
+ /** Ctrl modifier was held */
472
+ ctrl?: boolean
473
+ /** Meta/Alt modifier was held */
474
+ meta?: boolean
475
+ /** Shift modifier was held */
476
+ shift?: boolean
477
+ /** Alt/Option modifier was held */
478
+ alt?: boolean
479
+ /** Super/Cmd modifier was held. Requires Kitty protocol. */
480
+ super?: boolean
481
+ /** Hyper modifier was held. Requires Kitty protocol. */
482
+ hyper?: boolean
483
+ /** Kitty event type. Requires Kitty flag 2. */
484
+ eventType?: "press" | "repeat" | "release"
485
+ /** CapsLock is active. Kitty modifier bit 6. */
486
+ capsLock?: boolean
487
+ /** NumLock is active. Kitty modifier bit 7. */
488
+ numLock?: boolean
489
+ }
490
+
491
+ /**
492
+ * Mouse event with position and button information.
493
+ */
494
+ export interface MouseEvent {
495
+ type: "mouse"
496
+ /** X position in terminal columns (0-indexed) */
497
+ x: number
498
+ /** Y position in terminal rows (0-indexed) */
499
+ y: number
500
+ /** Mouse button (0=left, 1=middle, 2=right) */
501
+ button: number
502
+ /** Event action */
503
+ action: "down" | "up" | "move" | "wheel"
504
+ /** Wheel delta for scroll events */
505
+ delta?: number
506
+ }
507
+
508
+ /**
509
+ * Terminal resize event.
510
+ */
511
+ export interface ResizeEvent {
512
+ type: "resize"
513
+ /** New width in columns */
514
+ width: number
515
+ /** New height in rows */
516
+ height: number
517
+ }
518
+
519
+ /**
520
+ * Terminal focus event.
521
+ */
522
+ export interface FocusEvent {
523
+ type: "focus"
524
+ }
525
+
526
+ /**
527
+ * Terminal blur event.
528
+ */
529
+ export interface BlurEvent {
530
+ type: "blur"
531
+ }
532
+
533
+ /**
534
+ * Signal event (SIGINT, SIGTERM, etc.).
535
+ */
536
+ export interface SignalEvent {
537
+ type: "signal"
538
+ /** Signal name (e.g., 'SIGINT', 'SIGTERM') */
539
+ signal: string
540
+ }
541
+
542
+ /**
543
+ * Custom event for extensibility.
544
+ */
545
+ export interface CustomEvent {
546
+ type: "custom"
547
+ /** Event name */
548
+ name: string
549
+ /** Event data */
550
+ data: unknown
551
+ }
552
+
553
+ /**
554
+ * Union of all event types.
555
+ *
556
+ * Events drive the render loop in interactive mode. When events are present,
557
+ * the render loop runs until exit() is called. When events are absent,
558
+ * the render completes when the UI is stable.
559
+ */
560
+ export type Event = KeyEvent | MouseEvent | ResizeEvent | FocusEvent | BlurEvent | SignalEvent | CustomEvent
561
+
562
+ /**
563
+ * Event source that can be subscribed to and unsubscribed from.
564
+ */
565
+ export interface EventSource {
566
+ /** Subscribe to events, returns unsubscribe function */
567
+ subscribe(handler: (event: Event) => void): () => void
568
+ /** Convert to async iterable */
569
+ [Symbol.asyncIterator](): AsyncIterator<Event>
570
+ }
571
+
572
+ // ============================================================================
573
+ // TermDef - Minimal Render Configuration
574
+ // ============================================================================
575
+
576
+ // ColorLevel is re-exported from ansi in index.ts
577
+ // Import here for use in TermDef
578
+ import type { ColorLevel } from "@silvery/term/ansi"
579
+
580
+ /**
581
+ * Minimal surface for configuring render().
582
+ *
583
+ * TermDef provides a simple way to configure rendering without requiring
584
+ * a full Term instance. It's useful for:
585
+ * - Static rendering (just width/height, no events)
586
+ * - Testing (mock dimensions and events)
587
+ * - Quick scripts (auto-detect everything from stdin/stdout)
588
+ *
589
+ * The presence of `events` (or `stdin` which auto-creates events)
590
+ * determines the render mode:
591
+ * - No events → static mode (render until stable)
592
+ * - Has events → interactive mode (render until exit() called)
593
+ *
594
+ * @example
595
+ * ```tsx
596
+ * // Static render with custom width
597
+ * const output = await render(<App />, { width: 100 })
598
+ *
599
+ * // Interactive with stdin/stdout
600
+ * await render(<App />, { stdin: process.stdin, stdout: process.stdout })
601
+ *
602
+ * // Custom events
603
+ * await render(<App />, { events: myEventSource })
604
+ * ```
605
+ */
606
+ export interface TermDef {
607
+ // -------------------------------------------------------------------------
608
+ // Output Configuration
609
+ // -------------------------------------------------------------------------
610
+
611
+ /** Output stream (used for dimensions if not specified) */
612
+ stdout?: NodeJS.WriteStream
613
+
614
+ /** Width in columns (default: stdout?.columns ?? 80) */
615
+ width?: number
616
+
617
+ /** Height in rows (default: stdout?.rows ?? 24) */
618
+ height?: number
619
+
620
+ /** Color support (true=detect, false=none, or specific level) */
621
+ colors?: boolean | ColorLevel | null
622
+
623
+ // -------------------------------------------------------------------------
624
+ // Input Configuration
625
+ // -------------------------------------------------------------------------
626
+
627
+ /**
628
+ * Event source for interactive mode.
629
+ *
630
+ * When present, render runs until exit() is called.
631
+ * When absent, render completes when UI is stable.
632
+ */
633
+ events?: AsyncIterable<Event> | EventSource
634
+
635
+ /**
636
+ * Standard input stream.
637
+ *
638
+ * When provided (and events is not), automatically creates input events
639
+ * from stdin, enabling interactive mode.
640
+ */
641
+ stdin?: NodeJS.ReadStream
642
+ }
643
+
644
+ // ============================================================================
645
+ // Render Context Types
646
+ // ============================================================================
647
+
648
+ /**
649
+ * Options passed to the render function.
650
+ */
651
+ export interface RenderOptions {
652
+ stdout?: NodeJS.WriteStream
653
+ stdin?: NodeJS.ReadStream
654
+ exitOnCtrlC?: boolean
655
+ debug?: boolean
656
+ }
657
+
658
+ /**
659
+ * The render instance returned by render().
660
+ */
661
+ export interface RenderInstance {
662
+ /** Re-render with new element */
663
+ rerender: (element: React.ReactNode) => void
664
+ /** Unmount and clean up */
665
+ unmount: () => void
666
+ /** Wait for render to complete */
667
+ waitUntilExit: () => Promise<void>
668
+ /** Clear terminal output */
669
+ clear: () => void
670
+ }