@mdxui/terminal 2.0.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.
Files changed (191) hide show
  1. package/README.md +571 -0
  2. package/dist/ansi-css-Sk5mWtdK.d.ts +119 -0
  3. package/dist/ansi-css-V6JIHGsM.d.ts +119 -0
  4. package/dist/ansi-css-_3eSEU9d.d.ts +119 -0
  5. package/dist/chunk-3EFDH7PK.js +5235 -0
  6. package/dist/chunk-3RG5ZIWI.js +10 -0
  7. package/dist/chunk-3X5IR6WE.js +884 -0
  8. package/dist/chunk-4FV5ZDCE.js +5236 -0
  9. package/dist/chunk-4OVMSF2J.js +243 -0
  10. package/dist/chunk-63FEETIS.js +4048 -0
  11. package/dist/chunk-B43KP7XJ.js +884 -0
  12. package/dist/chunk-BMTJXWUV.js +655 -0
  13. package/dist/chunk-C3SVH4N7.js +882 -0
  14. package/dist/chunk-EVWR7Y47.js +874 -0
  15. package/dist/chunk-F6A5VWUC.js +1285 -0
  16. package/dist/chunk-FD7KW7GE.js +882 -0
  17. package/dist/chunk-GBQ6UD6I.js +655 -0
  18. package/dist/chunk-GMDD3M6U.js +5227 -0
  19. package/dist/chunk-JBHRXOXM.js +1058 -0
  20. package/dist/chunk-JFOO3EYO.js +1182 -0
  21. package/dist/chunk-JQ5H3WXL.js +1291 -0
  22. package/dist/chunk-JQD5NASE.js +234 -0
  23. package/dist/chunk-KRHJP5R7.js +592 -0
  24. package/dist/chunk-KWF6WVJE.js +962 -0
  25. package/dist/chunk-LHYQVN3H.js +1038 -0
  26. package/dist/chunk-M3TLQLGC.js +1032 -0
  27. package/dist/chunk-MVW4Q5OP.js +240 -0
  28. package/dist/chunk-NXCZSWLU.js +1294 -0
  29. package/dist/chunk-O25TNRO6.js +607 -0
  30. package/dist/chunk-PNECDA2I.js +884 -0
  31. package/dist/chunk-QIHWRLJR.js +962 -0
  32. package/dist/chunk-QW5YMQ7K.js +882 -0
  33. package/dist/chunk-R5U7XKVJ.js +16 -0
  34. package/dist/chunk-RP2MVQLR.js +962 -0
  35. package/dist/chunk-TP6RXGXA.js +1087 -0
  36. package/dist/chunk-TQQSTITZ.js +655 -0
  37. package/dist/chunk-X24GWXQV.js +1281 -0
  38. package/dist/components/index.d.ts +802 -0
  39. package/dist/components/index.js +149 -0
  40. package/dist/data/index.d.ts +2554 -0
  41. package/dist/data/index.js +51 -0
  42. package/dist/forms/index.d.ts +1596 -0
  43. package/dist/forms/index.js +464 -0
  44. package/dist/index-CQRFZntR.d.ts +867 -0
  45. package/dist/index.d.ts +579 -0
  46. package/dist/index.js +786 -0
  47. package/dist/interactive-D0JkWosD.d.ts +217 -0
  48. package/dist/keyboard/index.d.ts +2 -0
  49. package/dist/keyboard/index.js +43 -0
  50. package/dist/renderers/index.d.ts +546 -0
  51. package/dist/renderers/index.js +2157 -0
  52. package/dist/storybook/index.d.ts +396 -0
  53. package/dist/storybook/index.js +641 -0
  54. package/dist/theme/index.d.ts +1339 -0
  55. package/dist/theme/index.js +123 -0
  56. package/dist/types-Bxu5PAgA.d.ts +710 -0
  57. package/dist/types-CIlop5Ji.d.ts +701 -0
  58. package/dist/types-Ca8p_p5X.d.ts +710 -0
  59. package/package.json +90 -0
  60. package/src/__tests__/components/data/card.test.ts +458 -0
  61. package/src/__tests__/components/data/list.test.ts +473 -0
  62. package/src/__tests__/components/data/metrics.test.ts +541 -0
  63. package/src/__tests__/components/data/table.test.ts +448 -0
  64. package/src/__tests__/components/input/field.test.ts +555 -0
  65. package/src/__tests__/components/input/form.test.ts +870 -0
  66. package/src/__tests__/components/input/search.test.ts +1238 -0
  67. package/src/__tests__/components/input/select.test.ts +658 -0
  68. package/src/__tests__/components/navigation/breadcrumb.test.ts +923 -0
  69. package/src/__tests__/components/navigation/command-palette.test.ts +1095 -0
  70. package/src/__tests__/components/navigation/sidebar.test.ts +1018 -0
  71. package/src/__tests__/components/navigation/tabs.test.ts +995 -0
  72. package/src/__tests__/components.test.tsx +1197 -0
  73. package/src/__tests__/core/compiler.test.ts +986 -0
  74. package/src/__tests__/core/parser.test.ts +785 -0
  75. package/src/__tests__/core/tier-switcher.test.ts +1103 -0
  76. package/src/__tests__/core/types.test.ts +1398 -0
  77. package/src/__tests__/data/collections.test.ts +1337 -0
  78. package/src/__tests__/data/db.test.ts +1265 -0
  79. package/src/__tests__/data/reactive.test.ts +1010 -0
  80. package/src/__tests__/data/sync.test.ts +1614 -0
  81. package/src/__tests__/errors.test.ts +660 -0
  82. package/src/__tests__/forms/integration.test.ts +444 -0
  83. package/src/__tests__/integration.test.ts +905 -0
  84. package/src/__tests__/keyboard.test.ts +1791 -0
  85. package/src/__tests__/renderer.test.ts +489 -0
  86. package/src/__tests__/renderers/ansi-css.test.ts +948 -0
  87. package/src/__tests__/renderers/ansi.test.ts +1366 -0
  88. package/src/__tests__/renderers/ascii.test.ts +1360 -0
  89. package/src/__tests__/renderers/interactive.test.ts +2353 -0
  90. package/src/__tests__/renderers/markdown.test.ts +1483 -0
  91. package/src/__tests__/renderers/text.test.ts +1369 -0
  92. package/src/__tests__/renderers/unicode.test.ts +1307 -0
  93. package/src/__tests__/theme.test.ts +639 -0
  94. package/src/__tests__/utils/assertions.ts +685 -0
  95. package/src/__tests__/utils/index.ts +115 -0
  96. package/src/__tests__/utils/test-renderer.ts +381 -0
  97. package/src/__tests__/utils/utils.test.ts +560 -0
  98. package/src/components/containers/card.ts +56 -0
  99. package/src/components/containers/dialog.ts +53 -0
  100. package/src/components/containers/index.ts +9 -0
  101. package/src/components/containers/panel.ts +59 -0
  102. package/src/components/feedback/badge.ts +40 -0
  103. package/src/components/feedback/index.ts +8 -0
  104. package/src/components/feedback/spinner.ts +23 -0
  105. package/src/components/helpers.ts +81 -0
  106. package/src/components/index.ts +153 -0
  107. package/src/components/layout/breadcrumb.ts +31 -0
  108. package/src/components/layout/index.ts +10 -0
  109. package/src/components/layout/list.ts +29 -0
  110. package/src/components/layout/sidebar.ts +79 -0
  111. package/src/components/layout/table.ts +62 -0
  112. package/src/components/primitives/box.ts +95 -0
  113. package/src/components/primitives/button.ts +54 -0
  114. package/src/components/primitives/index.ts +11 -0
  115. package/src/components/primitives/input.ts +88 -0
  116. package/src/components/primitives/select.ts +97 -0
  117. package/src/components/primitives/text.ts +60 -0
  118. package/src/components/render.ts +155 -0
  119. package/src/components/templates/app.ts +43 -0
  120. package/src/components/templates/index.ts +8 -0
  121. package/src/components/templates/site.ts +54 -0
  122. package/src/components/types.ts +777 -0
  123. package/src/core/compiler.ts +718 -0
  124. package/src/core/parser.ts +127 -0
  125. package/src/core/tier-switcher.ts +607 -0
  126. package/src/core/types.ts +672 -0
  127. package/src/data/collection.ts +316 -0
  128. package/src/data/collections.ts +50 -0
  129. package/src/data/context.tsx +174 -0
  130. package/src/data/db.ts +127 -0
  131. package/src/data/hooks.ts +532 -0
  132. package/src/data/index.ts +138 -0
  133. package/src/data/reactive.ts +1225 -0
  134. package/src/data/saas-collections.ts +375 -0
  135. package/src/data/sync.ts +1213 -0
  136. package/src/data/types.ts +660 -0
  137. package/src/forms/converters.ts +512 -0
  138. package/src/forms/index.ts +133 -0
  139. package/src/forms/schemas.ts +403 -0
  140. package/src/forms/types.ts +476 -0
  141. package/src/index.ts +542 -0
  142. package/src/keyboard/focus.ts +748 -0
  143. package/src/keyboard/index.ts +96 -0
  144. package/src/keyboard/integration.ts +371 -0
  145. package/src/keyboard/manager.ts +377 -0
  146. package/src/keyboard/presets.ts +90 -0
  147. package/src/renderers/ansi-css.ts +576 -0
  148. package/src/renderers/ansi.ts +802 -0
  149. package/src/renderers/ascii.ts +680 -0
  150. package/src/renderers/breadcrumb.ts +480 -0
  151. package/src/renderers/command-palette.ts +802 -0
  152. package/src/renderers/components/field.ts +210 -0
  153. package/src/renderers/components/form.ts +327 -0
  154. package/src/renderers/components/index.ts +21 -0
  155. package/src/renderers/components/search.ts +449 -0
  156. package/src/renderers/components/select.ts +222 -0
  157. package/src/renderers/index.ts +101 -0
  158. package/src/renderers/interactive/component-handlers.ts +622 -0
  159. package/src/renderers/interactive/cursor-manager.ts +147 -0
  160. package/src/renderers/interactive/focus-manager.ts +279 -0
  161. package/src/renderers/interactive/index.ts +661 -0
  162. package/src/renderers/interactive/input-handler.ts +164 -0
  163. package/src/renderers/interactive/keyboard-handler.ts +212 -0
  164. package/src/renderers/interactive/mouse-handler.ts +167 -0
  165. package/src/renderers/interactive/state-manager.ts +109 -0
  166. package/src/renderers/interactive/types.ts +338 -0
  167. package/src/renderers/interactive-string.ts +299 -0
  168. package/src/renderers/interactive.ts +59 -0
  169. package/src/renderers/markdown.ts +950 -0
  170. package/src/renderers/sidebar.ts +549 -0
  171. package/src/renderers/tabs.ts +682 -0
  172. package/src/renderers/text.ts +791 -0
  173. package/src/renderers/unicode.ts +917 -0
  174. package/src/renderers/utils.ts +942 -0
  175. package/src/router/adapters.ts +383 -0
  176. package/src/router/types.ts +140 -0
  177. package/src/router/utils.ts +452 -0
  178. package/src/schemas.ts +205 -0
  179. package/src/storybook/index.ts +91 -0
  180. package/src/storybook/interactive-decorator.tsx +659 -0
  181. package/src/storybook/keyboard-simulator.ts +501 -0
  182. package/src/theme/ansi-codes.ts +80 -0
  183. package/src/theme/box-drawing.ts +132 -0
  184. package/src/theme/color-convert.ts +254 -0
  185. package/src/theme/color-support.ts +321 -0
  186. package/src/theme/index.ts +134 -0
  187. package/src/theme/strip-ansi.ts +50 -0
  188. package/src/theme/tailwind-map.ts +469 -0
  189. package/src/theme/text-styles.ts +206 -0
  190. package/src/theme/theme-system.ts +568 -0
  191. package/src/types.ts +103 -0
