@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,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/primitives/button
|
|
3
|
+
*
|
|
4
|
+
* Button component for terminal rendering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { type ReactElement } from 'react'
|
|
8
|
+
import { ANSI } from '../../theme'
|
|
9
|
+
import type { ButtonProps, RenderContext } from '../types'
|
|
10
|
+
import { extractChildren } from '../helpers'
|
|
11
|
+
|
|
12
|
+
export function renderButton(props: ButtonProps, _context: RenderContext): string[] {
|
|
13
|
+
const { children, hotkey, disabled, focused, variant = 'primary' } = props
|
|
14
|
+
const text = extractChildren(children)
|
|
15
|
+
|
|
16
|
+
let style = ''
|
|
17
|
+
let bgColor = ''
|
|
18
|
+
|
|
19
|
+
// Apply variant styling
|
|
20
|
+
switch (variant) {
|
|
21
|
+
case 'primary':
|
|
22
|
+
bgColor = '\x1b[44m' // blue bg
|
|
23
|
+
style = ANSI.bold
|
|
24
|
+
break
|
|
25
|
+
case 'secondary':
|
|
26
|
+
bgColor = '\x1b[100m' // gray bg
|
|
27
|
+
break
|
|
28
|
+
case 'destructive':
|
|
29
|
+
bgColor = '\x1b[41m' // red bg
|
|
30
|
+
style = ANSI.bold
|
|
31
|
+
break
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Build button text
|
|
35
|
+
let buttonText = ` ${text} `
|
|
36
|
+
if (hotkey) {
|
|
37
|
+
buttonText += `[${hotkey}]`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Apply styling
|
|
41
|
+
if (disabled) {
|
|
42
|
+
return [ANSI.dim + `[ ${text} ]` + ANSI.reset]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (focused) {
|
|
46
|
+
return [bgColor + style + '\x1b[37m' + buttonText + ANSI.reset]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return [bgColor + style + buttonText + ANSI.reset]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function Button(props: ButtonProps): ReactElement {
|
|
53
|
+
return React.createElement('button', props)
|
|
54
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/primitives
|
|
3
|
+
*
|
|
4
|
+
* Primitive terminal components.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { renderBox, Box, setRenderElement } from './box'
|
|
8
|
+
export { renderText, Text } from './text'
|
|
9
|
+
export { renderInput, Input } from './input'
|
|
10
|
+
export { renderSelect, Select } from './select'
|
|
11
|
+
export { renderButton, Button } from './button'
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/primitives/input
|
|
3
|
+
*
|
|
4
|
+
* Input component for terminal rendering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { type ReactElement } from 'react'
|
|
8
|
+
import { ANSI } from '../../theme'
|
|
9
|
+
import type { InputProps, RenderContext } from '../types'
|
|
10
|
+
import { visibleLength } from '../helpers'
|
|
11
|
+
|
|
12
|
+
export function renderInput(props: InputProps, context: RenderContext): string[] {
|
|
13
|
+
const {
|
|
14
|
+
value = '',
|
|
15
|
+
placeholder = '',
|
|
16
|
+
label,
|
|
17
|
+
disabled,
|
|
18
|
+
focused = false,
|
|
19
|
+
type,
|
|
20
|
+
cursorPosition,
|
|
21
|
+
width,
|
|
22
|
+
} = props
|
|
23
|
+
const lines: string[] = []
|
|
24
|
+
|
|
25
|
+
// Render label
|
|
26
|
+
if (label) {
|
|
27
|
+
lines.push(label)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Determine display value (mask for password, show placeholder when empty)
|
|
31
|
+
let displayValue: string
|
|
32
|
+
if (type === 'password' && value) {
|
|
33
|
+
displayValue = '*'.repeat(value.length)
|
|
34
|
+
} else if (value) {
|
|
35
|
+
displayValue = value
|
|
36
|
+
} else {
|
|
37
|
+
displayValue = ''
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Calculate input field width
|
|
41
|
+
const inputWidth = width || context.width || 20
|
|
42
|
+
|
|
43
|
+
// Calculate effective cursor position
|
|
44
|
+
const effectiveCursorPosition = cursorPosition !== undefined
|
|
45
|
+
? Math.min(cursorPosition, displayValue.length)
|
|
46
|
+
: displayValue.length
|
|
47
|
+
|
|
48
|
+
// Build the rendered string
|
|
49
|
+
let rendered: string
|
|
50
|
+
|
|
51
|
+
if (disabled) {
|
|
52
|
+
// Disabled state: dim text, no cursor
|
|
53
|
+
const content = displayValue || placeholder
|
|
54
|
+
rendered = ANSI.dim + content + ANSI.reset
|
|
55
|
+
} else if (focused) {
|
|
56
|
+
if (displayValue.length === 0) {
|
|
57
|
+
// Empty value when focused: show cursor at start
|
|
58
|
+
rendered = ANSI.inverse + ' ' + ANSI.reset
|
|
59
|
+
} else {
|
|
60
|
+
// Show cursor at position using inverse video
|
|
61
|
+
const before = displayValue.slice(0, effectiveCursorPosition)
|
|
62
|
+
const cursorChar = displayValue[effectiveCursorPosition] || ' '
|
|
63
|
+
const after = displayValue.slice(effectiveCursorPosition + 1)
|
|
64
|
+
rendered = ANSI.underline + before + ANSI.reset + ANSI.inverse + cursorChar + ANSI.reset + ANSI.underline + after + ANSI.reset
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
// Unfocused state
|
|
68
|
+
if (displayValue.length === 0 && placeholder) {
|
|
69
|
+
// Show placeholder when empty and unfocused
|
|
70
|
+
rendered = ANSI.dim + placeholder + ANSI.reset
|
|
71
|
+
} else {
|
|
72
|
+
rendered = displayValue
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Pad to width (accounting for ANSI codes)
|
|
77
|
+
const visible = visibleLength(rendered)
|
|
78
|
+
const padding = ' '.repeat(Math.max(0, inputWidth - visible))
|
|
79
|
+
rendered = rendered + padding
|
|
80
|
+
|
|
81
|
+
lines.push(rendered)
|
|
82
|
+
|
|
83
|
+
return lines
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function Input(props: InputProps): ReactElement {
|
|
87
|
+
return React.createElement('input', props)
|
|
88
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/primitives/select
|
|
3
|
+
*
|
|
4
|
+
* Select component for terminal rendering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { type ReactElement } from 'react'
|
|
8
|
+
import { ANSI } from '../../theme'
|
|
9
|
+
import type { SelectProps, RenderContext } from '../types'
|
|
10
|
+
|
|
11
|
+
export function renderSelect<T>(props: SelectProps<T>, _context: RenderContext): string[] {
|
|
12
|
+
const {
|
|
13
|
+
options,
|
|
14
|
+
value,
|
|
15
|
+
label,
|
|
16
|
+
focused = false,
|
|
17
|
+
highlightedIndex = 0,
|
|
18
|
+
maxVisible = 5,
|
|
19
|
+
} = props
|
|
20
|
+
const lines: string[] = []
|
|
21
|
+
|
|
22
|
+
// Render label
|
|
23
|
+
if (label) {
|
|
24
|
+
lines.push(label)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Find current selection
|
|
28
|
+
const selectedIndex = options.findIndex(opt => opt.value === value)
|
|
29
|
+
const selected = selectedIndex >= 0 ? options[selectedIndex] : null
|
|
30
|
+
|
|
31
|
+
if (focused) {
|
|
32
|
+
// Calculate visible range based on highlighted index
|
|
33
|
+
let startIdx = 0
|
|
34
|
+
let endIdx = options.length
|
|
35
|
+
|
|
36
|
+
if (options.length > maxVisible) {
|
|
37
|
+
// Center the highlighted option when possible
|
|
38
|
+
const halfVisible = Math.floor(maxVisible / 2)
|
|
39
|
+
startIdx = Math.max(0, highlightedIndex - halfVisible)
|
|
40
|
+
endIdx = Math.min(options.length, startIdx + maxVisible)
|
|
41
|
+
|
|
42
|
+
// Adjust start if we're near the end
|
|
43
|
+
if (endIdx - startIdx < maxVisible) {
|
|
44
|
+
startIdx = Math.max(0, endIdx - maxVisible)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Show scroll indicator at top if needed
|
|
49
|
+
if (startIdx > 0) {
|
|
50
|
+
lines.push(ANSI.dim + ' \u25B2 more' + ANSI.reset)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Render visible options
|
|
54
|
+
for (let idx = startIdx; idx < endIdx; idx++) {
|
|
55
|
+
const opt = options[idx]
|
|
56
|
+
const isSelected = opt.value === value
|
|
57
|
+
const isHighlighted = idx === highlightedIndex
|
|
58
|
+
|
|
59
|
+
// Prefix: selection marker (filled for selected, empty for unselected)
|
|
60
|
+
const prefix = isSelected ? '\u25CF ' : '\u25CB '
|
|
61
|
+
|
|
62
|
+
let line: string
|
|
63
|
+
|
|
64
|
+
if (opt.disabled) {
|
|
65
|
+
// Disabled option: dim with strikethrough indicator
|
|
66
|
+
line = ANSI.dim + prefix + opt.label + ANSI.reset
|
|
67
|
+
} else if (isHighlighted) {
|
|
68
|
+
// Highlighted option: inverse video (highest priority)
|
|
69
|
+
line = ANSI.inverse + prefix + opt.label + ANSI.reset
|
|
70
|
+
} else if (isSelected) {
|
|
71
|
+
// Selected but not highlighted: bold
|
|
72
|
+
line = ANSI.bold + prefix + opt.label + ANSI.reset
|
|
73
|
+
} else {
|
|
74
|
+
// Normal option: dim
|
|
75
|
+
line = ANSI.dim + prefix + opt.label + ANSI.reset
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
lines.push(line)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Show scroll indicator at bottom if needed
|
|
82
|
+
if (endIdx < options.length) {
|
|
83
|
+
lines.push(ANSI.dim + ' \u25BC more' + ANSI.reset)
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
// Unfocused: show only selected value with dropdown indicator
|
|
87
|
+
const selectedLabel = selected ? selected.label : ''
|
|
88
|
+
const prefix = selected ? '\u25CF ' : '\u25CB '
|
|
89
|
+
lines.push(prefix + selectedLabel + ' \u25BC')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return lines
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function Select<T>(props: SelectProps<T>): ReactElement {
|
|
96
|
+
return React.createElement('select', props as unknown as Record<string, unknown>)
|
|
97
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/primitives/text
|
|
3
|
+
*
|
|
4
|
+
* Text component for terminal rendering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { type ReactElement } from 'react'
|
|
8
|
+
import { ANSI } from '../../theme'
|
|
9
|
+
import type { TextProps, RenderContext } from '../types'
|
|
10
|
+
import { extractChildren, getColorCode, getBgColorCode } from '../helpers'
|
|
11
|
+
|
|
12
|
+
export function renderText(props: TextProps, context: RenderContext): string[] {
|
|
13
|
+
const { children, bold, italic, underline, dim, color, backgroundColor, wrap } = props
|
|
14
|
+
|
|
15
|
+
let text = extractChildren(children)
|
|
16
|
+
|
|
17
|
+
// Build style codes
|
|
18
|
+
const codes: string[] = []
|
|
19
|
+
if (bold) codes.push(ANSI.bold)
|
|
20
|
+
if (italic) codes.push(ANSI.italic)
|
|
21
|
+
if (underline) codes.push(ANSI.underline)
|
|
22
|
+
if (dim) codes.push(ANSI.dim)
|
|
23
|
+
if (color) codes.push(getColorCode(color))
|
|
24
|
+
if (backgroundColor) codes.push(getBgColorCode(backgroundColor))
|
|
25
|
+
|
|
26
|
+
// Handle wrapping
|
|
27
|
+
const maxWidth = context.parentWidth || context.width
|
|
28
|
+
if (wrap === 'truncate' && text.length > maxWidth) {
|
|
29
|
+
text = text.slice(0, maxWidth - 1) + '\u2026'
|
|
30
|
+
} else if (wrap === 'wrap' && text.length > maxWidth) {
|
|
31
|
+
// Word wrap
|
|
32
|
+
const lines: string[] = []
|
|
33
|
+
let remaining = text
|
|
34
|
+
while (remaining.length > 0) {
|
|
35
|
+
if (remaining.length <= maxWidth) {
|
|
36
|
+
lines.push(remaining)
|
|
37
|
+
break
|
|
38
|
+
}
|
|
39
|
+
// Find break point
|
|
40
|
+
let breakPoint = maxWidth
|
|
41
|
+
const lastSpace = remaining.lastIndexOf(' ', maxWidth)
|
|
42
|
+
if (lastSpace > 0) breakPoint = lastSpace
|
|
43
|
+
lines.push(remaining.slice(0, breakPoint))
|
|
44
|
+
remaining = remaining.slice(breakPoint).trim()
|
|
45
|
+
}
|
|
46
|
+
// Apply styling to each line
|
|
47
|
+
return lines.map(line => codes.join('') + line + ANSI.reset)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Apply styling
|
|
51
|
+
if (codes.length > 0) {
|
|
52
|
+
return [codes.join('') + text + ANSI.reset]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return [text]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function Text(props: TextProps): ReactElement {
|
|
59
|
+
return React.createElement('text', props)
|
|
60
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/render
|
|
3
|
+
*
|
|
4
|
+
* Core rendering functions for terminal components.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { type ReactElement } from 'react'
|
|
8
|
+
import type { RenderContext } from './types'
|
|
9
|
+
import {
|
|
10
|
+
isTerminalComponentType,
|
|
11
|
+
isBoxProps,
|
|
12
|
+
isTextProps,
|
|
13
|
+
isTableProps,
|
|
14
|
+
isInputProps,
|
|
15
|
+
isSelectProps,
|
|
16
|
+
isSidebarProps,
|
|
17
|
+
isSidebarItemProps,
|
|
18
|
+
isBreadcrumbProps,
|
|
19
|
+
isBadgeProps,
|
|
20
|
+
isDialogProps,
|
|
21
|
+
isSpinnerProps,
|
|
22
|
+
isButtonProps,
|
|
23
|
+
isPanelProps,
|
|
24
|
+
isListProps,
|
|
25
|
+
isCardProps,
|
|
26
|
+
isPropsObject,
|
|
27
|
+
} from './types'
|
|
28
|
+
import { extractChildren } from './helpers'
|
|
29
|
+
import { renderBox, setRenderElement } from './primitives/box'
|
|
30
|
+
import { renderText } from './primitives/text'
|
|
31
|
+
import { renderInput } from './primitives/input'
|
|
32
|
+
import { renderSelect } from './primitives/select'
|
|
33
|
+
import { renderButton } from './primitives/button'
|
|
34
|
+
import { renderPanel } from './containers/panel'
|
|
35
|
+
import { renderCard } from './containers/card'
|
|
36
|
+
import { renderDialog } from './containers/dialog'
|
|
37
|
+
import { renderSidebar, renderSidebarItem } from './layout/sidebar'
|
|
38
|
+
import { renderBreadcrumb } from './layout/breadcrumb'
|
|
39
|
+
import { renderList } from './layout/list'
|
|
40
|
+
import { renderTable } from './layout/table'
|
|
41
|
+
import { renderBadge } from './feedback/badge'
|
|
42
|
+
import { renderSpinner } from './feedback/spinner'
|
|
43
|
+
|
|
44
|
+
export function renderElement(element: ReactElement, context: RenderContext): string[] {
|
|
45
|
+
const props = element.props
|
|
46
|
+
const type = element.type
|
|
47
|
+
|
|
48
|
+
// Handle function components
|
|
49
|
+
if (typeof type === 'function') {
|
|
50
|
+
const result = (type as (props: unknown) => ReactElement)(props)
|
|
51
|
+
return renderElement(result, context)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Handle terminal component types with proper type guards
|
|
55
|
+
if (isTerminalComponentType(type)) {
|
|
56
|
+
switch (type) {
|
|
57
|
+
case 'box':
|
|
58
|
+
if (isBoxProps(props)) {
|
|
59
|
+
return renderBox(props, context)
|
|
60
|
+
}
|
|
61
|
+
break
|
|
62
|
+
case 'text':
|
|
63
|
+
if (isTextProps(props)) {
|
|
64
|
+
return renderText(props, context)
|
|
65
|
+
}
|
|
66
|
+
break
|
|
67
|
+
case 'table':
|
|
68
|
+
if (isTableProps(props)) {
|
|
69
|
+
return renderTable(props, context)
|
|
70
|
+
}
|
|
71
|
+
break
|
|
72
|
+
case 'input':
|
|
73
|
+
if (isInputProps(props)) {
|
|
74
|
+
return renderInput(props, context)
|
|
75
|
+
}
|
|
76
|
+
break
|
|
77
|
+
case 'select':
|
|
78
|
+
if (isSelectProps(props)) {
|
|
79
|
+
return renderSelect(props, context)
|
|
80
|
+
}
|
|
81
|
+
break
|
|
82
|
+
case 'sidebar':
|
|
83
|
+
if (isSidebarProps(props)) {
|
|
84
|
+
return renderSidebar(props, context)
|
|
85
|
+
}
|
|
86
|
+
break
|
|
87
|
+
case 'sidebar-item':
|
|
88
|
+
if (isSidebarItemProps(props)) {
|
|
89
|
+
return renderSidebarItem(props, context)
|
|
90
|
+
}
|
|
91
|
+
break
|
|
92
|
+
case 'breadcrumb':
|
|
93
|
+
if (isBreadcrumbProps(props)) {
|
|
94
|
+
return renderBreadcrumb(props, context)
|
|
95
|
+
}
|
|
96
|
+
break
|
|
97
|
+
case 'badge':
|
|
98
|
+
if (isBadgeProps(props)) {
|
|
99
|
+
return renderBadge(props, context)
|
|
100
|
+
}
|
|
101
|
+
break
|
|
102
|
+
case 'dialog':
|
|
103
|
+
if (isDialogProps(props)) {
|
|
104
|
+
return renderDialog(props, context)
|
|
105
|
+
}
|
|
106
|
+
break
|
|
107
|
+
case 'spinner':
|
|
108
|
+
if (isSpinnerProps(props)) {
|
|
109
|
+
return renderSpinner(props, context)
|
|
110
|
+
}
|
|
111
|
+
break
|
|
112
|
+
case 'button':
|
|
113
|
+
if (isButtonProps(props)) {
|
|
114
|
+
return renderButton(props, context)
|
|
115
|
+
}
|
|
116
|
+
break
|
|
117
|
+
case 'panel':
|
|
118
|
+
if (isPanelProps(props)) {
|
|
119
|
+
return renderPanel(props, context)
|
|
120
|
+
}
|
|
121
|
+
break
|
|
122
|
+
case 'list':
|
|
123
|
+
if (isListProps(props)) {
|
|
124
|
+
return renderList(props, context)
|
|
125
|
+
}
|
|
126
|
+
break
|
|
127
|
+
case 'card':
|
|
128
|
+
if (isCardProps(props)) {
|
|
129
|
+
return renderCard(props, context)
|
|
130
|
+
}
|
|
131
|
+
break
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Generic element rendering for unknown types or failed type guards
|
|
136
|
+
if (isPropsObject(props)) {
|
|
137
|
+
const childText = extractChildren(props.children as import('react').ReactNode)
|
|
138
|
+
return childText ? [childText] : []
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return []
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Initialize circular dependency for box component
|
|
145
|
+
setRenderElement(renderElement)
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Render a React element to terminal output strings
|
|
149
|
+
*/
|
|
150
|
+
export function renderComponent(element: ReactElement): string[] {
|
|
151
|
+
const context: RenderContext = { width: 80 }
|
|
152
|
+
return renderElement(element, context)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export type { RenderContext }
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/templates/app
|
|
3
|
+
*
|
|
4
|
+
* App template components (mdxui AppComponents interface).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { type ReactElement } from 'react'
|
|
8
|
+
import type {
|
|
9
|
+
AppProps,
|
|
10
|
+
ShellProps,
|
|
11
|
+
SidebarProps,
|
|
12
|
+
AppHeaderProps,
|
|
13
|
+
DashboardProps,
|
|
14
|
+
SettingsProps,
|
|
15
|
+
} from '../types'
|
|
16
|
+
|
|
17
|
+
export function App(props: AppProps): ReactElement {
|
|
18
|
+
return React.createElement('app', props)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function Shell(props: ShellProps): ReactElement {
|
|
22
|
+
return React.createElement('shell', props)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function Sidebar(props: SidebarProps): ReactElement {
|
|
26
|
+
return React.createElement('sidebar', props)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* AppHeader component for the top header bar.
|
|
31
|
+
* Note: The test imports this as Header but it uses AppHeaderProps.
|
|
32
|
+
*/
|
|
33
|
+
export function AppHeader(props: AppHeaderProps): ReactElement {
|
|
34
|
+
return React.createElement('app-header', props)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function Dashboard(props: DashboardProps): ReactElement {
|
|
38
|
+
return React.createElement('dashboard', props)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function Settings(props: SettingsProps): ReactElement {
|
|
42
|
+
return React.createElement('settings', props)
|
|
43
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/templates
|
|
3
|
+
*
|
|
4
|
+
* Template components for mdxui Site and App interfaces.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { Site, Header, Footer, Hero, Features, Pricing, FAQ, LandingPage, Page } from './site'
|
|
8
|
+
export { App, Shell, Sidebar, AppHeader, Dashboard, Settings } from './app'
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/templates/site
|
|
3
|
+
*
|
|
4
|
+
* Site template components (mdxui SiteComponents interface).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { type ReactElement } from 'react'
|
|
8
|
+
import type {
|
|
9
|
+
SiteProps,
|
|
10
|
+
HeaderProps,
|
|
11
|
+
FooterProps,
|
|
12
|
+
HeroProps,
|
|
13
|
+
FeaturesProps,
|
|
14
|
+
PricingProps,
|
|
15
|
+
FAQProps,
|
|
16
|
+
LandingPageProps,
|
|
17
|
+
PageProps,
|
|
18
|
+
} from '../types'
|
|
19
|
+
|
|
20
|
+
export function Site(props: SiteProps): ReactElement {
|
|
21
|
+
return React.createElement('site', props)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function Header(props: HeaderProps): ReactElement {
|
|
25
|
+
return React.createElement('header', props)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function Footer(props: FooterProps): ReactElement {
|
|
29
|
+
return React.createElement('footer', props)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function Hero(props: HeroProps): ReactElement {
|
|
33
|
+
return React.createElement('hero', props)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function Features(props: FeaturesProps): ReactElement {
|
|
37
|
+
return React.createElement('features', props)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function Pricing(props: PricingProps): ReactElement {
|
|
41
|
+
return React.createElement('pricing', props)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function FAQ(props: FAQProps): ReactElement {
|
|
45
|
+
return React.createElement('faq', props)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function LandingPage(props: LandingPageProps): ReactElement {
|
|
49
|
+
return React.createElement('landing-page', props)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function Page(props: PageProps): ReactElement {
|
|
53
|
+
return React.createElement('page', props)
|
|
54
|
+
}
|