@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.
- package/README.md +571 -0
- package/dist/ansi-css-Sk5mWtdK.d.ts +119 -0
- package/dist/ansi-css-V6JIHGsM.d.ts +119 -0
- package/dist/ansi-css-_3eSEU9d.d.ts +119 -0
- package/dist/chunk-3EFDH7PK.js +5235 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-3X5IR6WE.js +884 -0
- package/dist/chunk-4FV5ZDCE.js +5236 -0
- package/dist/chunk-4OVMSF2J.js +243 -0
- package/dist/chunk-63FEETIS.js +4048 -0
- package/dist/chunk-B43KP7XJ.js +884 -0
- package/dist/chunk-BMTJXWUV.js +655 -0
- package/dist/chunk-C3SVH4N7.js +882 -0
- package/dist/chunk-EVWR7Y47.js +874 -0
- package/dist/chunk-F6A5VWUC.js +1285 -0
- package/dist/chunk-FD7KW7GE.js +882 -0
- package/dist/chunk-GBQ6UD6I.js +655 -0
- package/dist/chunk-GMDD3M6U.js +5227 -0
- package/dist/chunk-JBHRXOXM.js +1058 -0
- package/dist/chunk-JFOO3EYO.js +1182 -0
- package/dist/chunk-JQ5H3WXL.js +1291 -0
- package/dist/chunk-JQD5NASE.js +234 -0
- package/dist/chunk-KRHJP5R7.js +592 -0
- package/dist/chunk-KWF6WVJE.js +962 -0
- package/dist/chunk-LHYQVN3H.js +1038 -0
- package/dist/chunk-M3TLQLGC.js +1032 -0
- package/dist/chunk-MVW4Q5OP.js +240 -0
- package/dist/chunk-NXCZSWLU.js +1294 -0
- package/dist/chunk-O25TNRO6.js +607 -0
- package/dist/chunk-PNECDA2I.js +884 -0
- package/dist/chunk-QIHWRLJR.js +962 -0
- package/dist/chunk-QW5YMQ7K.js +882 -0
- package/dist/chunk-R5U7XKVJ.js +16 -0
- package/dist/chunk-RP2MVQLR.js +962 -0
- package/dist/chunk-TP6RXGXA.js +1087 -0
- package/dist/chunk-TQQSTITZ.js +655 -0
- package/dist/chunk-X24GWXQV.js +1281 -0
- package/dist/components/index.d.ts +802 -0
- package/dist/components/index.js +149 -0
- package/dist/data/index.d.ts +2554 -0
- package/dist/data/index.js +51 -0
- package/dist/forms/index.d.ts +1596 -0
- package/dist/forms/index.js +464 -0
- package/dist/index-CQRFZntR.d.ts +867 -0
- package/dist/index.d.ts +579 -0
- package/dist/index.js +786 -0
- package/dist/interactive-D0JkWosD.d.ts +217 -0
- package/dist/keyboard/index.d.ts +2 -0
- package/dist/keyboard/index.js +43 -0
- package/dist/renderers/index.d.ts +546 -0
- package/dist/renderers/index.js +2157 -0
- package/dist/storybook/index.d.ts +396 -0
- package/dist/storybook/index.js +641 -0
- package/dist/theme/index.d.ts +1339 -0
- package/dist/theme/index.js +123 -0
- package/dist/types-Bxu5PAgA.d.ts +710 -0
- package/dist/types-CIlop5Ji.d.ts +701 -0
- package/dist/types-Ca8p_p5X.d.ts +710 -0
- package/package.json +90 -0
- package/src/__tests__/components/data/card.test.ts +458 -0
- package/src/__tests__/components/data/list.test.ts +473 -0
- package/src/__tests__/components/data/metrics.test.ts +541 -0
- package/src/__tests__/components/data/table.test.ts +448 -0
- package/src/__tests__/components/input/field.test.ts +555 -0
- package/src/__tests__/components/input/form.test.ts +870 -0
- package/src/__tests__/components/input/search.test.ts +1238 -0
- package/src/__tests__/components/input/select.test.ts +658 -0
- package/src/__tests__/components/navigation/breadcrumb.test.ts +923 -0
- package/src/__tests__/components/navigation/command-palette.test.ts +1095 -0
- package/src/__tests__/components/navigation/sidebar.test.ts +1018 -0
- package/src/__tests__/components/navigation/tabs.test.ts +995 -0
- package/src/__tests__/components.test.tsx +1197 -0
- package/src/__tests__/core/compiler.test.ts +986 -0
- package/src/__tests__/core/parser.test.ts +785 -0
- package/src/__tests__/core/tier-switcher.test.ts +1103 -0
- package/src/__tests__/core/types.test.ts +1398 -0
- package/src/__tests__/data/collections.test.ts +1337 -0
- package/src/__tests__/data/db.test.ts +1265 -0
- package/src/__tests__/data/reactive.test.ts +1010 -0
- package/src/__tests__/data/sync.test.ts +1614 -0
- package/src/__tests__/errors.test.ts +660 -0
- package/src/__tests__/forms/integration.test.ts +444 -0
- package/src/__tests__/integration.test.ts +905 -0
- package/src/__tests__/keyboard.test.ts +1791 -0
- package/src/__tests__/renderer.test.ts +489 -0
- package/src/__tests__/renderers/ansi-css.test.ts +948 -0
- package/src/__tests__/renderers/ansi.test.ts +1366 -0
- package/src/__tests__/renderers/ascii.test.ts +1360 -0
- package/src/__tests__/renderers/interactive.test.ts +2353 -0
- package/src/__tests__/renderers/markdown.test.ts +1483 -0
- package/src/__tests__/renderers/text.test.ts +1369 -0
- package/src/__tests__/renderers/unicode.test.ts +1307 -0
- package/src/__tests__/theme.test.ts +639 -0
- package/src/__tests__/utils/assertions.ts +685 -0
- package/src/__tests__/utils/index.ts +115 -0
- package/src/__tests__/utils/test-renderer.ts +381 -0
- package/src/__tests__/utils/utils.test.ts +560 -0
- package/src/components/containers/card.ts +56 -0
- package/src/components/containers/dialog.ts +53 -0
- package/src/components/containers/index.ts +9 -0
- package/src/components/containers/panel.ts +59 -0
- package/src/components/feedback/badge.ts +40 -0
- package/src/components/feedback/index.ts +8 -0
- package/src/components/feedback/spinner.ts +23 -0
- package/src/components/helpers.ts +81 -0
- package/src/components/index.ts +153 -0
- package/src/components/layout/breadcrumb.ts +31 -0
- package/src/components/layout/index.ts +10 -0
- package/src/components/layout/list.ts +29 -0
- package/src/components/layout/sidebar.ts +79 -0
- package/src/components/layout/table.ts +62 -0
- package/src/components/primitives/box.ts +95 -0
- package/src/components/primitives/button.ts +54 -0
- package/src/components/primitives/index.ts +11 -0
- package/src/components/primitives/input.ts +88 -0
- package/src/components/primitives/select.ts +97 -0
- package/src/components/primitives/text.ts +60 -0
- package/src/components/render.ts +155 -0
- package/src/components/templates/app.ts +43 -0
- package/src/components/templates/index.ts +8 -0
- package/src/components/templates/site.ts +54 -0
- package/src/components/types.ts +777 -0
- package/src/core/compiler.ts +718 -0
- package/src/core/parser.ts +127 -0
- package/src/core/tier-switcher.ts +607 -0
- package/src/core/types.ts +672 -0
- package/src/data/collection.ts +316 -0
- package/src/data/collections.ts +50 -0
- package/src/data/context.tsx +174 -0
- package/src/data/db.ts +127 -0
- package/src/data/hooks.ts +532 -0
- package/src/data/index.ts +138 -0
- package/src/data/reactive.ts +1225 -0
- package/src/data/saas-collections.ts +375 -0
- package/src/data/sync.ts +1213 -0
- package/src/data/types.ts +660 -0
- package/src/forms/converters.ts +512 -0
- package/src/forms/index.ts +133 -0
- package/src/forms/schemas.ts +403 -0
- package/src/forms/types.ts +476 -0
- package/src/index.ts +542 -0
- package/src/keyboard/focus.ts +748 -0
- package/src/keyboard/index.ts +96 -0
- package/src/keyboard/integration.ts +371 -0
- package/src/keyboard/manager.ts +377 -0
- package/src/keyboard/presets.ts +90 -0
- package/src/renderers/ansi-css.ts +576 -0
- package/src/renderers/ansi.ts +802 -0
- package/src/renderers/ascii.ts +680 -0
- package/src/renderers/breadcrumb.ts +480 -0
- package/src/renderers/command-palette.ts +802 -0
- package/src/renderers/components/field.ts +210 -0
- package/src/renderers/components/form.ts +327 -0
- package/src/renderers/components/index.ts +21 -0
- package/src/renderers/components/search.ts +449 -0
- package/src/renderers/components/select.ts +222 -0
- package/src/renderers/index.ts +101 -0
- package/src/renderers/interactive/component-handlers.ts +622 -0
- package/src/renderers/interactive/cursor-manager.ts +147 -0
- package/src/renderers/interactive/focus-manager.ts +279 -0
- package/src/renderers/interactive/index.ts +661 -0
- package/src/renderers/interactive/input-handler.ts +164 -0
- package/src/renderers/interactive/keyboard-handler.ts +212 -0
- package/src/renderers/interactive/mouse-handler.ts +167 -0
- package/src/renderers/interactive/state-manager.ts +109 -0
- package/src/renderers/interactive/types.ts +338 -0
- package/src/renderers/interactive-string.ts +299 -0
- package/src/renderers/interactive.ts +59 -0
- package/src/renderers/markdown.ts +950 -0
- package/src/renderers/sidebar.ts +549 -0
- package/src/renderers/tabs.ts +682 -0
- package/src/renderers/text.ts +791 -0
- package/src/renderers/unicode.ts +917 -0
- package/src/renderers/utils.ts +942 -0
- package/src/router/adapters.ts +383 -0
- package/src/router/types.ts +140 -0
- package/src/router/utils.ts +452 -0
- package/src/schemas.ts +205 -0
- package/src/storybook/index.ts +91 -0
- package/src/storybook/interactive-decorator.tsx +659 -0
- package/src/storybook/keyboard-simulator.ts +501 -0
- package/src/theme/ansi-codes.ts +80 -0
- package/src/theme/box-drawing.ts +132 -0
- package/src/theme/color-convert.ts +254 -0
- package/src/theme/color-support.ts +321 -0
- package/src/theme/index.ts +134 -0
- package/src/theme/strip-ansi.ts +50 -0
- package/src/theme/tailwind-map.ts +469 -0
- package/src/theme/text-styles.ts +206 -0
- package/src/theme/theme-system.ts +568 -0
- 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'
|