@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
package/README.md
ADDED
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
# @mdxui/terminal
|
|
2
|
+
|
|
3
|
+
Universal UI renderer for MDXUI - same components render to terminal, web, and AI agents.
|
|
4
|
+
|
|
5
|
+
> **Alpha Status**: This package is under active development. Some features are experimental or incomplete. APIs may change.
|
|
6
|
+
|
|
7
|
+
**Multi-tier rendering**: Components output to TEXT, MARKDOWN, ASCII, UNICODE, ANSI, or Interactive based on context. AI agents get structured Markdown via MCP. Humans get rich TUIs. UX designers preview in Storybook.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @mdxui/terminal
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { CLI, Box, Text } from '@mdxui/terminal'
|
|
19
|
+
|
|
20
|
+
async function main() {
|
|
21
|
+
const cli = await CLI()
|
|
22
|
+
cli.render(
|
|
23
|
+
<Box border="single">
|
|
24
|
+
<Text color="cyan">Hello Terminal!</Text>
|
|
25
|
+
</Box>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
main()
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Architecture Overview
|
|
33
|
+
|
|
34
|
+
The terminal package implements a **UINode-based rendering architecture** that decouples component definitions from their output format. This enables the same semantic UI tree to render appropriately across different environments.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
38
|
+
│ Input Sources │
|
|
39
|
+
├─────────────────────────────────────────────────────────────┤
|
|
40
|
+
│ JSX Components │ JSON/API Responses │
|
|
41
|
+
│ <Dashboard> │ { type: 'dashboard', ... } │
|
|
42
|
+
│ <Metrics /> │ │
|
|
43
|
+
│ </Dashboard> │ │
|
|
44
|
+
└────────────┬─────────────┴──────────────┬───────────────────┘
|
|
45
|
+
│ │
|
|
46
|
+
▼ ▼
|
|
47
|
+
┌───────────────┐ ┌───────────────┐
|
|
48
|
+
│ compileJSX() │ │ parseUINode() │
|
|
49
|
+
│ JSX Compiler │ │ JSON Parser │
|
|
50
|
+
└───────┬───────┘ └───────┬───────┘
|
|
51
|
+
│ │
|
|
52
|
+
└────────────┬───────────────┘
|
|
53
|
+
▼
|
|
54
|
+
┌───────────────────────┐
|
|
55
|
+
│ UINode │
|
|
56
|
+
│ Universal UI Tree │
|
|
57
|
+
│ (type, props, data, │
|
|
58
|
+
│ children, key) │
|
|
59
|
+
└───────────┬───────────┘
|
|
60
|
+
│
|
|
61
|
+
┌─────────┬───────────┼───────────┬─────────┬─────────────┐
|
|
62
|
+
▼ ▼ ▼ ▼ ▼ ▼
|
|
63
|
+
┌───────┐ ┌───────┐ ┌─────────┐ ┌─────────┐ ┌──────┐ ┌─────────────┐
|
|
64
|
+
│ TEXT │ │ MD │ │ ASCII │ │ UNICODE │ │ ANSI │ │ INTERACTIVE │
|
|
65
|
+
│Tier 1 │ │Tier 2 │ │ Tier 3 │ │ Tier 4 │ │Tier 5│ │ Tier 6 │
|
|
66
|
+
└───────┘ └───────┘ └─────────┘ └─────────┘ └──────┘ └─────────────┘
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Core Types
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// The universal UI tree node
|
|
73
|
+
interface UINode {
|
|
74
|
+
type: string // Component type ('text', 'box', 'table', etc.)
|
|
75
|
+
props: Record<string, unknown> // Component configuration
|
|
76
|
+
children?: UINode[] // Nested components
|
|
77
|
+
data?: unknown // Bound query data for data-driven components
|
|
78
|
+
key?: string // React-style reconciliation key
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Rendering capability level
|
|
82
|
+
type RenderTier = 'text' | 'markdown' | 'ascii' | 'unicode' | 'ansi' | 'interactive'
|
|
83
|
+
|
|
84
|
+
// Context passed during rendering
|
|
85
|
+
interface RenderContext {
|
|
86
|
+
tier: RenderTier
|
|
87
|
+
width: number // Terminal width in columns
|
|
88
|
+
height: number // Terminal height in rows
|
|
89
|
+
depth: number // Current nesting depth
|
|
90
|
+
theme: ThemeTokens // Semantic color tokens
|
|
91
|
+
interactive: boolean
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Package Exports
|
|
96
|
+
|
|
97
|
+
The package provides several entry points for different use cases:
|
|
98
|
+
|
|
99
|
+
| Entry Point | Description |
|
|
100
|
+
|-------------|-------------|
|
|
101
|
+
| `@mdxui/terminal` | Main entry - CLI API, components, hooks, compiler, parser |
|
|
102
|
+
| `@mdxui/terminal/renderers` | All tier renderers (text, markdown, ascii, unicode, ansi, interactive) |
|
|
103
|
+
| `@mdxui/terminal/data` | Data layer with TanStack DB-like API |
|
|
104
|
+
| `@mdxui/terminal/components` | Terminal UI components |
|
|
105
|
+
| `@mdxui/terminal/keyboard` | Keyboard and focus management |
|
|
106
|
+
| `@mdxui/terminal/theme` | Theme utilities and color support |
|
|
107
|
+
| `@mdxui/terminal/storybook` | Storybook integration helpers |
|
|
108
|
+
|
|
109
|
+
## The 6 Render Tiers
|
|
110
|
+
|
|
111
|
+
| Tier | Use Case | Capabilities | Example Output |
|
|
112
|
+
|------|----------|--------------|----------------|
|
|
113
|
+
| **text** | Logs, piping, CI | Plain text only | `Users: 1234, API Calls: 45K` |
|
|
114
|
+
| **markdown** | AI agents, MCP | Tables, links, formatting | `\| Users \| 1,234 \| +12% \|` |
|
|
115
|
+
| **ascii** | Legacy terminals | ASCII box drawing (`+`, `-`, `\|`) | `+------+-------+` |
|
|
116
|
+
| **unicode** | Modern terminals | Unicode box drawing | `┌──────┬───────┐` |
|
|
117
|
+
| **ansi** | Full terminal | Colors, bold, dim | Colored output with ANSI escapes |
|
|
118
|
+
| **interactive** | Human TUI | Keyboard, focus, real-time | Full interactive application |
|
|
119
|
+
|
|
120
|
+
### Using Renderers
|
|
121
|
+
|
|
122
|
+
Import renderers from the dedicated entry point:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import {
|
|
126
|
+
renderText,
|
|
127
|
+
renderMarkdown,
|
|
128
|
+
renderASCII,
|
|
129
|
+
renderUnicode,
|
|
130
|
+
renderANSI,
|
|
131
|
+
createInteractiveRenderer
|
|
132
|
+
} from '@mdxui/terminal/renderers'
|
|
133
|
+
|
|
134
|
+
// Compile JSX to UINode tree
|
|
135
|
+
import { compileJSX } from '@mdxui/terminal'
|
|
136
|
+
const uiTree = compileJSX(<Dashboard metrics={data} />)
|
|
137
|
+
|
|
138
|
+
// Render to different tiers
|
|
139
|
+
const textOutput = renderText(uiTree) // Tier 1: Plain text
|
|
140
|
+
const mdOutput = renderMarkdown(uiTree) // Tier 2: Markdown
|
|
141
|
+
const asciiOutput = renderASCII(uiTree) // Tier 3: ASCII art
|
|
142
|
+
const unicodeOutput = renderUnicode(uiTree) // Tier 4: Unicode
|
|
143
|
+
const ansiOutput = renderANSI(uiTree) // Tier 5: ANSI colors
|
|
144
|
+
// Tier 6: Interactive requires a renderer instance
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## JSX Compiler
|
|
148
|
+
|
|
149
|
+
The compiler converts React JSX trees into UINode trees. It handles:
|
|
150
|
+
|
|
151
|
+
- Fragment flattening
|
|
152
|
+
- Null/undefined/boolean child filtering (React semantics)
|
|
153
|
+
- Key preservation for list reconciliation
|
|
154
|
+
- Deep tree support via iterative compilation
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { compileJSX, compileJSXDeep } from '@mdxui/terminal'
|
|
158
|
+
|
|
159
|
+
// Standard compilation (recursive)
|
|
160
|
+
const node = compileJSX(
|
|
161
|
+
<Box padding={2}>
|
|
162
|
+
<Text bold>Hello</Text>
|
|
163
|
+
<Text>World</Text>
|
|
164
|
+
</Box>
|
|
165
|
+
)
|
|
166
|
+
// Result:
|
|
167
|
+
// {
|
|
168
|
+
// type: 'Box',
|
|
169
|
+
// props: { padding: 2 },
|
|
170
|
+
// children: [
|
|
171
|
+
// { type: 'Text', props: { bold: true }, children: ['Hello'] },
|
|
172
|
+
// { type: 'Text', props: {}, children: ['World'] }
|
|
173
|
+
// ]
|
|
174
|
+
// }
|
|
175
|
+
|
|
176
|
+
// For very deep trees (>1000 levels), use iterative version
|
|
177
|
+
const deepNode = compileJSXDeep(veryDeepElement)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## JSON Parser
|
|
181
|
+
|
|
182
|
+
Parse JSON strings or objects into validated UINode trees with Zod schemas.
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { parseUINode, ParseError } from '@mdxui/terminal'
|
|
186
|
+
|
|
187
|
+
// Parse JSON string
|
|
188
|
+
const node = parseUINode('{"type":"text","props":{"content":"Hello"}}')
|
|
189
|
+
|
|
190
|
+
// Parse object (validates structure)
|
|
191
|
+
const node = parseUINode({
|
|
192
|
+
type: 'table',
|
|
193
|
+
props: { columns: [{ key: 'name', header: 'Name' }] },
|
|
194
|
+
data: [{ name: 'Alice' }, { name: 'Bob' }]
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
// Handle validation errors
|
|
198
|
+
try {
|
|
199
|
+
parseUINode({ props: {} }) // Missing 'type'
|
|
200
|
+
} catch (error) {
|
|
201
|
+
if (error instanceof ParseError) {
|
|
202
|
+
console.error('Invalid UINode:', error.message)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Renderer Examples
|
|
208
|
+
|
|
209
|
+
### Text Renderer (Tier 1)
|
|
210
|
+
|
|
211
|
+
Plain text output with no formatting. Ideal for logs, CI output, or piping.
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { renderText } from '@mdxui/terminal/renderers'
|
|
215
|
+
|
|
216
|
+
const output = renderText({
|
|
217
|
+
type: 'dashboard',
|
|
218
|
+
props: { title: 'Metrics' },
|
|
219
|
+
children: [
|
|
220
|
+
{ type: 'text', props: { content: 'Active Users: 1,234' } }
|
|
221
|
+
]
|
|
222
|
+
})
|
|
223
|
+
// Output:
|
|
224
|
+
// Metrics
|
|
225
|
+
//
|
|
226
|
+
// Active Users: 1,234
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Markdown Renderer (Tier 2)
|
|
230
|
+
|
|
231
|
+
Structured markdown for AI agents consuming via MCP. Tables, links, and formatting preserved.
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { renderMarkdown } from '@mdxui/terminal/renderers'
|
|
235
|
+
|
|
236
|
+
const output = renderMarkdown({
|
|
237
|
+
type: 'table',
|
|
238
|
+
props: {
|
|
239
|
+
columns: [
|
|
240
|
+
{ key: 'name', header: 'Name' },
|
|
241
|
+
{ key: 'value', header: 'Value' }
|
|
242
|
+
],
|
|
243
|
+
data: [
|
|
244
|
+
{ name: 'Users', value: '1,234' },
|
|
245
|
+
{ name: 'Revenue', value: '$45K' }
|
|
246
|
+
]
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
// Output:
|
|
250
|
+
// | Name | Value |
|
|
251
|
+
// | --- | --- |
|
|
252
|
+
// | Users | 1,234 |
|
|
253
|
+
// | Revenue | $45K |
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### ASCII Renderer (Tier 3)
|
|
257
|
+
|
|
258
|
+
Pure ASCII characters for maximum compatibility. No unicode, no ANSI codes.
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import { renderASCII } from '@mdxui/terminal/renderers'
|
|
262
|
+
|
|
263
|
+
const output = renderASCII({
|
|
264
|
+
type: 'box',
|
|
265
|
+
props: { border: 'single' },
|
|
266
|
+
children: [{ type: 'text', props: { content: 'Hello' } }]
|
|
267
|
+
})
|
|
268
|
+
// Output:
|
|
269
|
+
// +-------+
|
|
270
|
+
// | Hello |
|
|
271
|
+
// +-------+
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Unicode Renderer (Tier 4)
|
|
275
|
+
|
|
276
|
+
Beautiful box-drawing characters for modern terminals. No colors.
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import { renderUnicode } from '@mdxui/terminal/renderers'
|
|
280
|
+
|
|
281
|
+
const output = renderUnicode({
|
|
282
|
+
type: 'box',
|
|
283
|
+
props: { border: 'rounded' },
|
|
284
|
+
children: [{ type: 'text', props: { content: 'Hello' } }]
|
|
285
|
+
})
|
|
286
|
+
// Output:
|
|
287
|
+
// ╭───────╮
|
|
288
|
+
// │ Hello │
|
|
289
|
+
// ╰───────╯
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### ANSI Renderer (Tier 5)
|
|
293
|
+
|
|
294
|
+
Full color support with 16, 256, or truecolor modes. Supports theming and color degradation.
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
import { renderANSI } from '@mdxui/terminal/renderers'
|
|
298
|
+
|
|
299
|
+
const output = renderANSI(
|
|
300
|
+
{ type: 'text', props: { content: 'Error!', color: 'red', bold: true } },
|
|
301
|
+
{ colorSupport: 'truecolor', theme: 'dark' }
|
|
302
|
+
)
|
|
303
|
+
// Output: Bold red text with ANSI escape codes
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Interactive Renderer (Tier 6)
|
|
307
|
+
|
|
308
|
+
Full TUI with keyboard navigation, focus management, and real-time updates.
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import { createInteractiveRenderer } from '@mdxui/terminal/renderers'
|
|
312
|
+
|
|
313
|
+
const renderer = await createInteractiveRenderer({
|
|
314
|
+
vimBindings: true,
|
|
315
|
+
wrapFocus: true
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
// Register focusable elements
|
|
319
|
+
renderer.registerFocusable('btn-1', { tabIndex: 0, onActivate: handleClick })
|
|
320
|
+
|
|
321
|
+
// Handle keyboard input
|
|
322
|
+
renderer.onKeyPress('enter', () => console.log('Pressed!'))
|
|
323
|
+
|
|
324
|
+
// Start the TUI
|
|
325
|
+
renderer.start()
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Supported Components
|
|
329
|
+
|
|
330
|
+
### Primitives
|
|
331
|
+
|
|
332
|
+
| Component | Description | Key Props |
|
|
333
|
+
|-----------|-------------|-----------|
|
|
334
|
+
| `text` | Styled text | `content`, `bold`, `italic`, `color` |
|
|
335
|
+
| `box` | Container with borders | `border`, `padding`, `width`, `height` |
|
|
336
|
+
| `list` | Bullet or numbered list | `items`, `numbered`, `bullet` |
|
|
337
|
+
| `table` | Data table | `columns`, `data`, `headers`, `rows` |
|
|
338
|
+
| `code` | Code block | `code`, `language` |
|
|
339
|
+
| `link` | Hyperlink | `text`, `href` |
|
|
340
|
+
| `button` | Interactive button | `label`, `hotkey`, `action` |
|
|
341
|
+
|
|
342
|
+
### Layout
|
|
343
|
+
|
|
344
|
+
| Component | Description | Key Props |
|
|
345
|
+
|-----------|-------------|-----------|
|
|
346
|
+
| `panel` | Titled panel | `title`, `collapsible`, `collapsed` |
|
|
347
|
+
| `card` | Content card | `title`, `border` |
|
|
348
|
+
| `sidebar` | Navigation sidebar | `nav`, `sections` |
|
|
349
|
+
| `breadcrumb` | Navigation path | `items`, `separator` |
|
|
350
|
+
| `dialog` | Modal dialog | `open`, `title`, `actions` |
|
|
351
|
+
|
|
352
|
+
### Data Display
|
|
353
|
+
|
|
354
|
+
| Component | Description | Key Props |
|
|
355
|
+
|-----------|-------------|-----------|
|
|
356
|
+
| `metrics` | Metric cards | `metrics: [{ label, value, trend }]` |
|
|
357
|
+
| `badge` | Status badge | `variant`, `children` |
|
|
358
|
+
| `progress` | Progress bar | `value`, `max`, `width` |
|
|
359
|
+
| `spinner` | Loading indicator | `label`, `frame` |
|
|
360
|
+
|
|
361
|
+
### Page Templates
|
|
362
|
+
|
|
363
|
+
| Component | Description |
|
|
364
|
+
|-----------|-------------|
|
|
365
|
+
| `dashboard` | Dashboard with metrics and panels |
|
|
366
|
+
| `settings` | Settings page with sections |
|
|
367
|
+
| `hero` | Landing page hero section |
|
|
368
|
+
| `features` | Feature showcase |
|
|
369
|
+
| `pricing` | Pricing tiers |
|
|
370
|
+
| `faq` | FAQ accordion |
|
|
371
|
+
| `footer` | Page footer |
|
|
372
|
+
|
|
373
|
+
## Data Layer
|
|
374
|
+
|
|
375
|
+
The data layer provides reactive data management with offline support, available via `@mdxui/terminal/data`.
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
import {
|
|
379
|
+
createDB,
|
|
380
|
+
createCollection,
|
|
381
|
+
createDOSync,
|
|
382
|
+
DBProvider,
|
|
383
|
+
useQuery,
|
|
384
|
+
useMutation
|
|
385
|
+
} from '@mdxui/terminal/data'
|
|
386
|
+
import { z } from 'zod'
|
|
387
|
+
|
|
388
|
+
// Define schema
|
|
389
|
+
const UserSchema = z.object({
|
|
390
|
+
id: z.string(),
|
|
391
|
+
name: z.string(),
|
|
392
|
+
email: z.string().email(),
|
|
393
|
+
role: z.enum(['admin', 'user', 'guest'])
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
// Create collection
|
|
397
|
+
const usersCollection = createCollection({
|
|
398
|
+
name: 'users',
|
|
399
|
+
schema: UserSchema
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
// Create database with Durable Objects sync
|
|
403
|
+
const db = createDB({
|
|
404
|
+
collections: [usersCollection],
|
|
405
|
+
sync: createDOSync({
|
|
406
|
+
namespaceUrl: 'https://api.example.com/do/workspace',
|
|
407
|
+
reconnect: { enabled: true, maxAttempts: 10 }
|
|
408
|
+
})
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
// Use in React components
|
|
412
|
+
function UserList() {
|
|
413
|
+
const { data, isLoading } = useQuery({
|
|
414
|
+
from: 'users',
|
|
415
|
+
where: { role: 'admin' }
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
const { mutate } = useMutation({
|
|
419
|
+
collection: 'users',
|
|
420
|
+
operation: 'insert'
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
if (isLoading) return <Spinner label="Loading users..." />
|
|
424
|
+
|
|
425
|
+
return (
|
|
426
|
+
<Table
|
|
427
|
+
columns={[
|
|
428
|
+
{ key: 'name', header: 'Name' },
|
|
429
|
+
{ key: 'email', header: 'Email' },
|
|
430
|
+
{ key: 'role', header: 'Role' }
|
|
431
|
+
]}
|
|
432
|
+
data={data}
|
|
433
|
+
/>
|
|
434
|
+
)
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Built-in SaaS Collections
|
|
439
|
+
|
|
440
|
+
Pre-defined schemas for common SaaS patterns:
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
import {
|
|
444
|
+
UsersCollection,
|
|
445
|
+
APIKeysCollection,
|
|
446
|
+
WebhooksCollection,
|
|
447
|
+
TeamsCollection,
|
|
448
|
+
UsageCollection
|
|
449
|
+
} from '@mdxui/terminal/data'
|
|
450
|
+
|
|
451
|
+
const db = createDB({
|
|
452
|
+
collections: [
|
|
453
|
+
UsersCollection(),
|
|
454
|
+
APIKeysCollection(),
|
|
455
|
+
WebhooksCollection(),
|
|
456
|
+
TeamsCollection(),
|
|
457
|
+
UsageCollection()
|
|
458
|
+
]
|
|
459
|
+
})
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Sync Adapter Features
|
|
463
|
+
|
|
464
|
+
The `createDOSync` adapter provides:
|
|
465
|
+
|
|
466
|
+
- **WebSocket connection management** with automatic reconnection
|
|
467
|
+
- **Exponential backoff** with jitter to prevent thundering herd
|
|
468
|
+
- **Offline mutation queue** - changes are queued when offline and synced on reconnect
|
|
469
|
+
- **Connection state observable** for UI feedback
|
|
470
|
+
- **Conflict resolution strategies** - server-wins, client-wins, merge, throw, or custom
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
const sync = createDOSync({
|
|
474
|
+
namespaceUrl: 'https://api.example.com/do/workspace',
|
|
475
|
+
authToken: 'jwt-token',
|
|
476
|
+
reconnect: {
|
|
477
|
+
enabled: true,
|
|
478
|
+
maxAttempts: 10,
|
|
479
|
+
initialDelay: 1000,
|
|
480
|
+
maxDelay: 30000
|
|
481
|
+
},
|
|
482
|
+
conflictResolution: 'server-wins',
|
|
483
|
+
requestTimeout: 10000
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
// Monitor connection state
|
|
487
|
+
sync.onConnectionStateChange((state) => {
|
|
488
|
+
// 'disconnected' | 'connecting' | 'connected' | 'reconnecting'
|
|
489
|
+
updateStatusIndicator(state)
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
// Check pending changes
|
|
493
|
+
const { count, oldestAt } = sync.getQueueStats()
|
|
494
|
+
if (count > 0) {
|
|
495
|
+
showPendingBanner(`${count} changes pending`)
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## Terminal Hooks
|
|
500
|
+
|
|
501
|
+
| Hook | Status | Description |
|
|
502
|
+
|------|--------|-------------|
|
|
503
|
+
| `useTerminalSize` | Implemented | Returns terminal dimensions (uses @opentui/react with fallbacks) |
|
|
504
|
+
| `useTerminal` | Implemented | Returns terminal context including dimensions and color support |
|
|
505
|
+
| `useFocus` | Implemented | Focus management via `@mdxui/terminal/keyboard` |
|
|
506
|
+
| `useTheme` | Implemented | Access current theme and colors |
|
|
507
|
+
| `useKeyboard` | Implemented | Low-level keyboard event handling |
|
|
508
|
+
| `createKeyboardManager` | Implemented | Create keyboard managers with presets (VIM_BINDINGS, COMMON_BINDINGS) |
|
|
509
|
+
|
|
510
|
+
## API Reference
|
|
511
|
+
|
|
512
|
+
### Compiler
|
|
513
|
+
|
|
514
|
+
```typescript
|
|
515
|
+
// Compile JSX to UINode (recursive)
|
|
516
|
+
function compileJSX(element: React.ReactElement): UINode
|
|
517
|
+
|
|
518
|
+
// Compile JSX to UINode (iterative, for deep trees)
|
|
519
|
+
function compileJSXDeep(element: React.ReactElement): UINode
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### Parser
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
// Parse JSON string or object to UINode
|
|
526
|
+
function parseUINode(input: string | unknown): UINode
|
|
527
|
+
|
|
528
|
+
// Parse JSON string only
|
|
529
|
+
function parseUINodeFromJSON(json: string): UINode
|
|
530
|
+
|
|
531
|
+
// Error class for parse failures
|
|
532
|
+
class ParseError extends Error {}
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### Renderers (from `@mdxui/terminal/renderers`)
|
|
536
|
+
|
|
537
|
+
```typescript
|
|
538
|
+
// Text tier
|
|
539
|
+
function renderText(node: UINode, options?: TextRenderOptions): string
|
|
540
|
+
|
|
541
|
+
// Markdown tier
|
|
542
|
+
function renderMarkdown(node: UINode, options?: MarkdownRenderOptions): string
|
|
543
|
+
|
|
544
|
+
// ASCII tier
|
|
545
|
+
function renderASCII(node: UINode, context?: RenderContext): string
|
|
546
|
+
|
|
547
|
+
// Unicode tier
|
|
548
|
+
function renderUnicode(node: UINode, context?: RenderContext): string
|
|
549
|
+
|
|
550
|
+
// ANSI tier
|
|
551
|
+
function renderANSI(node: UINode, options?: ANSIRenderOptions): string
|
|
552
|
+
|
|
553
|
+
// Interactive tier
|
|
554
|
+
function createInteractiveRenderer(config?: InteractiveRendererConfig): Promise<InteractiveRenderer>
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Zod Schemas
|
|
558
|
+
|
|
559
|
+
```typescript
|
|
560
|
+
import { UINodeSchema, RenderTierSchema, ThemeTokensSchema, RenderContextSchema } from '@mdxui/terminal'
|
|
561
|
+
|
|
562
|
+
// Validate a UINode
|
|
563
|
+
const result = UINodeSchema.safeParse(data)
|
|
564
|
+
if (result.success) {
|
|
565
|
+
const node: UINode = result.data
|
|
566
|
+
}
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
## License
|
|
570
|
+
|
|
571
|
+
MIT
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { U as UINode } from './types-Ca8p_p5X.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Text Renderer - Plain text output without formatting
|
|
5
|
+
*
|
|
6
|
+
* The TEXT renderer is the first tier in the Universal Terminal UI's
|
|
7
|
+
* multi-tier rendering architecture. It outputs plain text without any
|
|
8
|
+
* formatting markers, colors, or special characters.
|
|
9
|
+
*
|
|
10
|
+
* Text Tier (tier 1 of 6):
|
|
11
|
+
* - Simple content only, no bold/italic/code markers
|
|
12
|
+
* - Tables rendered as key=value lines (one per line)
|
|
13
|
+
* - Lists rendered as bullet points (- item)
|
|
14
|
+
* - Metrics rendered as "Label: value" format
|
|
15
|
+
* - Nested structures indented (2 spaces per level)
|
|
16
|
+
* - Dashboard layout as labeled sections with separators
|
|
17
|
+
* - All special characters and formatting stripped
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Text render options
|
|
22
|
+
*/
|
|
23
|
+
interface TextRenderOptions {
|
|
24
|
+
/** Current indentation level */
|
|
25
|
+
indent?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Renders a UINode tree to a plain text string.
|
|
29
|
+
*
|
|
30
|
+
* @param node - The UINode tree to render
|
|
31
|
+
* @param options - Optional rendering configuration
|
|
32
|
+
* @returns Plain text string
|
|
33
|
+
*/
|
|
34
|
+
declare function renderText(node: UINode, options?: TextRenderOptions): string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* ANSI to CSS Converter
|
|
38
|
+
*
|
|
39
|
+
* Converts ANSI escape codes to CSS styles for web rendering.
|
|
40
|
+
* This enables browser-based terminal emulators to display styled output.
|
|
41
|
+
*
|
|
42
|
+
* Supports:
|
|
43
|
+
* - 16 basic colors (30-37, 40-47)
|
|
44
|
+
* - Bright colors (90-97, 100-107)
|
|
45
|
+
* - 256-color mode (38;5;n and 48;5;n)
|
|
46
|
+
* - True color / 24-bit RGB (38;2;r;g;b and 48;2;r;g;b)
|
|
47
|
+
* - Text styles (bold, dim, italic, underline, strikethrough, inverse)
|
|
48
|
+
* - Reset codes (0, 22, 23, 24, 39, 49)
|
|
49
|
+
*
|
|
50
|
+
* @module
|
|
51
|
+
*/
|
|
52
|
+
/**
|
|
53
|
+
* CSS style properties for a styled text span
|
|
54
|
+
*/
|
|
55
|
+
interface CSSStyleProperties {
|
|
56
|
+
color?: string;
|
|
57
|
+
backgroundColor?: string;
|
|
58
|
+
fontWeight?: 'bold' | 'normal';
|
|
59
|
+
fontStyle?: 'italic' | 'normal';
|
|
60
|
+
textDecoration?: 'underline' | 'line-through' | 'none';
|
|
61
|
+
opacity?: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A span of text with associated CSS styles
|
|
65
|
+
*/
|
|
66
|
+
interface StyledSpan {
|
|
67
|
+
text: string;
|
|
68
|
+
style: CSSStyleProperties;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Result of parsing ANSI string to CSS
|
|
72
|
+
*/
|
|
73
|
+
interface ANSIToCSSResult {
|
|
74
|
+
spans: StyledSpan[];
|
|
75
|
+
plainText: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Converts ANSI-escaped string to CSS-styled spans.
|
|
79
|
+
*
|
|
80
|
+
* @param ansiString - String containing ANSI escape codes
|
|
81
|
+
* @returns Object with spans array and plain text
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* const result = ansiToCSS('\x1b[31mRed text\x1b[0m')
|
|
86
|
+
* // result.spans[0].style.color === 'red'
|
|
87
|
+
* // result.plainText === 'Red text'
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
declare function ansiToCSS(ansiString: string): ANSIToCSSResult;
|
|
91
|
+
/**
|
|
92
|
+
* Converts a styled span to an inline CSS style string.
|
|
93
|
+
*
|
|
94
|
+
* @param span - A styled span object
|
|
95
|
+
* @returns CSS inline style string (e.g., "color: red; font-weight: bold")
|
|
96
|
+
*/
|
|
97
|
+
declare function spanToInlineStyle(span: StyledSpan): string;
|
|
98
|
+
/**
|
|
99
|
+
* Converts ANSI-escaped string to HTML with styled span elements.
|
|
100
|
+
*
|
|
101
|
+
* @param ansiString - String containing ANSI escape codes
|
|
102
|
+
* @returns HTML string with styled spans
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* const html = ansiToHTML('\x1b[31mRed text\x1b[0m')
|
|
107
|
+
* // html === '<span style="color: red">Red text</span>'
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
declare function ansiToHTML(ansiString: string): string;
|
|
111
|
+
/**
|
|
112
|
+
* Parses ANSI string to array of styled spans for React rendering.
|
|
113
|
+
*
|
|
114
|
+
* @param ansiString - String containing ANSI escape codes
|
|
115
|
+
* @returns Array of styled spans with React-compatible style objects
|
|
116
|
+
*/
|
|
117
|
+
declare function parseAnsiToSpans(ansiString: string): StyledSpan[];
|
|
118
|
+
|
|
119
|
+
export { type ANSIToCSSResult as A, type CSSStyleProperties as C, type StyledSpan as S, type TextRenderOptions as T, ansiToCSS as a, ansiToHTML as b, parseAnsiToSpans as p, renderText as r, spanToInlineStyle as s };
|