@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/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mdxui/terminal",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "OpenTUI renderer for MDXUI primitives - build CLI dashboards with the same components as web",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./components": {
|
|
15
|
+
"import": "./dist/components/index.js",
|
|
16
|
+
"types": "./dist/components/index.d.ts"
|
|
17
|
+
},
|
|
18
|
+
"./data": {
|
|
19
|
+
"import": "./dist/data/index.js",
|
|
20
|
+
"types": "./dist/data/index.d.ts"
|
|
21
|
+
},
|
|
22
|
+
"./forms": {
|
|
23
|
+
"import": "./dist/forms/index.js",
|
|
24
|
+
"types": "./dist/forms/index.d.ts"
|
|
25
|
+
},
|
|
26
|
+
"./keyboard": {
|
|
27
|
+
"import": "./dist/keyboard/index.js",
|
|
28
|
+
"types": "./dist/keyboard/index.d.ts"
|
|
29
|
+
},
|
|
30
|
+
"./renderers": {
|
|
31
|
+
"import": "./dist/renderers/index.js",
|
|
32
|
+
"types": "./dist/renderers/index.d.ts"
|
|
33
|
+
},
|
|
34
|
+
"./storybook": {
|
|
35
|
+
"import": "./dist/storybook/index.js",
|
|
36
|
+
"types": "./dist/storybook/index.d.ts"
|
|
37
|
+
},
|
|
38
|
+
"./theme": {
|
|
39
|
+
"import": "./dist/theme/index.js",
|
|
40
|
+
"types": "./dist/theme/index.d.ts"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsup src/index.ts src/components/index.ts src/data/index.ts src/forms/index.ts src/keyboard/index.ts src/renderers/index.ts src/storybook/index.ts src/theme/index.ts --format esm --dts",
|
|
45
|
+
"dev": "tsup src/index.ts src/components/index.ts src/data/index.ts src/forms/index.ts src/keyboard/index.ts src/renderers/index.ts src/storybook/index.ts src/theme/index.ts --format esm --dts --watch",
|
|
46
|
+
"test": "vitest",
|
|
47
|
+
"typecheck": "tsc --noEmit"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@opentui/core": "^0.1.70",
|
|
51
|
+
"@opentui/react": "^0.1.70"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@testing-library/react": "^16.3.1",
|
|
55
|
+
"@types/react": "^18.3.0",
|
|
56
|
+
"react": "^18.3.0",
|
|
57
|
+
"react-dom": "^18.3.0",
|
|
58
|
+
"tsup": "^8.0.0",
|
|
59
|
+
"typescript": "^5.7.0",
|
|
60
|
+
"vitest": "^2.1.0",
|
|
61
|
+
"zod": "^3.23.0"
|
|
62
|
+
},
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"@mdxui/primitives": "workspace:*",
|
|
65
|
+
"@storybook/react": ">=7.0.0",
|
|
66
|
+
"react": ">=18.0.0",
|
|
67
|
+
"zod": ">=3.20.0"
|
|
68
|
+
},
|
|
69
|
+
"peerDependenciesMeta": {
|
|
70
|
+
"@storybook/react": {
|
|
71
|
+
"optional": true
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"files": [
|
|
75
|
+
"dist",
|
|
76
|
+
"src"
|
|
77
|
+
],
|
|
78
|
+
"keywords": [
|
|
79
|
+
"mdxui",
|
|
80
|
+
"terminal",
|
|
81
|
+
"cli",
|
|
82
|
+
"tui",
|
|
83
|
+
"opentui",
|
|
84
|
+
"dashboard"
|
|
85
|
+
],
|
|
86
|
+
"license": "MIT",
|
|
87
|
+
"publishConfig": {
|
|
88
|
+
"access": "public"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal Card Component Tests (RED phase)
|
|
3
|
+
*
|
|
4
|
+
* TDD RED Phase: These tests define the contract for the Card data component
|
|
5
|
+
* across all 6 render tiers (TEXT, MARKDOWN, ASCII, UNICODE, ANSI, INTERACTIVE).
|
|
6
|
+
*
|
|
7
|
+
* Card Component Features:
|
|
8
|
+
* - Title and subtitle
|
|
9
|
+
* - Key-value pairs display
|
|
10
|
+
* - Actions (buttons, links)
|
|
11
|
+
* - Status badges
|
|
12
|
+
* - Different card variants (default, outlined, elevated)
|
|
13
|
+
*
|
|
14
|
+
* NOTE: These tests are expected to FAIL until implementation is complete.
|
|
15
|
+
* Run: pnpm --filter @mdxui/terminal test -- --run src/__tests__/components/data/card.test.ts
|
|
16
|
+
*/
|
|
17
|
+
import { describe, it, expect } from 'vitest'
|
|
18
|
+
|
|
19
|
+
import type { UINode, RenderContext, ThemeTokens, RenderTier } from '../../../core/types'
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Test Helpers
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
const ALL_TIERS: RenderTier[] = ['text', 'markdown', 'ascii', 'unicode', 'ansi', 'interactive']
|
|
26
|
+
|
|
27
|
+
function createTestTheme(): ThemeTokens {
|
|
28
|
+
return {
|
|
29
|
+
primary: '\x1b[34m',
|
|
30
|
+
secondary: '\x1b[36m',
|
|
31
|
+
muted: '\x1b[90m',
|
|
32
|
+
foreground: '\x1b[37m',
|
|
33
|
+
background: '\x1b[40m',
|
|
34
|
+
border: '\x1b[90m',
|
|
35
|
+
success: '\x1b[32m',
|
|
36
|
+
warning: '\x1b[33m',
|
|
37
|
+
error: '\x1b[31m',
|
|
38
|
+
info: '\x1b[34m',
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function createTestContext(tier: RenderTier, overrides?: Partial<RenderContext>): RenderContext {
|
|
43
|
+
return {
|
|
44
|
+
tier,
|
|
45
|
+
width: 80,
|
|
46
|
+
height: 24,
|
|
47
|
+
depth: 0,
|
|
48
|
+
theme: createTestTheme(),
|
|
49
|
+
interactive: tier === 'interactive',
|
|
50
|
+
...overrides,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function createCardNode(props: Record<string, unknown>, children?: UINode[]): UINode {
|
|
55
|
+
return {
|
|
56
|
+
type: 'card',
|
|
57
|
+
props,
|
|
58
|
+
children,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Renderer import helper - uses dynamic import for all tiers
|
|
64
|
+
// ============================================================================
|
|
65
|
+
|
|
66
|
+
async function renderForTier(node: UINode, tier: RenderTier, ctx?: RenderContext): Promise<string> {
|
|
67
|
+
const context = ctx ?? createTestContext(tier)
|
|
68
|
+
switch (tier) {
|
|
69
|
+
case 'text': {
|
|
70
|
+
const { renderText } = await import('../../../renderers/text')
|
|
71
|
+
return renderText(node)
|
|
72
|
+
}
|
|
73
|
+
case 'markdown': {
|
|
74
|
+
const { renderMarkdown } = await import('../../../renderers/markdown')
|
|
75
|
+
return renderMarkdown(node)
|
|
76
|
+
}
|
|
77
|
+
case 'ascii': {
|
|
78
|
+
const { renderASCII } = await import('../../../renderers/ascii')
|
|
79
|
+
return renderASCII(node, context)
|
|
80
|
+
}
|
|
81
|
+
case 'unicode': {
|
|
82
|
+
const { renderUnicode } = await import('../../../renderers/unicode')
|
|
83
|
+
return renderUnicode(node, context)
|
|
84
|
+
}
|
|
85
|
+
case 'ansi': {
|
|
86
|
+
const { renderANSI } = await import('../../../renderers/ansi')
|
|
87
|
+
return renderANSI(node)
|
|
88
|
+
}
|
|
89
|
+
case 'interactive': {
|
|
90
|
+
const { renderInteractive } = await import('../../../renderers/interactive')
|
|
91
|
+
return renderInteractive(node, context)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// Basic Card Rendering Tests
|
|
98
|
+
// ============================================================================
|
|
99
|
+
|
|
100
|
+
describe('Card Component', () => {
|
|
101
|
+
describe('basic rendering across all tiers', () => {
|
|
102
|
+
ALL_TIERS.forEach((tier) => {
|
|
103
|
+
describe(`${tier.toUpperCase()} tier`, () => {
|
|
104
|
+
it('renders card with title', async () => {
|
|
105
|
+
const node = createCardNode({ title: 'User Profile' })
|
|
106
|
+
const result = await renderForTier(node, tier)
|
|
107
|
+
|
|
108
|
+
expect(result).toContain('User Profile')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('renders card with title and subtitle', async () => {
|
|
112
|
+
const node = createCardNode({
|
|
113
|
+
title: 'Product Info',
|
|
114
|
+
subtitle: 'Premium Widget XL',
|
|
115
|
+
})
|
|
116
|
+
const result = await renderForTier(node, tier)
|
|
117
|
+
|
|
118
|
+
expect(result).toContain('Product Info')
|
|
119
|
+
expect(result).toContain('Premium Widget XL')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('renders card with children content', async () => {
|
|
123
|
+
const node = createCardNode({ title: 'Details' }, [
|
|
124
|
+
{ type: 'text', props: { content: 'This is the card body content.' } },
|
|
125
|
+
])
|
|
126
|
+
const result = await renderForTier(node, tier)
|
|
127
|
+
|
|
128
|
+
expect(result).toContain('Details')
|
|
129
|
+
expect(result).toContain('card body content')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('handles card without title', async () => {
|
|
133
|
+
const node = createCardNode({}, [
|
|
134
|
+
{ type: 'text', props: { content: 'Just content, no title' } },
|
|
135
|
+
])
|
|
136
|
+
const result = await renderForTier(node, tier)
|
|
137
|
+
|
|
138
|
+
expect(result).toContain('Just content')
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('handles empty card', async () => {
|
|
142
|
+
const node = createCardNode({})
|
|
143
|
+
const result = await renderForTier(node, tier)
|
|
144
|
+
|
|
145
|
+
expect(typeof result).toBe('string')
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
// ============================================================================
|
|
152
|
+
// Key-Value Pairs Tests
|
|
153
|
+
// ============================================================================
|
|
154
|
+
|
|
155
|
+
describe('key-value pairs', () => {
|
|
156
|
+
const kvPairs = [
|
|
157
|
+
{ key: 'Name', value: 'John Doe' },
|
|
158
|
+
{ key: 'Email', value: 'john@example.com' },
|
|
159
|
+
{ key: 'Role', value: 'Administrator' },
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
ALL_TIERS.forEach((tier) => {
|
|
163
|
+
describe(`${tier.toUpperCase()} tier`, () => {
|
|
164
|
+
it('renders key-value pairs', async () => {
|
|
165
|
+
const node = createCardNode({
|
|
166
|
+
title: 'User Details',
|
|
167
|
+
pairs: kvPairs,
|
|
168
|
+
})
|
|
169
|
+
const result = await renderForTier(node, tier)
|
|
170
|
+
|
|
171
|
+
expect(result).toContain('Name')
|
|
172
|
+
expect(result).toContain('John Doe')
|
|
173
|
+
expect(result).toContain('Email')
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('handles empty value', async () => {
|
|
177
|
+
const node = createCardNode({
|
|
178
|
+
pairs: [
|
|
179
|
+
{ key: 'Empty', value: '' },
|
|
180
|
+
{ key: 'Null', value: null },
|
|
181
|
+
],
|
|
182
|
+
})
|
|
183
|
+
const result = await renderForTier(node, tier)
|
|
184
|
+
|
|
185
|
+
expect(result).toContain('Empty')
|
|
186
|
+
expect(result).toContain('Null')
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('supports value with status variant', async () => {
|
|
190
|
+
const node = createCardNode({
|
|
191
|
+
pairs: [
|
|
192
|
+
{ key: 'Status', value: 'Active', variant: 'success' },
|
|
193
|
+
],
|
|
194
|
+
})
|
|
195
|
+
const result = await renderForTier(node, tier)
|
|
196
|
+
|
|
197
|
+
expect(result).toContain('Active')
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
// ============================================================================
|
|
204
|
+
// Title Section Tests
|
|
205
|
+
// ============================================================================
|
|
206
|
+
|
|
207
|
+
describe('title section', () => {
|
|
208
|
+
ALL_TIERS.forEach((tier) => {
|
|
209
|
+
describe(`${tier.toUpperCase()} tier`, () => {
|
|
210
|
+
it('renders title with badge', async () => {
|
|
211
|
+
const node = createCardNode({
|
|
212
|
+
title: 'Notifications',
|
|
213
|
+
badge: { content: '5', variant: 'info' },
|
|
214
|
+
})
|
|
215
|
+
const result = await renderForTier(node, tier)
|
|
216
|
+
|
|
217
|
+
expect(result).toContain('Notifications')
|
|
218
|
+
expect(result).toContain('5')
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
it('renders title with action button', async () => {
|
|
222
|
+
const node = createCardNode({
|
|
223
|
+
title: 'Recent Activity',
|
|
224
|
+
titleAction: { label: 'View All', action: '/activity' },
|
|
225
|
+
})
|
|
226
|
+
const result = await renderForTier(node, tier)
|
|
227
|
+
|
|
228
|
+
expect(result).toContain('Recent Activity')
|
|
229
|
+
expect(result).toContain('View All')
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
// ============================================================================
|
|
236
|
+
// Actions Tests
|
|
237
|
+
// ============================================================================
|
|
238
|
+
|
|
239
|
+
describe('card actions', () => {
|
|
240
|
+
ALL_TIERS.forEach((tier) => {
|
|
241
|
+
describe(`${tier.toUpperCase()} tier`, () => {
|
|
242
|
+
it('renders single action button', async () => {
|
|
243
|
+
const node = createCardNode({
|
|
244
|
+
title: 'Confirm Order',
|
|
245
|
+
actions: [{ label: 'Confirm', action: 'confirm', variant: 'primary' }],
|
|
246
|
+
})
|
|
247
|
+
const result = await renderForTier(node, tier)
|
|
248
|
+
|
|
249
|
+
expect(result).toContain('Confirm Order')
|
|
250
|
+
expect(result).toContain('Confirm')
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('renders multiple action buttons', async () => {
|
|
254
|
+
const node = createCardNode({
|
|
255
|
+
title: 'Delete Item',
|
|
256
|
+
actions: [
|
|
257
|
+
{ label: 'Cancel', action: 'cancel' },
|
|
258
|
+
{ label: 'Delete', action: 'delete' },
|
|
259
|
+
],
|
|
260
|
+
})
|
|
261
|
+
const result = await renderForTier(node, tier)
|
|
262
|
+
|
|
263
|
+
expect(result).toContain('Cancel')
|
|
264
|
+
expect(result).toContain('Delete')
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
// ============================================================================
|
|
271
|
+
// Card Variants Tests
|
|
272
|
+
// ============================================================================
|
|
273
|
+
|
|
274
|
+
describe('card variants', () => {
|
|
275
|
+
ALL_TIERS.forEach((tier) => {
|
|
276
|
+
describe(`${tier.toUpperCase()} tier`, () => {
|
|
277
|
+
it('renders default variant', async () => {
|
|
278
|
+
const node = createCardNode({ title: 'Default Card', variant: 'default' })
|
|
279
|
+
const result = await renderForTier(node, tier)
|
|
280
|
+
|
|
281
|
+
expect(result).toContain('Default Card')
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
it('renders outlined variant', async () => {
|
|
285
|
+
const node = createCardNode({ title: 'Outlined Card', variant: 'outlined' })
|
|
286
|
+
const result = await renderForTier(node, tier)
|
|
287
|
+
|
|
288
|
+
expect(result).toContain('Outlined Card')
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it('renders elevated variant', async () => {
|
|
292
|
+
const node = createCardNode({ title: 'Elevated Card', variant: 'elevated' })
|
|
293
|
+
const result = await renderForTier(node, tier)
|
|
294
|
+
|
|
295
|
+
expect(result).toContain('Elevated Card')
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
// ============================================================================
|
|
302
|
+
// Tier-Specific Rendering Tests
|
|
303
|
+
// ============================================================================
|
|
304
|
+
|
|
305
|
+
describe('tier-specific rendering', () => {
|
|
306
|
+
describe('TEXT tier', () => {
|
|
307
|
+
it('renders without borders', async () => {
|
|
308
|
+
const node = createCardNode({
|
|
309
|
+
title: 'Simple Card',
|
|
310
|
+
pairs: [{ key: 'Field', value: 'Value' }],
|
|
311
|
+
})
|
|
312
|
+
const result = await renderForTier(node, 'text')
|
|
313
|
+
|
|
314
|
+
expect(result).toContain('Simple Card')
|
|
315
|
+
expect(result).toContain('Field')
|
|
316
|
+
expect(result).toContain('Value')
|
|
317
|
+
expect(result).not.toContain('|')
|
|
318
|
+
expect(result).not.toContain('+')
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
describe('MARKDOWN tier', () => {
|
|
323
|
+
it('renders title as heading', async () => {
|
|
324
|
+
const node = createCardNode({ title: 'Markdown Card' })
|
|
325
|
+
const result = await renderForTier(node, 'markdown')
|
|
326
|
+
|
|
327
|
+
expect(result).toMatch(/#+\s*Markdown Card/)
|
|
328
|
+
})
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
describe('ASCII tier', () => {
|
|
332
|
+
it('renders card with ASCII border', async () => {
|
|
333
|
+
const node = createCardNode({ title: 'ASCII Card', border: true })
|
|
334
|
+
const result = await renderForTier(node, 'ascii')
|
|
335
|
+
|
|
336
|
+
expect(result).toContain('ASCII Card')
|
|
337
|
+
expect(result).toMatch(/[+\-|]/)
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
describe('UNICODE tier', () => {
|
|
342
|
+
it('renders card with unicode box-drawing', async () => {
|
|
343
|
+
const node = createCardNode({ title: 'Unicode Card', border: 'single' })
|
|
344
|
+
const result = await renderForTier(node, 'unicode')
|
|
345
|
+
|
|
346
|
+
expect(result).toContain('Unicode Card')
|
|
347
|
+
expect(result).toMatch(/[─│┌┐└┘├┤┬┴┼]/)
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
it('renders rounded corners', async () => {
|
|
351
|
+
const node = createCardNode({ title: 'Rounded', border: 'rounded' })
|
|
352
|
+
const result = await renderForTier(node, 'unicode')
|
|
353
|
+
|
|
354
|
+
expect(result).toContain('Rounded')
|
|
355
|
+
expect(result).toMatch(/[╭╮╰╯]/)
|
|
356
|
+
})
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
describe('ANSI tier', () => {
|
|
360
|
+
it('renders with ANSI styling', async () => {
|
|
361
|
+
const node = createCardNode({ title: 'Styled Card' })
|
|
362
|
+
const result = await renderForTier(node, 'ansi')
|
|
363
|
+
|
|
364
|
+
expect(result).toMatch(/\x1b\[/)
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
it('applies variant colors', async () => {
|
|
368
|
+
const node = createCardNode({ title: 'Success Card', variant: 'success' })
|
|
369
|
+
const result = await renderForTier(node, 'ansi')
|
|
370
|
+
|
|
371
|
+
expect(result).toContain('Success Card')
|
|
372
|
+
expect(result).toMatch(/\x1b\[\d+m/)
|
|
373
|
+
})
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
describe('INTERACTIVE tier', () => {
|
|
377
|
+
it('renders focusable actions', async () => {
|
|
378
|
+
const node = createCardNode({
|
|
379
|
+
title: 'Interactive Card',
|
|
380
|
+
actions: [{ label: 'Button 1', focusable: true }],
|
|
381
|
+
focusedActionIndex: 0,
|
|
382
|
+
})
|
|
383
|
+
const result = await renderForTier(node, 'interactive')
|
|
384
|
+
|
|
385
|
+
expect(result).toContain('Button 1')
|
|
386
|
+
})
|
|
387
|
+
})
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
// ============================================================================
|
|
391
|
+
// Edge Cases
|
|
392
|
+
// ============================================================================
|
|
393
|
+
|
|
394
|
+
describe('edge cases', () => {
|
|
395
|
+
ALL_TIERS.forEach((tier) => {
|
|
396
|
+
describe(`${tier.toUpperCase()} tier`, () => {
|
|
397
|
+
it('handles very long title', async () => {
|
|
398
|
+
const longTitle = 'A'.repeat(200)
|
|
399
|
+
const node = createCardNode({ title: longTitle })
|
|
400
|
+
const ctx = createTestContext(tier, { width: 40 })
|
|
401
|
+
const result = await renderForTier(node, tier, ctx)
|
|
402
|
+
|
|
403
|
+
expect(result).toContain('A')
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
it('handles special characters in title', async () => {
|
|
407
|
+
const node = createCardNode({ title: 'HTML tags and quotes' })
|
|
408
|
+
const result = await renderForTier(node, tier)
|
|
409
|
+
|
|
410
|
+
expect(result).toContain('HTML')
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
it('handles unicode in content', async () => {
|
|
414
|
+
const node = createCardNode({
|
|
415
|
+
title: 'International Card',
|
|
416
|
+
pairs: [{ key: 'Greeting', value: 'Hello world' }],
|
|
417
|
+
})
|
|
418
|
+
const result = await renderForTier(node, tier)
|
|
419
|
+
|
|
420
|
+
expect(result).toContain('International Card')
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
it('handles empty pairs array', async () => {
|
|
424
|
+
const node = createCardNode({ title: 'Empty Pairs', pairs: [] })
|
|
425
|
+
const result = await renderForTier(node, tier)
|
|
426
|
+
|
|
427
|
+
expect(result).toContain('Empty Pairs')
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
it('handles many key-value pairs', async () => {
|
|
431
|
+
const pairs = Array.from({ length: 20 }, (_, i) => ({
|
|
432
|
+
key: `Key ${i}`,
|
|
433
|
+
value: `Value ${i}`,
|
|
434
|
+
}))
|
|
435
|
+
const node = createCardNode({ title: 'Many Pairs', pairs })
|
|
436
|
+
const result = await renderForTier(node, tier)
|
|
437
|
+
|
|
438
|
+
expect(result).toContain('Key 0')
|
|
439
|
+
expect(result).toContain('Key 19')
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
it('handles nested children', async () => {
|
|
443
|
+
const node = createCardNode({ title: 'Nested' }, [
|
|
444
|
+
{
|
|
445
|
+
type: 'box',
|
|
446
|
+
props: {},
|
|
447
|
+
children: [{ type: 'text', props: { content: 'Deeply nested' } }],
|
|
448
|
+
},
|
|
449
|
+
])
|
|
450
|
+
const result = await renderForTier(node, tier)
|
|
451
|
+
|
|
452
|
+
expect(result).toContain('Nested')
|
|
453
|
+
expect(result).toContain('Deeply nested')
|
|
454
|
+
})
|
|
455
|
+
})
|
|
456
|
+
})
|
|
457
|
+
})
|
|
458
|
+
})
|