@@ -0,0 +1,338 @@
1
+ /**
2
+ * @mdxui/terminal Interactive Renderer Types
3
+ *
4
+ * Type definitions for the interactive renderer, including:
5
+ * - Configuration options
6
+ * - Focusable/Clickable options
7
+ * - Input and component options
8
+ * - Internal state types
9
+ *
10
+ * @packageDocumentation
11
+ */
12
+
13
+ // ============================================================================
14
+ // Configuration Types
15
+ // ============================================================================
16
+
17
+ /**
18
+ * Configuration options for creating an interactive renderer
19
+ */
20
+ export interface InteractiveRendererConfig {
21
+ /** Terminal width in columns */
22
+ width?: number
23
+ /** Terminal height in rows */
24
+ height?: number
25
+ /** Enable mouse support */
26
+ useMouse?: boolean
27
+ /** Use alternate screen buffer */
28
+ useAlternateScreen?: boolean
29
+ /** Wrap focus at boundaries */
30
+ wrapFocus?: boolean
31
+ /** Enable vim-style key bindings */
32
+ vimBindings?: boolean
33
+ /** Timeout for key sequences in ms */
34
+ sequenceTimeout?: number
35
+ /** Target frames per second */
36
+ targetFps?: number
37
+ }
38
+
39
+ // ============================================================================
40
+ // Focus Types
41
+ // ============================================================================
42
+
43
+ /**
44
+ * Focusable element options
45
+ */
46
+ export interface FocusableOptions {
47
+ /** Tab order index */
48
+ tabIndex: number
49
+ /** Focus group */
50
+ group?: string
51
+ /** Callback when focused */
52
+ onFocus?: (event: { id: string }) => void
53
+ /** Callback when blurred */
54
+ onBlur?: (event: { id: string }) => void
55
+ /** Callback when activated */
56
+ onActivate?: (event: { id: string }) => void
57
+ /** Callback when toggled */
58
+ onToggle?: (event: { id: string }) => void
59
+ /** Whether element is disabled */
60
+ disabled?: boolean
61
+ /** Column position for grid navigation */
62
+ column?: number
63
+ /** Cursor position for input elements */
64
+ cursorPosition?: { x: number; y: number }
65
+ /** Whether to show cursor when focused */
66
+ showCursor?: boolean
67
+ }
68
+
69
+ /**
70
+ * Internal focusable entry with registration metadata
71
+ */
72
+ export interface FocusableEntry {
73
+ id: string
74
+ options: FocusableOptions
75
+ registrationOrder: number
76
+ }
77
+
78
+ /**
79
+ * Focus trap stack entry
80
+ */
81
+ export interface FocusTrapEntry {
82
+ group: string
83
+ previousFocusId: string | null
84
+ }
85
+
86
+ // ============================================================================
87
+ // Mouse Types
88
+ // ============================================================================
89
+
90
+ /**
91
+ * Clickable area options
92
+ */
93
+ export interface ClickableOptions {
94
+ /** X position */
95
+ x: number
96
+ /** Y position */
97
+ y: number
98
+ /** Width */
99
+ width: number
100
+ /** Height */
101
+ height: number
102
+ /** Z-index for layering */
103
+ zIndex?: number
104
+ /** Whether clicking focuses the element */
105
+ focusable?: boolean
106
+ /** Click handler */
107
+ onClick?: (event: { x: number; y: number; id: string }) => void
108
+ /** Double-click handler */
109
+ onDoubleClick?: () => void
110
+ /** Right-click handler */
111
+ onRightClick?: () => void
112
+ }
113
+
114
+ // ============================================================================
115
+ // Input Types
116
+ // ============================================================================
117
+
118
+ /**
119
+ * Input registration options
120
+ */
121
+ export interface InputOptions {
122
+ /** Current value */
123
+ value: string
124
+ /** Placeholder text */
125
+ placeholder?: string
126
+ /** Input mask */
127
+ mask?: string
128
+ /** Maximum length */
129
+ maxLength?: number
130
+ /** Validation function */
131
+ validate?: (value: string) => boolean
132
+ /** Whether multiline */
133
+ multiline?: boolean
134
+ /** Current cursor index */
135
+ cursorIndex?: number
136
+ /** Change handler */
137
+ onChange?: (value: string) => void
138
+ /** Submit handler */
139
+ onSubmit?: (value: string) => void
140
+ }
141
+
142
+ /**
143
+ * Internal input state with current values
144
+ */
145
+ export interface InputState extends InputOptions {
146
+ currentValue: string
147
+ currentCursorIndex: number
148
+ }
149
+
150
+ // ============================================================================
151
+ // Component Types
152
+ // ============================================================================
153
+
154
+ /**
155
+ * Component registration options
156
+ */
157
+ export interface ComponentOptions {
158
+ /** Component type */
159
+ type: string
160
+ /** Additional options */
161
+ [key: string]: unknown
162
+ }
163
+
164
+ // ============================================================================
165
+ // Keyboard Types
166
+ // ============================================================================
167
+
168
+ /**
169
+ * Key handler with priority
170
+ */
171
+ export interface KeyHandler {
172
+ handler: () => boolean | void
173
+ priority: number
174
+ }
175
+
176
+ // ============================================================================
177
+ // Renderer Interface
178
+ // ============================================================================
179
+
180
+ /**
181
+ * Interactive renderer interface
182
+ */
183
+ export interface InteractiveRenderer {
184
+ // Dimensions
185
+ width: number
186
+ height: number
187
+
188
+ // Lifecycle
189
+ start(): void
190
+ stop(): void
191
+ destroy(): void
192
+ requestRender(): void
193
+
194
+ // Focus management
195
+ focusNext(): void
196
+ focusPrev(): void
197
+ focusById(id: string): void
198
+ getFocusedId(): string | null
199
+ getFocusableIds(): string[]
200
+ registerFocusable(id: string, options: FocusableOptions): void
201
+ unregisterFocusable(id: string): void
202
+ setActiveGroup(group: string): void
203
+ pushFocusTrap(group: string): void
204
+ popFocusTrap(): void
205
+
206
+ // Keyboard management
207
+ onKeyPress(key: string, handler: () => boolean | void, options?: { priority?: number }): void
208
+ offKeyPress(key: string, handler: () => boolean | void): void
209
+ emitKey(key: string): void
210
+ onKeySequence(sequence: string, handler: () => void): void
211
+ getPendingSequence(): string
212
+ onSearchMode(handler: () => void): void
213
+ onCancel(handler: () => void): void
214
+ getMode(): string
215
+
216
+ // Mouse management
217
+ onClick(handler: (event: { x: number; y: number }) => void): void
218
+ offClick(handler: (event: { x: number; y: number }) => void): void
219
+ emitClick(x: number, y: number, options?: { clickCount?: number; button?: string }): void
220
+ registerClickable(id: string, options: ClickableOptions): void
221
+ unregisterClickable(id: string): void
222
+ getClickableAreas(): Array<{ id: string } & ClickableOptions>
223
+ onScroll(handler: (event: { deltaY: number }) => void): void
224
+ emitScroll(x: number, y: number, options: { deltaY: number }): void
225
+
226
+ // Cursor management
227
+ setCursorPosition(x: number, y: number): void
228
+ getCursorPosition(): { x: number; y: number }
229
+ showCursor(): void
230
+ hideCursor(): void
231
+ isCursorVisible(): boolean
232
+ setCursorStyle(style: 'block' | 'underline' | 'bar'): void
233
+ getCursorStyle(): string
234
+ setCursorBlink(blink: boolean): void
235
+ isCursorBlinking(): boolean
236
+ updateCursorPosition(id: string, position: { x: number; y: number }): void
237
+
238
+ // State management
239
+ setState(key: string, value: unknown): void
240
+ getState(key: string): unknown
241
+ subscribe(key: string, subscriber: (newValue: unknown, oldValue: unknown) => void): () => void
242
+ onRender(handler: () => void): void
243
+ getTargetFps(): number
244
+
245
+ // Input management
246
+ registerInput(id: string, options: InputOptions): void
247
+ getInputCursorIndex(id: string): number
248
+
249
+ // Component management
250
+ registerComponent(id: string, options: ComponentOptions): void
251
+ unregisterComponent(id: string): void
252
+ getComponent(id: string): ComponentOptions | undefined
253
+ updateComponent(id: string, updates: Partial<ComponentOptions>): void
254
+ }
255
+
256
+ /**
257
+ * UINode type for rendering
258
+ */
259
+ export interface UINode {
260
+ type: string
261
+ props: Record<string, unknown>
262
+ children?: UINode[]
263
+ data?: unknown
264
+ }
265
+
266
+ // ============================================================================
267
+ // Manager State Types (for internal use)
268
+ // ============================================================================
269
+
270
+ /**
271
+ * Focus manager state
272
+ */
273
+ export interface FocusManagerState {
274
+ focusables: Map<string, FocusableEntry>
275
+ focusedId: string | null
276
+ activeGroup: string | null
277
+ focusTrapStack: FocusTrapEntry[]
278
+ registrationCounter: number
279
+ wrapFocus: boolean
280
+ }
281
+
282
+ /**
283
+ * Keyboard manager state
284
+ */
285
+ export interface KeyboardManagerState {
286
+ keyHandlers: Map<string, KeyHandler[]>
287
+ sequenceHandlers: Map<string, () => void>
288
+ pendingSequence: string
289
+ sequenceTimer: ReturnType<typeof setTimeout> | null
290
+ sequenceTimeout: number
291
+ mode: 'normal' | 'search'
292
+ searchModeHandler: (() => void) | null
293
+ cancelHandler: (() => void) | null
294
+ vimBindings: boolean
295
+ }
296
+
297
+ /**
298
+ * Cursor manager state
299
+ */
300
+ export interface CursorManagerState {
301
+ cursorPosition: { x: number; y: number }
302
+ cursorVisible: boolean
303
+ cursorStyle: 'block' | 'underline' | 'bar'
304
+ cursorBlinking: boolean
305
+ }
306
+
307
+ /**
308
+ * State manager state
309
+ */
310
+ export interface StateManagerState {
311
+ state: Map<string, unknown>
312
+ stateSubscribers: Map<string, Set<(newValue: unknown, oldValue: unknown) => void>>
313
+ renderHandler: (() => void) | null
314
+ targetFps: number
315
+ }
316
+
317
+ /**
318
+ * Mouse manager state
319
+ */
320
+ export interface MouseManagerState {
321
+ clickables: Map<string, ClickableOptions>
322
+ clickHandlers: Array<(event: { x: number; y: number }) => void>
323
+ scrollHandler: ((event: { deltaY: number }) => void) | null
324
+ }
325
+
326
+ /**
327
+ * Input manager state
328
+ */
329
+ export interface InputManagerState {
330
+ inputs: Map<string, InputState>
331
+ }
332
+
333
+ /**
334
+ * Component manager state
335
+ */
336
+ export interface ComponentManagerState {
337
+ components: Map<string, ComponentOptions>
338
+ }
@@ -0,0 +1,299 @@
1
+ /**
2
+ * @mdxui/terminal Interactive String Renderer
3
+ *
4
+ * This module provides a string-based implementation of the interactive tier
5
+ * that does not depend on @opentui/core. It is used for testing and situations
6
+ * where a string output is needed rather than an actual TUI.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+
11
+ import type { UINode, RenderContext } from '../core/types'
12
+
13
+ /**
14
+ * Options for interactive rendering
15
+ */
16
+ export interface InteractiveRenderOptions {
17
+ width?: number
18
+ height?: number
19
+ }
20
+
21
+ /**
22
+ * Renders a UINode to a string for the interactive tier.
23
+ * This provides compatibility with the test suite that expects
24
+ * a string-returning render function like other tiers.
25
+ */
26
+ export function renderInteractive(node: UINode, _context: RenderContext): string {
27
+ return renderInteractiveToString(node, 0)
28
+ }
29
+
30
+ function renderInteractiveToString(node: UINode, depth: number): string {
31
+ const props = node.props || {}
32
+
33
+ switch (node.type) {
34
+ case 'text':
35
+ return (props.content as string) || ''
36
+
37
+ case 'list':
38
+ return renderInteractiveList(node)
39
+
40
+ case 'table':
41
+ return renderInteractiveTable(node)
42
+
43
+ case 'card':
44
+ return renderInteractiveCard(node)
45
+
46
+ case 'metrics':
47
+ return renderInteractiveMetrics(node)
48
+
49
+ case 'metric':
50
+ return renderInteractiveSingleMetric(node)
51
+
52
+ default:
53
+ // For unknown types, try to render children
54
+ if (node.children && Array.isArray(node.children) && node.children.length > 0) {
55
+ return node.children
56
+ .map(child => renderInteractiveToString(child, depth + 1))
57
+ .join('\n')
58
+ }
59
+ return ''
60
+ }
61
+ }
62
+
63
+ function renderInteractiveList(node: UINode): string {
64
+ const props = node.props || {}
65
+ const items = props.items as Array<string | { text: string; checked?: boolean }> | undefined
66
+ const numbered = (props.numbered as boolean) ?? false
67
+ const taskList = (props.taskList as boolean) ?? false
68
+ const style = (props.style as string) || 'unordered'
69
+ const rawChildren = node.children
70
+ const children: UINode[] = Array.isArray(rawChildren) ? rawChildren : []
71
+
72
+ const lines: string[] = []
73
+
74
+ // Handle items prop
75
+ if (items && items.length > 0) {
76
+ items.forEach((item, index) => {
77
+ let text: string
78
+ let marker: string
79
+
80
+ if (typeof item === 'string') {
81
+ text = item
82
+ marker = numbered || style === 'ordered' ? `${index + 1}. ` : '- '
83
+ } else {
84
+ text = item.text ?? ''
85
+ if (taskList && 'checked' in item) {
86
+ marker = item.checked ? '[x] ' : '[ ] '
87
+ } else {
88
+ marker = numbered || style === 'ordered' ? `${index + 1}. ` : '- '
89
+ }
90
+ }
91
+
92
+ lines.push(marker + text)
93
+ })
94
+ }
95
+
96
+ // Handle list-item children (with nested list support)
97
+ children.forEach((child: UINode, index: number) => {
98
+ if (child.type === 'list-item') {
99
+ const content = (child.props?.content as string) || ''
100
+ let marker: string
101
+
102
+ if (numbered || style === 'ordered') {
103
+ marker = `${index + 1}. `
104
+ } else if (taskList || style === 'checklist') {
105
+ const checked = child.props?.checked as boolean
106
+ marker = checked ? '[x] ' : '[ ] '
107
+ } else {
108
+ marker = '- '
109
+ }
110
+
111
+ lines.push(marker + content)
112
+
113
+ // Handle nested lists
114
+ const childChildren = child.children
115
+ if (childChildren && Array.isArray(childChildren) && childChildren.length > 0) {
116
+ for (const nestedChild of childChildren) {
117
+ if (nestedChild.type === 'list') {
118
+ const nestedLines = renderNestedInteractiveList(nestedChild, 1)
119
+ lines.push(...nestedLines)
120
+ }
121
+ }
122
+ }
123
+ }
124
+ })
125
+
126
+ return lines.join('\n')
127
+ }
128
+
129
+ function renderNestedInteractiveList(node: UINode, depth: number): string[] {
130
+ const props = node.props || {}
131
+ const style = (props.style as string) || 'unordered'
132
+ const numbered = (props.numbered as boolean) ?? false
133
+ const rawChildren = node.children
134
+ const children: UINode[] = Array.isArray(rawChildren) ? rawChildren : []
135
+ const lines: string[] = []
136
+ const indent = ' '.repeat(depth)
137
+
138
+ children.forEach((child: UINode, index: number) => {
139
+ if (child.type === 'list-item') {
140
+ const content = (child.props?.content as string) || ''
141
+ let marker: string
142
+
143
+ if (numbered || style === 'ordered') {
144
+ marker = `${index + 1}. `
145
+ } else {
146
+ marker = '- '
147
+ }
148
+
149
+ lines.push(indent + marker + content)
150
+
151
+ // Handle deeper nested lists
152
+ const childChildren = child.children
153
+ if (childChildren && Array.isArray(childChildren) && childChildren.length > 0) {
154
+ for (const nestedChild of childChildren) {
155
+ if (nestedChild.type === 'list') {
156
+ const nestedLines = renderNestedInteractiveList(nestedChild, depth + 1)
157
+ lines.push(...nestedLines)
158
+ }
159
+ }
160
+ }
161
+ }
162
+ })
163
+
164
+ return lines
165
+ }
166
+
167
+ function renderInteractiveTable(node: UINode): string {
168
+ const props = node.props || {}
169
+ const columns = (props.columns as Array<{ key: string; header: string; width?: number }>) || []
170
+ // Support data from node.data (TDD tests) or props.data
171
+ const nodeData = node.data as Array<Record<string, unknown>> | undefined
172
+ const propsData = props.data as Array<Record<string, unknown>> | undefined
173
+ const data = nodeData ?? propsData ?? []
174
+
175
+ if (columns.length === 0) return ''
176
+
177
+ const lines: string[] = []
178
+
179
+ // Header
180
+ const headers = columns.map(c => c.header).join('\t')
181
+ lines.push(headers)
182
+
183
+ // Rows
184
+ for (const row of data) {
185
+ const cells = columns.map(c => {
186
+ const val = row[c.key]
187
+ return val != null ? String(val) : ''
188
+ })
189
+ lines.push(cells.join('\t'))
190
+ }
191
+
192
+ return lines.join('\n')
193
+ }
194
+
195
+ function renderInteractiveCard(node: UINode): string {
196
+ const props = node.props || {}
197
+ const title = props.title as string | undefined
198
+ const subtitle = props.subtitle as string | undefined
199
+ const badge = props.badge as { content: string; variant?: string } | undefined
200
+ const titleAction = props.titleAction as { label: string; action?: string } | undefined
201
+ const pairs = props.pairs as Array<{ key: string; value: unknown }> | undefined
202
+ const actions = props.actions as Array<{ label: string; action?: string }> | undefined
203
+
204
+ const contentLines: string[] = []
205
+
206
+ // Title section
207
+ if (title) {
208
+ let titleLine = title
209
+ if (badge) {
210
+ titleLine += ` [${badge.content}]`
211
+ }
212
+ if (titleAction) {
213
+ titleLine += ` | ${titleAction.label}`
214
+ }
215
+ contentLines.push(titleLine)
216
+ }
217
+
218
+ if (subtitle) {
219
+ contentLines.push(subtitle)
220
+ }
221
+
222
+ // Key-value pairs
223
+ if (pairs && pairs.length > 0) {
224
+ for (const pair of pairs) {
225
+ const val = pair.value != null ? String(pair.value) : ''
226
+ contentLines.push(`${pair.key}: ${val}`)
227
+ }
228
+ }
229
+
230
+ // Children content
231
+ const rawChildren = node.children
232
+ const children: UINode[] = Array.isArray(rawChildren) ? rawChildren : []
233
+ if (children.length > 0) {
234
+ const childContent = children
235
+ .map((child: UINode) => renderInteractiveToString(child, 0))
236
+ .filter((s: string) => s)
237
+ .join('\n')
238
+ if (childContent) {
239
+ contentLines.push(childContent)
240
+ }
241
+ }
242
+
243
+ // Actions
244
+ if (actions && actions.length > 0) {
245
+ const actionLabels = actions.map(a => `[ ${a.label} ]`).join(' ')
246
+ contentLines.push(actionLabels)
247
+ }
248
+
249
+ return contentLines.join('\n')
250
+ }
251
+
252
+ function renderInteractiveMetrics(node: UINode): string {
253
+ const props = node.props || {}
254
+ const metrics = props.metrics as Array<{
255
+ label: string
256
+ value: unknown
257
+ format?: string
258
+ unit?: string
259
+ trend?: string
260
+ }> | undefined
261
+
262
+ if (!metrics || metrics.length === 0) return ''
263
+
264
+ const lines: string[] = []
265
+ for (const m of metrics) {
266
+ const val = m.value != null ? String(m.value) : ''
267
+ let formatted = val
268
+ if (m.format === 'percentage' && !val.includes('%')) {
269
+ formatted = `${val}%`
270
+ }
271
+ if (m.unit) {
272
+ formatted = `${formatted} ${m.unit}`
273
+ }
274
+ lines.push(`${m.label}: ${formatted}`)
275
+ }
276
+
277
+ return lines.join('\n')
278
+ }
279
+
280
+ function renderInteractiveSingleMetric(node: UINode): string {
281
+ const props = node.props || {}
282
+ const label = props.label as string | undefined
283
+ const value = props.value
284
+ const format = props.format as string | undefined
285
+ const unit = props.unit as string | undefined
286
+
287
+ if (!label) return ''
288
+
289
+ const val = value != null ? String(value) : ''
290
+ let formatted = val
291
+ if (format === 'percentage' && !val.includes('%')) {
292
+ formatted = `${val}%`
293
+ }
294
+ if (unit) {
295
+ formatted = `${formatted} ${unit}`
296
+ }
297
+
298
+ return `${label}: ${formatted}`
299
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @mdxui/terminal Interactive Renderer
3
+ *
4
+ * The Interactive renderer is the highest-capability tier providing:
5
+ * - Full TUI with keyboard navigation
6
+ * - Focus management and Tab cycling
7
+ * - Mouse click support
8
+ * - Cursor positioning
9
+ * - Real-time updates
10
+ * - Input field handling
11
+ * - Component state management
12
+ *
13
+ * This file re-exports from the modular implementation in ./interactive/
14
+ *
15
+ * @packageDocumentation
16
+ */
17
+
18
+ // Re-export everything from the modular implementation
19
+ export {
20
+ // Main factory function
21
+ createInteractiveRenderer,
22
+ // Node registration function
23
+ registerInteractiveNode,
24
+ // String-based renderer for testing
25
+ renderInteractive,
26
+ } from './interactive/index'
27
+
28
+ // Re-export all types
29
+ export type {
30
+ // Configuration types
31
+ InteractiveRendererConfig,
32
+ // Focus types
33
+ FocusableOptions,
34
+ FocusableEntry,
35
+ FocusTrapEntry,
36
+ // Mouse types
37
+ ClickableOptions,
38
+ // Input types
39
+ InputOptions,
40
+ InputState,
41
+ // Component types
42
+ ComponentOptions,
43
+ // Keyboard types
44
+ KeyHandler,
45
+ // Main interface
46
+ InteractiveRenderer,
47
+ // Node type
48
+ UINode,
49
+ // Manager state types (for advanced usage)
50
+ FocusManagerState,
51
+ KeyboardManagerState,
52
+ CursorManagerState,
53
+ StateManagerState,
54
+ MouseManagerState,
55
+ InputManagerState,
56
+ ComponentManagerState,
57
+ // String renderer options
58
+ InteractiveRenderOptions,
59
+ } from './interactive/index'