@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,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/containers/panel
|
|
3
|
+
*
|
|
4
|
+
* Panel component for terminal rendering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { type ReactElement } from 'react'
|
|
8
|
+
import { ANSI, boxChars } from '../../theme'
|
|
9
|
+
import type { PanelProps, RenderContext } from '../types'
|
|
10
|
+
import { extractChildren } from '../helpers'
|
|
11
|
+
|
|
12
|
+
export function renderPanel(props: PanelProps, context: RenderContext): string[] {
|
|
13
|
+
const { children, title, border = 'single', collapsed, padding = 0 } = props
|
|
14
|
+
|
|
15
|
+
// If collapsed, show just the title
|
|
16
|
+
if (collapsed) {
|
|
17
|
+
return [ANSI.dim + '\u25B6 ' + ANSI.reset + ANSI.bold + (title || 'Panel') + ANSI.reset]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const lines: string[] = []
|
|
21
|
+
const chars = border === 'double' ? boxChars.double :
|
|
22
|
+
border === 'rounded' ? boxChars.rounded :
|
|
23
|
+
boxChars.single
|
|
24
|
+
const panelWidth = context.width
|
|
25
|
+
|
|
26
|
+
// Top border with title
|
|
27
|
+
if (title && border !== 'none') {
|
|
28
|
+
const titleText = ` ${title} `
|
|
29
|
+
const remainingWidth = panelWidth - titleText.length - 2
|
|
30
|
+
const leftBorder = Math.floor(remainingWidth / 2)
|
|
31
|
+
const rightBorder = remainingWidth - leftBorder
|
|
32
|
+
lines.push(chars.topLeft + chars.horizontal.repeat(leftBorder) + ANSI.bold + titleText + ANSI.reset + chars.horizontal.repeat(rightBorder) + chars.topRight)
|
|
33
|
+
} else if (border !== 'none') {
|
|
34
|
+
lines.push(chars.topLeft + chars.horizontal.repeat(panelWidth - 2) + chars.topRight)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Content
|
|
38
|
+
const content = extractChildren(children)
|
|
39
|
+
const contentLines = content.split('\n')
|
|
40
|
+
const paddingStr = ' '.repeat(padding)
|
|
41
|
+
for (const line of contentLines) {
|
|
42
|
+
if (border !== 'none') {
|
|
43
|
+
lines.push(chars.vertical + paddingStr + line.padEnd(panelWidth - 2 - padding * 2) + paddingStr + chars.vertical)
|
|
44
|
+
} else {
|
|
45
|
+
lines.push(paddingStr + line)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Bottom border
|
|
50
|
+
if (border !== 'none') {
|
|
51
|
+
lines.push(chars.bottomLeft + chars.horizontal.repeat(panelWidth - 2) + chars.bottomRight)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return lines
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function Panel(props: PanelProps): ReactElement {
|
|
58
|
+
return React.createElement('panel', props)
|
|
59
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/feedback/badge
|
|
3
|
+
*
|
|
4
|
+
* Badge component for terminal rendering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { type ReactElement } from 'react'
|
|
8
|
+
import { ANSI } from '../../theme'
|
|
9
|
+
import type { BadgeProps, RenderContext } from '../types'
|
|
10
|
+
import { extractChildren } from '../helpers'
|
|
11
|
+
|
|
12
|
+
export function renderBadge(props: BadgeProps, _context: RenderContext): string[] {
|
|
13
|
+
const { children, variant = 'default' } = props
|
|
14
|
+
const text = extractChildren(children)
|
|
15
|
+
|
|
16
|
+
// Color based on variant
|
|
17
|
+
let color: string
|
|
18
|
+
switch (variant) {
|
|
19
|
+
case 'success':
|
|
20
|
+
color = '\x1b[32m' // green
|
|
21
|
+
break
|
|
22
|
+
case 'warning':
|
|
23
|
+
color = '\x1b[33m' // yellow
|
|
24
|
+
break
|
|
25
|
+
case 'error':
|
|
26
|
+
color = '\x1b[31m' // red
|
|
27
|
+
break
|
|
28
|
+
case 'info':
|
|
29
|
+
color = '\x1b[34m' // blue
|
|
30
|
+
break
|
|
31
|
+
default:
|
|
32
|
+
color = ANSI.dim
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return [color + '[' + text + ']' + ANSI.reset]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function Badge(props: BadgeProps): ReactElement {
|
|
39
|
+
return React.createElement('badge', props)
|
|
40
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/feedback/spinner
|
|
3
|
+
*
|
|
4
|
+
* Spinner component for terminal rendering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { type ReactElement } from 'react'
|
|
8
|
+
import { ANSI } from '../../theme'
|
|
9
|
+
import type { SpinnerProps, RenderContext } from '../types'
|
|
10
|
+
|
|
11
|
+
export const spinnerFrames = ['\u280B', '\u2819', '\u2839', '\u2838', '\u283C', '\u2834', '\u2826', '\u2827', '\u2807', '\u280F']
|
|
12
|
+
|
|
13
|
+
export function renderSpinner(props: SpinnerProps, _context: RenderContext): string[] {
|
|
14
|
+
const { label } = props
|
|
15
|
+
// For static rendering, show first frame. In real TUI, this would animate.
|
|
16
|
+
const frame = spinnerFrames[0]
|
|
17
|
+
const text = label ? `${frame} ${label}` : frame
|
|
18
|
+
return [ANSI.cyan + text + ANSI.reset]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function Spinner(props: SpinnerProps): ReactElement {
|
|
22
|
+
return React.createElement('spinner', props)
|
|
23
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/helpers
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for terminal component rendering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { type ReactNode, isValidElement } from 'react'
|
|
8
|
+
import { ANSI, colors, visibleLength as themeVisibleLength } from '../theme'
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// ANSI Color Helpers (using theme module)
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Color map combining basic ANSI colors with semantic theme colors.
|
|
16
|
+
* Re-exports from theme module for backward compatibility.
|
|
17
|
+
*/
|
|
18
|
+
export const colorMap: Record<string, string> = {
|
|
19
|
+
// Basic colors (from ANSI)
|
|
20
|
+
black: ANSI.black,
|
|
21
|
+
red: ANSI.red,
|
|
22
|
+
green: ANSI.green,
|
|
23
|
+
yellow: ANSI.yellow,
|
|
24
|
+
blue: ANSI.blue,
|
|
25
|
+
magenta: ANSI.magenta,
|
|
26
|
+
cyan: ANSI.cyan,
|
|
27
|
+
white: ANSI.white,
|
|
28
|
+
// Semantic colors (from theme)
|
|
29
|
+
error: colors.error,
|
|
30
|
+
success: colors.success,
|
|
31
|
+
warning: colors.warning,
|
|
32
|
+
info: colors.info,
|
|
33
|
+
primary: colors.primary,
|
|
34
|
+
secondary: colors.secondary,
|
|
35
|
+
muted: colors.muted,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Background color map using ANSI constants.
|
|
40
|
+
*/
|
|
41
|
+
export const bgColorMap: Record<string, string> = {
|
|
42
|
+
black: ANSI.bgBlack,
|
|
43
|
+
red: ANSI.bgRed,
|
|
44
|
+
green: ANSI.bgGreen,
|
|
45
|
+
yellow: ANSI.bgYellow,
|
|
46
|
+
blue: ANSI.bgBlue,
|
|
47
|
+
magenta: ANSI.bgMagenta,
|
|
48
|
+
cyan: ANSI.bgCyan,
|
|
49
|
+
white: ANSI.bgWhite,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function getColorCode(color: string): string {
|
|
53
|
+
return colorMap[color] || ANSI.white
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function getBgColorCode(color: string): string {
|
|
57
|
+
return bgColorMap[color] || ''
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// Content Extraction Helpers
|
|
62
|
+
// ============================================================================
|
|
63
|
+
|
|
64
|
+
export function extractChildren(children: ReactNode): string {
|
|
65
|
+
if (typeof children === 'string') return children
|
|
66
|
+
if (typeof children === 'number') return String(children)
|
|
67
|
+
if (Array.isArray(children)) {
|
|
68
|
+
return children.map(extractChildren).join('')
|
|
69
|
+
}
|
|
70
|
+
if (isValidElement(children)) {
|
|
71
|
+
const props = children.props as Record<string, unknown>
|
|
72
|
+
return extractChildren(props.children as ReactNode)
|
|
73
|
+
}
|
|
74
|
+
return ''
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Calculate visible length of text (excluding ANSI codes).
|
|
79
|
+
* Re-exported from theme module for backward compatibility.
|
|
80
|
+
*/
|
|
81
|
+
export const visibleLength = themeVisibleLength
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components
|
|
3
|
+
*
|
|
4
|
+
* MDXUI primitives mapped to OpenTUI components.
|
|
5
|
+
* These maintain the same props API as @mdxui/primitives.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Types and Type Guards
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
export type {
|
|
13
|
+
// Render context
|
|
14
|
+
RenderContext,
|
|
15
|
+
TerminalComponentType,
|
|
16
|
+
// Primitive component props
|
|
17
|
+
BoxProps,
|
|
18
|
+
TextProps,
|
|
19
|
+
InputProps,
|
|
20
|
+
SelectOption,
|
|
21
|
+
SelectProps,
|
|
22
|
+
ButtonProps,
|
|
23
|
+
// Container component props
|
|
24
|
+
PanelProps,
|
|
25
|
+
CardProps,
|
|
26
|
+
DialogProps,
|
|
27
|
+
// Layout component props
|
|
28
|
+
TerminalSidebarLayoutProps,
|
|
29
|
+
SidebarItemProps,
|
|
30
|
+
BreadcrumbProps,
|
|
31
|
+
ListProps,
|
|
32
|
+
TableColumn,
|
|
33
|
+
TableProps,
|
|
34
|
+
// Feedback component props
|
|
35
|
+
BadgeProps,
|
|
36
|
+
SpinnerProps,
|
|
37
|
+
// mdxui common types
|
|
38
|
+
NavItem,
|
|
39
|
+
ActionConfig,
|
|
40
|
+
ActionProp,
|
|
41
|
+
ActionsProps,
|
|
42
|
+
FooterLinkGroup,
|
|
43
|
+
SocialLink,
|
|
44
|
+
FeatureItem,
|
|
45
|
+
PricingTier,
|
|
46
|
+
FAQItem,
|
|
47
|
+
DashboardMetric,
|
|
48
|
+
SettingsSection,
|
|
49
|
+
AppHeaderUser,
|
|
50
|
+
BreadcrumbItem,
|
|
51
|
+
// Site component props (mdxui SiteComponents interface)
|
|
52
|
+
SiteProps,
|
|
53
|
+
HeaderProps,
|
|
54
|
+
FooterProps,
|
|
55
|
+
HeroProps,
|
|
56
|
+
FeaturesProps,
|
|
57
|
+
PricingProps,
|
|
58
|
+
FAQProps,
|
|
59
|
+
LandingPageProps,
|
|
60
|
+
PageProps,
|
|
61
|
+
// App component props (mdxui AppComponents interface)
|
|
62
|
+
AppProps,
|
|
63
|
+
ShellProps,
|
|
64
|
+
SidebarProps,
|
|
65
|
+
AppHeaderProps,
|
|
66
|
+
DashboardProps,
|
|
67
|
+
SettingsProps,
|
|
68
|
+
} from './types'
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
COMPONENT_TYPES,
|
|
72
|
+
isTerminalComponentType,
|
|
73
|
+
isPropsObject,
|
|
74
|
+
isBoxProps,
|
|
75
|
+
isTextProps,
|
|
76
|
+
isTableProps,
|
|
77
|
+
isInputProps,
|
|
78
|
+
isSelectProps,
|
|
79
|
+
isSidebarProps,
|
|
80
|
+
isSidebarItemProps,
|
|
81
|
+
isBreadcrumbProps,
|
|
82
|
+
isBadgeProps,
|
|
83
|
+
isDialogProps,
|
|
84
|
+
isSpinnerProps,
|
|
85
|
+
isButtonProps,
|
|
86
|
+
isPanelProps,
|
|
87
|
+
isListProps,
|
|
88
|
+
isCardProps,
|
|
89
|
+
} from './types'
|
|
90
|
+
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// Helpers
|
|
93
|
+
// ============================================================================
|
|
94
|
+
|
|
95
|
+
export {
|
|
96
|
+
colorMap,
|
|
97
|
+
bgColorMap,
|
|
98
|
+
getColorCode,
|
|
99
|
+
getBgColorCode,
|
|
100
|
+
extractChildren,
|
|
101
|
+
} from './helpers'
|
|
102
|
+
|
|
103
|
+
// ============================================================================
|
|
104
|
+
// Render Functions
|
|
105
|
+
// ============================================================================
|
|
106
|
+
|
|
107
|
+
export { renderComponent, renderElement } from './render'
|
|
108
|
+
|
|
109
|
+
// ============================================================================
|
|
110
|
+
// Primitive Components
|
|
111
|
+
// ============================================================================
|
|
112
|
+
|
|
113
|
+
export { renderBox, Box } from './primitives/box'
|
|
114
|
+
export { renderText, Text } from './primitives/text'
|
|
115
|
+
export { renderInput, Input } from './primitives/input'
|
|
116
|
+
export { renderSelect, Select } from './primitives/select'
|
|
117
|
+
export { renderButton, Button } from './primitives/button'
|
|
118
|
+
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// Container Components
|
|
121
|
+
// ============================================================================
|
|
122
|
+
|
|
123
|
+
export { renderPanel, Panel } from './containers/panel'
|
|
124
|
+
export { renderCard, Card } from './containers/card'
|
|
125
|
+
export { renderDialog, Dialog } from './containers/dialog'
|
|
126
|
+
|
|
127
|
+
// ============================================================================
|
|
128
|
+
// Layout Components
|
|
129
|
+
// ============================================================================
|
|
130
|
+
|
|
131
|
+
export { renderSidebar, renderSidebarItem, SidebarLayout, SidebarItem } from './layout/sidebar'
|
|
132
|
+
export { renderBreadcrumb, Breadcrumb } from './layout/breadcrumb'
|
|
133
|
+
export { renderList, List } from './layout/list'
|
|
134
|
+
export { renderTable, Table } from './layout/table'
|
|
135
|
+
|
|
136
|
+
// ============================================================================
|
|
137
|
+
// Feedback Components
|
|
138
|
+
// ============================================================================
|
|
139
|
+
|
|
140
|
+
export { renderBadge, Badge } from './feedback/badge'
|
|
141
|
+
export { renderSpinner, Spinner, spinnerFrames } from './feedback/spinner'
|
|
142
|
+
|
|
143
|
+
// ============================================================================
|
|
144
|
+
// Site Template Components (mdxui SiteComponents interface)
|
|
145
|
+
// ============================================================================
|
|
146
|
+
|
|
147
|
+
export { Site, Header, Footer, Hero, Features, Pricing, FAQ, LandingPage, Page } from './templates/site'
|
|
148
|
+
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// App Template Components (mdxui AppComponents interface)
|
|
151
|
+
// ============================================================================
|
|
152
|
+
|
|
153
|
+
export { App, Shell, Sidebar, AppHeader, Dashboard, Settings } from './templates/app'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/layout/breadcrumb
|
|
3
|
+
*
|
|
4
|
+
* Breadcrumb component for terminal rendering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { type ReactElement } from 'react'
|
|
8
|
+
import { ANSI } from '../../theme'
|
|
9
|
+
import type { BreadcrumbProps, RenderContext } from '../types'
|
|
10
|
+
|
|
11
|
+
export function renderBreadcrumb(props: BreadcrumbProps, _context: RenderContext): string[] {
|
|
12
|
+
const { items, separator = '/' } = props
|
|
13
|
+
|
|
14
|
+
const parts: string[] = []
|
|
15
|
+
items.forEach((item, idx) => {
|
|
16
|
+
const isLast = idx === items.length - 1
|
|
17
|
+
if (isLast) {
|
|
18
|
+
// Current item - bold
|
|
19
|
+
parts.push(ANSI.bold + item.label + ANSI.reset)
|
|
20
|
+
} else {
|
|
21
|
+
// Previous items - dim
|
|
22
|
+
parts.push(ANSI.dim + item.label + ANSI.reset)
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
return [parts.join(` ${separator} `)]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function Breadcrumb(props: BreadcrumbProps): ReactElement {
|
|
30
|
+
return React.createElement('breadcrumb', props)
|
|
31
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/layout
|
|
3
|
+
*
|
|
4
|
+
* Layout terminal components.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { renderSidebar, renderSidebarItem, SidebarLayout, SidebarItem } from './sidebar'
|
|
8
|
+
export { renderBreadcrumb, Breadcrumb } from './breadcrumb'
|
|
9
|
+
export { renderList, List } from './list'
|
|
10
|
+
export { renderTable, Table } from './table'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/layout/list
|
|
3
|
+
*
|
|
4
|
+
* List component for terminal rendering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { type ReactElement, type ReactNode } from 'react'
|
|
8
|
+
import { ANSI } from '../../theme'
|
|
9
|
+
import type { ListProps, RenderContext } from '../types'
|
|
10
|
+
import { extractChildren } from '../helpers'
|
|
11
|
+
|
|
12
|
+
export function renderList(props: ListProps, _context: RenderContext): string[] {
|
|
13
|
+
const { items, selectedIndex, bullet = '\u2022', numbered = false } = props
|
|
14
|
+
|
|
15
|
+
return items.map((item, idx) => {
|
|
16
|
+
const text = typeof item === 'string' ? item : extractChildren(item as ReactNode)
|
|
17
|
+
const prefix = numbered ? `${idx + 1}. ` : `${bullet} `
|
|
18
|
+
const isSelected = selectedIndex === idx
|
|
19
|
+
|
|
20
|
+
if (isSelected) {
|
|
21
|
+
return ANSI.cyan + '\x1b[44m' + prefix + text + ANSI.reset
|
|
22
|
+
}
|
|
23
|
+
return prefix + text
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function List(props: ListProps): ReactElement {
|
|
28
|
+
return React.createElement('list', props)
|
|
29
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/layout/sidebar
|
|
3
|
+
*
|
|
4
|
+
* Internal sidebar layout components for terminal rendering.
|
|
5
|
+
* These are primitive layout components, distinct from mdxui Sidebar.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { type ReactElement, Children, isValidElement } from 'react'
|
|
9
|
+
import { ANSI } from '../../theme'
|
|
10
|
+
import type { TerminalSidebarLayoutProps, SidebarItemProps, RenderContext } from '../types'
|
|
11
|
+
|
|
12
|
+
export function renderSidebarItem(props: SidebarItemProps & { _collapsed?: boolean }, _context: RenderContext): string[] {
|
|
13
|
+
const { label, icon, active, _collapsed } = props
|
|
14
|
+
|
|
15
|
+
if (_collapsed) {
|
|
16
|
+
// Only show icon
|
|
17
|
+
return [icon || '']
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let line = ''
|
|
21
|
+
|
|
22
|
+
// Add active indicator
|
|
23
|
+
if (active) {
|
|
24
|
+
line = '\u25B6 '
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Add icon
|
|
28
|
+
if (icon) {
|
|
29
|
+
line += icon + ' '
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Add label
|
|
33
|
+
line += label
|
|
34
|
+
|
|
35
|
+
// Apply active styling
|
|
36
|
+
if (active) {
|
|
37
|
+
line = '\x1b[44m' + line + ANSI.reset
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return [line]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function renderSidebar(props: TerminalSidebarLayoutProps, context: RenderContext): string[] {
|
|
44
|
+
const { children, width = 20, collapsed } = props
|
|
45
|
+
const lines: string[] = []
|
|
46
|
+
|
|
47
|
+
// Render children (SidebarItems)
|
|
48
|
+
const childArray = Children.toArray(children)
|
|
49
|
+
for (const child of childArray) {
|
|
50
|
+
if (isValidElement(child)) {
|
|
51
|
+
const childProps = child.props as SidebarItemProps
|
|
52
|
+
// Pass collapsed state to child
|
|
53
|
+
const modifiedProps = { ...childProps, _collapsed: collapsed }
|
|
54
|
+
const childLines = renderSidebarItem(modifiedProps as SidebarItemProps & { _collapsed?: boolean }, { ...context, width })
|
|
55
|
+
lines.push(...childLines)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Ensure width constraint
|
|
60
|
+
return lines.map(line => {
|
|
61
|
+
const stripped = line.replace(/\x1b\[[\d;]*m/g, '')
|
|
62
|
+
if (stripped.length > width) {
|
|
63
|
+
return line.slice(0, width)
|
|
64
|
+
}
|
|
65
|
+
return line
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* SidebarLayout - primitive sidebar layout component for terminal rendering.
|
|
71
|
+
* Use mdxui Sidebar (from templates/app) for mdxui-compatible interface.
|
|
72
|
+
*/
|
|
73
|
+
export function SidebarLayout(props: TerminalSidebarLayoutProps): ReactElement {
|
|
74
|
+
return React.createElement('sidebar', props)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function SidebarItem(props: SidebarItemProps): ReactElement {
|
|
78
|
+
return React.createElement('sidebar-item', props)
|
|
79
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/layout/table
|
|
3
|
+
*
|
|
4
|
+
* Table component for terminal rendering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { type ReactElement } from 'react'
|
|
8
|
+
import { ANSI } from '../../theme'
|
|
9
|
+
import type { TableProps, RenderContext } from '../types'
|
|
10
|
+
|
|
11
|
+
export function renderTable<T>(props: TableProps<T>, _context: RenderContext): string[] {
|
|
12
|
+
const { data, columns, selectedIndex } = props
|
|
13
|
+
const lines: string[] = []
|
|
14
|
+
|
|
15
|
+
// Calculate column widths
|
|
16
|
+
const colWidths = columns.map((col) => {
|
|
17
|
+
if (col.width) return col.width
|
|
18
|
+
let maxWidth = col.header.length
|
|
19
|
+
for (const row of data) {
|
|
20
|
+
const value = String((row as Record<string, unknown>)[col.key as string] ?? '')
|
|
21
|
+
maxWidth = Math.max(maxWidth, value.length)
|
|
22
|
+
}
|
|
23
|
+
return maxWidth + 2 // padding
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// Render header
|
|
27
|
+
const headerParts = columns.map((col, idx) => {
|
|
28
|
+
const width = colWidths[idx]
|
|
29
|
+
return col.header.padEnd(width)
|
|
30
|
+
})
|
|
31
|
+
lines.push('\u2502' + headerParts.join('\u2502') + '\u2502')
|
|
32
|
+
|
|
33
|
+
// Header separator
|
|
34
|
+
const separatorParts = colWidths.map(w => '\u2500'.repeat(w))
|
|
35
|
+
lines.push('\u251C' + separatorParts.join('\u253C') + '\u2524')
|
|
36
|
+
|
|
37
|
+
// Render rows
|
|
38
|
+
data.forEach((row, rowIndex) => {
|
|
39
|
+
const rowParts = columns.map((col, idx) => {
|
|
40
|
+
const value = String((row as Record<string, unknown>)[col.key as string] ?? '')
|
|
41
|
+
const width = colWidths[idx]
|
|
42
|
+
if (col.align === 'right') {
|
|
43
|
+
return value.padStart(width)
|
|
44
|
+
}
|
|
45
|
+
return value.padEnd(width)
|
|
46
|
+
})
|
|
47
|
+
let rowLine = '\u2502' + rowParts.join('\u2502') + '\u2502'
|
|
48
|
+
|
|
49
|
+
// Highlight selected row
|
|
50
|
+
if (selectedIndex === rowIndex) {
|
|
51
|
+
rowLine = '\x1b[44m' + rowLine + ANSI.reset
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
lines.push(rowLine)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
return lines
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function Table<T>(props: TableProps<T>): ReactElement {
|
|
61
|
+
return React.createElement('table', props as unknown as Record<string, unknown>)
|
|
62
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/components/primitives/box
|
|
3
|
+
*
|
|
4
|
+
* Box component for terminal rendering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { type ReactElement, isValidElement } from 'react'
|
|
8
|
+
import { ANSI, boxChars } from '../../theme'
|
|
9
|
+
import type { BoxProps, RenderContext } from '../types'
|
|
10
|
+
import { extractChildren, getBgColorCode } from '../helpers'
|
|
11
|
+
|
|
12
|
+
// Forward declaration for circular dependency
|
|
13
|
+
let renderElementFn: ((element: ReactElement, context: RenderContext) => string[]) | null = null
|
|
14
|
+
|
|
15
|
+
export function setRenderElement(fn: (element: ReactElement, context: RenderContext) => string[]): void {
|
|
16
|
+
renderElementFn = fn
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function renderBox(props: BoxProps, context: RenderContext): string[] {
|
|
20
|
+
const { children, border = 'none', padding = 0, width, height, bg } = props
|
|
21
|
+
const boxWidth = typeof width === 'number' ? width : context.width
|
|
22
|
+
const boxHeight = typeof height === 'number' ? height : undefined
|
|
23
|
+
|
|
24
|
+
// Get child content
|
|
25
|
+
let childLines: string[] = []
|
|
26
|
+
if (children) {
|
|
27
|
+
if (isValidElement(children) && renderElementFn) {
|
|
28
|
+
childLines = renderElementFn(children, { ...context, width: boxWidth - (border !== 'none' ? 2 : 0) - padding * 2, parentWidth: boxWidth })
|
|
29
|
+
} else {
|
|
30
|
+
const text = extractChildren(children)
|
|
31
|
+
if (text) childLines = [text]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Apply padding to content
|
|
36
|
+
if (padding > 0) {
|
|
37
|
+
childLines = childLines.map(line => ' '.repeat(padding) + line + ' '.repeat(padding))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Calculate content width
|
|
41
|
+
const contentWidth = border !== 'none' ? boxWidth - 2 : boxWidth
|
|
42
|
+
|
|
43
|
+
// If no border
|
|
44
|
+
if (border === 'none') {
|
|
45
|
+
let result = childLines.length > 0 ? childLines : ['']
|
|
46
|
+
// Handle height
|
|
47
|
+
if (boxHeight) {
|
|
48
|
+
while (result.length < boxHeight) {
|
|
49
|
+
result.push('')
|
|
50
|
+
}
|
|
51
|
+
result = result.slice(0, boxHeight)
|
|
52
|
+
}
|
|
53
|
+
// Apply background color
|
|
54
|
+
if (bg) {
|
|
55
|
+
const bgCode = getBgColorCode(bg)
|
|
56
|
+
result = result.map(line => bgCode + line.padEnd(boxWidth) + ANSI.reset)
|
|
57
|
+
}
|
|
58
|
+
return result
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Get box characters
|
|
62
|
+
const chars = boxChars[border] || boxChars.single
|
|
63
|
+
|
|
64
|
+
// Build box with border
|
|
65
|
+
const lines: string[] = []
|
|
66
|
+
|
|
67
|
+
// Top border
|
|
68
|
+
lines.push(chars.topLeft + chars.horizontal.repeat(contentWidth) + chars.topRight)
|
|
69
|
+
|
|
70
|
+
// Content lines
|
|
71
|
+
const targetHeight = boxHeight ? boxHeight - 2 : childLines.length
|
|
72
|
+
|
|
73
|
+
for (let i = 0; i < targetHeight; i++) {
|
|
74
|
+
const content = i < childLines.length ? childLines[i] : ''
|
|
75
|
+
// Strip ANSI for length calculation
|
|
76
|
+
const stripped = content.replace(/\x1b\[[\d;]*m/g, '')
|
|
77
|
+
const paddedContent = content + ' '.repeat(Math.max(0, contentWidth - stripped.length))
|
|
78
|
+
lines.push(chars.vertical + paddedContent + chars.vertical)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Bottom border
|
|
82
|
+
lines.push(chars.bottomLeft + chars.horizontal.repeat(contentWidth) + chars.bottomRight)
|
|
83
|
+
|
|
84
|
+
// Apply background color
|
|
85
|
+
if (bg) {
|
|
86
|
+
const bgCode = getBgColorCode(bg)
|
|
87
|
+
return lines.map(line => bgCode + line + ANSI.reset)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return lines
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function Box(props: BoxProps): ReactElement {
|
|
94
|
+
return React.createElement('box', props)
|
|
95
|
+
}
|