@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,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color Conversion Utilities
|
|
3
|
+
*
|
|
4
|
+
* Functions for converting between color formats:
|
|
5
|
+
* - RGB to ANSI 256 color codes
|
|
6
|
+
* - Hex to RGB
|
|
7
|
+
* - True color (24-bit) escape sequences
|
|
8
|
+
*
|
|
9
|
+
* All public APIs include runtime validation via Zod schemas.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
Ansi256CodeSchema,
|
|
16
|
+
HexColorSchema,
|
|
17
|
+
RgbComponentSchema,
|
|
18
|
+
} from '../schemas'
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Internal Helpers
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Convert RGB values to the nearest ANSI 256 color code
|
|
26
|
+
*/
|
|
27
|
+
function rgbToAnsi256(r: number, g: number, b: number): number {
|
|
28
|
+
// Check for exact black
|
|
29
|
+
if (r === 0 && g === 0 && b === 0) {
|
|
30
|
+
return 0
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check for exact white
|
|
34
|
+
if (r === 255 && g === 255 && b === 255) {
|
|
35
|
+
return 15
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Check for grayscale (when r, g, b are close to each other)
|
|
39
|
+
if (Math.abs(r - g) < 10 && Math.abs(g - b) < 10 && Math.abs(r - b) < 10) {
|
|
40
|
+
const avg = (r + g + b) / 3
|
|
41
|
+
if (avg < 8) return 0 // black
|
|
42
|
+
if (avg > 248) return 15 // white
|
|
43
|
+
// Grayscale range: 232-255 (24 shades)
|
|
44
|
+
const grayIndex = Math.round((avg - 8) / 10)
|
|
45
|
+
return Math.min(255, Math.max(232, 232 + grayIndex))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Convert to 6x6x6 color cube (colors 16-231)
|
|
49
|
+
const toColorCubeIndex = (v: number): number => {
|
|
50
|
+
if (v < 48) return 0
|
|
51
|
+
if (v < 115) return 1
|
|
52
|
+
return Math.min(5, Math.floor((v - 35) / 40))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const ri = toColorCubeIndex(r)
|
|
56
|
+
const gi = toColorCubeIndex(g)
|
|
57
|
+
const bi = toColorCubeIndex(b)
|
|
58
|
+
|
|
59
|
+
return 16 + 36 * ri + 6 * gi + bi
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Parse hex color to RGB values.
|
|
64
|
+
*
|
|
65
|
+
* Handles edge cases:
|
|
66
|
+
* - Empty string returns black (0, 0, 0)
|
|
67
|
+
* - Invalid hex returns black (0, 0, 0)
|
|
68
|
+
* - Supports with/without # prefix
|
|
69
|
+
* - Supports 3-character shorthand (#fff)
|
|
70
|
+
*/
|
|
71
|
+
export function hexToRgb(hex: string): { r: number; g: number; b: number } {
|
|
72
|
+
// Handle empty or null input
|
|
73
|
+
if (!hex || typeof hex !== 'string') {
|
|
74
|
+
return { r: 0, g: 0, b: 0 }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Remove # prefix if present
|
|
78
|
+
hex = hex.replace(/^#/, '')
|
|
79
|
+
|
|
80
|
+
// Handle empty after removing #
|
|
81
|
+
if (hex.length === 0) {
|
|
82
|
+
return { r: 0, g: 0, b: 0 }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Handle shorthand hex (e.g., #fff -> #ffffff)
|
|
86
|
+
if (hex.length === 3) {
|
|
87
|
+
hex = hex
|
|
88
|
+
.split('')
|
|
89
|
+
.map((c) => c + c)
|
|
90
|
+
.join('')
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Validate hex string
|
|
94
|
+
if (!/^[0-9a-fA-F]{6}$/.test(hex)) {
|
|
95
|
+
return { r: 0, g: 0, b: 0 }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const num = parseInt(hex, 16)
|
|
99
|
+
return {
|
|
100
|
+
r: (num >> 16) & 255,
|
|
101
|
+
g: (num >> 8) & 255,
|
|
102
|
+
b: num & 255,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// Public API
|
|
108
|
+
// ============================================================================
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Options for RGB to ANSI conversion.
|
|
112
|
+
*/
|
|
113
|
+
export interface RgbToAnsiOptions {
|
|
114
|
+
/** If true, generates background color code; otherwise foreground */
|
|
115
|
+
background?: boolean
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Converts RGB color values to an ANSI 256 escape sequence.
|
|
120
|
+
*
|
|
121
|
+
* @param r - Red component (0-255)
|
|
122
|
+
* @param g - Green component (0-255)
|
|
123
|
+
* @param b - Blue component (0-255)
|
|
124
|
+
* @param opts - Optional settings for background/foreground
|
|
125
|
+
* @returns ANSI escape sequence string
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```tsx
|
|
129
|
+
* // Coral foreground color
|
|
130
|
+
* console.log(`${rgbToAnsi(255, 127, 80)}Coral text${ANSI.reset}`)
|
|
131
|
+
*
|
|
132
|
+
* // Blue background
|
|
133
|
+
* console.log(`${rgbToAnsi(0, 0, 255, { background: true })}Blue bg${ANSI.reset}`)
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export function rgbToAnsi(
|
|
137
|
+
r: number,
|
|
138
|
+
g: number,
|
|
139
|
+
b: number,
|
|
140
|
+
opts?: RgbToAnsiOptions
|
|
141
|
+
): string {
|
|
142
|
+
const code = rgbToAnsi256(r, g, b)
|
|
143
|
+
const prefix = opts?.background ? '48' : '38'
|
|
144
|
+
return `\x1b[${prefix};5;${code}m`
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Converts a hex color string to an ANSI 256 escape sequence.
|
|
149
|
+
*
|
|
150
|
+
* Supports both 3-character (#RGB) and 6-character (#RRGGBB) hex formats.
|
|
151
|
+
* The '#' prefix is optional.
|
|
152
|
+
*
|
|
153
|
+
* @param hex - Hex color string (e.g., '#ff7f50', 'ff7f50', '#f00')
|
|
154
|
+
* @param opts - Optional settings for background/foreground
|
|
155
|
+
* @returns ANSI escape sequence string
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```tsx
|
|
159
|
+
* // Tomato red foreground
|
|
160
|
+
* console.log(`${hexToAnsi('#ff6347')}Tomato${ANSI.reset}`)
|
|
161
|
+
*
|
|
162
|
+
* // Short-form hex with background
|
|
163
|
+
* console.log(`${hexToAnsi('#f00', { background: true })}Red bg${ANSI.reset}`)
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
export function hexToAnsi(hex: string, opts?: RgbToAnsiOptions): string {
|
|
167
|
+
const { r, g, b } = hexToRgb(hex)
|
|
168
|
+
return rgbToAnsi(r, g, b, opts)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Generate ANSI 256-color foreground escape code.
|
|
173
|
+
*
|
|
174
|
+
* @param n - ANSI 256 color code (0-255)
|
|
175
|
+
* @returns ANSI escape sequence string
|
|
176
|
+
* @throws {ZodError} If n is not a valid ANSI 256 code (0-255)
|
|
177
|
+
*/
|
|
178
|
+
export function ansi256(n: number): string {
|
|
179
|
+
Ansi256CodeSchema.parse(n)
|
|
180
|
+
return `\x1b[38;5;${n}m`
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Generate ANSI 256-color background escape code.
|
|
185
|
+
*
|
|
186
|
+
* @param n - ANSI 256 color code (0-255)
|
|
187
|
+
* @returns ANSI escape sequence string
|
|
188
|
+
* @throws {ZodError} If n is not a valid ANSI 256 code (0-255)
|
|
189
|
+
*/
|
|
190
|
+
export function ansi256Bg(n: number): string {
|
|
191
|
+
Ansi256CodeSchema.parse(n)
|
|
192
|
+
return `\x1b[48;5;${n}m`
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Generate true color (24-bit) foreground escape code.
|
|
197
|
+
*
|
|
198
|
+
* @param r - Red component (0-255)
|
|
199
|
+
* @param g - Green component (0-255)
|
|
200
|
+
* @param b - Blue component (0-255)
|
|
201
|
+
* @returns ANSI escape sequence string
|
|
202
|
+
* @throws {ZodError} If any component is not in range 0-255
|
|
203
|
+
*/
|
|
204
|
+
export function rgb(r: number, g: number, b: number): string {
|
|
205
|
+
RgbComponentSchema.parse(r)
|
|
206
|
+
RgbComponentSchema.parse(g)
|
|
207
|
+
RgbComponentSchema.parse(b)
|
|
208
|
+
return `\x1b[38;2;${r};${g};${b}m`
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Generate true color (24-bit) background escape code.
|
|
213
|
+
*
|
|
214
|
+
* @param r - Red component (0-255)
|
|
215
|
+
* @param g - Green component (0-255)
|
|
216
|
+
* @param b - Blue component (0-255)
|
|
217
|
+
* @returns ANSI escape sequence string
|
|
218
|
+
* @throws {ZodError} If any component is not in range 0-255
|
|
219
|
+
*/
|
|
220
|
+
export function rgbBg(r: number, g: number, b: number): string {
|
|
221
|
+
RgbComponentSchema.parse(r)
|
|
222
|
+
RgbComponentSchema.parse(g)
|
|
223
|
+
RgbComponentSchema.parse(b)
|
|
224
|
+
return `\x1b[48;2;${r};${g};${b}m`
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Generate true color foreground from hex color.
|
|
229
|
+
*
|
|
230
|
+
* @param color - Hex color string (#RGB, RGB, #RRGGBB, or RRGGBB)
|
|
231
|
+
* @returns ANSI escape sequence string
|
|
232
|
+
* @throws {ZodError} If color is not a valid hex color format
|
|
233
|
+
*/
|
|
234
|
+
export function hex(color: string): string {
|
|
235
|
+
HexColorSchema.parse(color)
|
|
236
|
+
const { r, g, b } = hexToRgb(color)
|
|
237
|
+
return `\x1b[38;2;${r};${g};${b}m`
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Generate true color background from hex color.
|
|
242
|
+
*
|
|
243
|
+
* @param color - Hex color string (#RGB, RGB, #RRGGBB, or RRGGBB)
|
|
244
|
+
* @returns ANSI escape sequence string
|
|
245
|
+
* @throws {ZodError} If color is not a valid hex color format
|
|
246
|
+
*/
|
|
247
|
+
export function hexBg(color: string): string {
|
|
248
|
+
HexColorSchema.parse(color)
|
|
249
|
+
const { r, g, b } = hexToRgb(color)
|
|
250
|
+
return `\x1b[48;2;${r};${g};${b}m`
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Export the internal helper for use by other modules
|
|
254
|
+
export { rgbToAnsi256 }
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color Support Detection and Degradation
|
|
3
|
+
*
|
|
4
|
+
* Utilities for detecting terminal color capabilities and
|
|
5
|
+
* gracefully degrading colors for limited terminals.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { rgbToAnsi256 } from './color-convert'
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Type Definitions
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Level of color support in the terminal.
|
|
18
|
+
*
|
|
19
|
+
* - `'none'` - No color support (monochrome)
|
|
20
|
+
* - `'16'` - Basic 16 ANSI colors
|
|
21
|
+
* - `'256'` - Extended 256 color palette
|
|
22
|
+
* - `'truecolor'` - Full 24-bit RGB color support
|
|
23
|
+
*/
|
|
24
|
+
export type ColorSupport = 'none' | '16' | '256' | 'truecolor'
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Color Support Detection
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Detects the terminal's color support level.
|
|
32
|
+
*
|
|
33
|
+
* Checks environment variables in this order:
|
|
34
|
+
* 1. `NO_COLOR` - Forces no color support
|
|
35
|
+
* 2. `FORCE_COLOR` - Forces a specific color level (0-3)
|
|
36
|
+
* 3. `COLORTERM` - Checks for truecolor support
|
|
37
|
+
* 4. `TERM` - Checks terminal type for color hints
|
|
38
|
+
*
|
|
39
|
+
* @returns The detected color support level
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* const support = detectColorSupport()
|
|
44
|
+
* if (support === 'truecolor') {
|
|
45
|
+
* // Use full RGB colors
|
|
46
|
+
* } else if (support === '256') {
|
|
47
|
+
* // Use 256 color palette
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function detectColorSupport(): ColorSupport {
|
|
52
|
+
// NO_COLOR takes precedence
|
|
53
|
+
if (process.env.NO_COLOR) {
|
|
54
|
+
return 'none'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// FORCE_COLOR can override
|
|
58
|
+
if (process.env.FORCE_COLOR) {
|
|
59
|
+
const level = parseInt(process.env.FORCE_COLOR, 10)
|
|
60
|
+
if (level === 0) return 'none'
|
|
61
|
+
if (level === 1) return '16'
|
|
62
|
+
if (level === 2) return '256'
|
|
63
|
+
if (level >= 3) return 'truecolor'
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check COLORTERM for truecolor
|
|
67
|
+
const colorTerm = process.env.COLORTERM
|
|
68
|
+
if (colorTerm === 'truecolor' || colorTerm === '24bit') {
|
|
69
|
+
return 'truecolor'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Check TERM for color support
|
|
73
|
+
const term = process.env.TERM || ''
|
|
74
|
+
|
|
75
|
+
if (term === 'dumb') {
|
|
76
|
+
return 'none'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (term.includes('256color')) {
|
|
80
|
+
return '256'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (term.includes('color') || term.includes('xterm') || term.includes('vt100')) {
|
|
84
|
+
return '16'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Default to 256 for most modern terminals
|
|
88
|
+
return '256'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// Color Degradation
|
|
93
|
+
// ============================================================================
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Map ANSI 256 color to basic 16 color
|
|
97
|
+
*/
|
|
98
|
+
function ansi256To16(code: number): number {
|
|
99
|
+
// Standard colors 0-7 map to 30-37
|
|
100
|
+
if (code < 8) {
|
|
101
|
+
return 30 + code
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Bright colors 8-15 map to 90-97
|
|
105
|
+
if (code < 16) {
|
|
106
|
+
return 90 + (code - 8)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Color cube (16-231) and grayscale (232-255)
|
|
110
|
+
// Need to find the closest basic color
|
|
111
|
+
|
|
112
|
+
let r: number, g: number, b: number
|
|
113
|
+
|
|
114
|
+
if (code >= 232) {
|
|
115
|
+
// Grayscale
|
|
116
|
+
const gray = (code - 232) * 10 + 8
|
|
117
|
+
r = g = b = gray
|
|
118
|
+
} else {
|
|
119
|
+
// Color cube
|
|
120
|
+
const cubeIndex = code - 16
|
|
121
|
+
r = Math.floor(cubeIndex / 36) * 51
|
|
122
|
+
g = Math.floor((cubeIndex % 36) / 6) * 51
|
|
123
|
+
b = (cubeIndex % 6) * 51
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Find the closest basic color
|
|
127
|
+
// Basic colors: black, red, green, yellow, blue, magenta, cyan, white
|
|
128
|
+
const basicColors = [
|
|
129
|
+
{ r: 0, g: 0, b: 0, code: 30 }, // black
|
|
130
|
+
{ r: 170, g: 0, b: 0, code: 31 }, // red
|
|
131
|
+
{ r: 0, g: 170, b: 0, code: 32 }, // green
|
|
132
|
+
{ r: 170, g: 170, b: 0, code: 33 }, // yellow
|
|
133
|
+
{ r: 0, g: 0, b: 170, code: 34 }, // blue
|
|
134
|
+
{ r: 170, g: 0, b: 170, code: 35 }, // magenta
|
|
135
|
+
{ r: 0, g: 170, b: 170, code: 36 }, // cyan
|
|
136
|
+
{ r: 170, g: 170, b: 170, code: 37 }, // white
|
|
137
|
+
{ r: 85, g: 85, b: 85, code: 90 }, // bright black
|
|
138
|
+
{ r: 255, g: 85, b: 85, code: 91 }, // bright red
|
|
139
|
+
{ r: 85, g: 255, b: 85, code: 92 }, // bright green
|
|
140
|
+
{ r: 255, g: 255, b: 85, code: 93 }, // bright yellow
|
|
141
|
+
{ r: 85, g: 85, b: 255, code: 94 }, // bright blue
|
|
142
|
+
{ r: 255, g: 85, b: 255, code: 95 }, // bright magenta
|
|
143
|
+
{ r: 85, g: 255, b: 255, code: 96 }, // bright cyan
|
|
144
|
+
{ r: 255, g: 255, b: 255, code: 97 }, // bright white
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
let minDist = Infinity
|
|
148
|
+
let closestCode = 37 // default to white
|
|
149
|
+
|
|
150
|
+
for (const bc of basicColors) {
|
|
151
|
+
const dist = Math.sqrt(
|
|
152
|
+
Math.pow(r - bc.r, 2) + Math.pow(g - bc.g, 2) + Math.pow(b - bc.b, 2)
|
|
153
|
+
)
|
|
154
|
+
if (dist < minDist) {
|
|
155
|
+
minDist = dist
|
|
156
|
+
closestCode = bc.code
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return closestCode
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Parse ANSI escape code to extract type and color code
|
|
165
|
+
*/
|
|
166
|
+
function parseAnsiCode(ansi: string): {
|
|
167
|
+
type: 'fg' | 'bg' | 'style'
|
|
168
|
+
format: '16' | '256' | 'truecolor'
|
|
169
|
+
code?: number
|
|
170
|
+
rgb?: { r: number; g: number; b: number }
|
|
171
|
+
} | null {
|
|
172
|
+
// Match ANSI 256 foreground: \x1b[38;5;Nm
|
|
173
|
+
const fg256Match = ansi.match(/\x1b\[38;5;(\d+)m/)
|
|
174
|
+
if (fg256Match) {
|
|
175
|
+
return { type: 'fg', format: '256', code: parseInt(fg256Match[1], 10) }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Match ANSI 256 background: \x1b[48;5;Nm
|
|
179
|
+
const bg256Match = ansi.match(/\x1b\[48;5;(\d+)m/)
|
|
180
|
+
if (bg256Match) {
|
|
181
|
+
return { type: 'bg', format: '256', code: parseInt(bg256Match[1], 10) }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Match truecolor foreground: \x1b[38;2;R;G;Bm
|
|
185
|
+
const fgTrueMatch = ansi.match(/\x1b\[38;2;(\d+);(\d+);(\d+)m/)
|
|
186
|
+
if (fgTrueMatch) {
|
|
187
|
+
return {
|
|
188
|
+
type: 'fg',
|
|
189
|
+
format: 'truecolor',
|
|
190
|
+
rgb: {
|
|
191
|
+
r: parseInt(fgTrueMatch[1], 10),
|
|
192
|
+
g: parseInt(fgTrueMatch[2], 10),
|
|
193
|
+
b: parseInt(fgTrueMatch[3], 10),
|
|
194
|
+
},
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Match truecolor background: \x1b[48;2;R;G;Bm
|
|
199
|
+
const bgTrueMatch = ansi.match(/\x1b\[48;2;(\d+);(\d+);(\d+)m/)
|
|
200
|
+
if (bgTrueMatch) {
|
|
201
|
+
return {
|
|
202
|
+
type: 'bg',
|
|
203
|
+
format: 'truecolor',
|
|
204
|
+
rgb: {
|
|
205
|
+
r: parseInt(bgTrueMatch[1], 10),
|
|
206
|
+
g: parseInt(bgTrueMatch[2], 10),
|
|
207
|
+
b: parseInt(bgTrueMatch[3], 10),
|
|
208
|
+
},
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Match basic 16 color codes
|
|
213
|
+
const basicMatch = ansi.match(/\x1b\[(\d+)m/)
|
|
214
|
+
if (basicMatch) {
|
|
215
|
+
const code = parseInt(basicMatch[1], 10)
|
|
216
|
+
if (code >= 30 && code <= 37) {
|
|
217
|
+
return { type: 'fg', format: '16', code }
|
|
218
|
+
}
|
|
219
|
+
if (code >= 90 && code <= 97) {
|
|
220
|
+
return { type: 'fg', format: '16', code }
|
|
221
|
+
}
|
|
222
|
+
if (code >= 40 && code <= 47) {
|
|
223
|
+
return { type: 'bg', format: '16', code }
|
|
224
|
+
}
|
|
225
|
+
if (code >= 100 && code <= 107) {
|
|
226
|
+
return { type: 'bg', format: '16', code }
|
|
227
|
+
}
|
|
228
|
+
// Style code
|
|
229
|
+
return { type: 'style', format: '16', code }
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return null
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Degrades an ANSI color to work with limited terminal color support.
|
|
237
|
+
*
|
|
238
|
+
* Automatically converts colors down to simpler formats:
|
|
239
|
+
* - Truecolor to 256 colors
|
|
240
|
+
* - 256 colors to 16 basic colors
|
|
241
|
+
* - Any color to nothing if support is 'none'
|
|
242
|
+
*
|
|
243
|
+
* @param ansi - Original ANSI escape sequence
|
|
244
|
+
* @param support - Target color support level
|
|
245
|
+
* @returns Degraded ANSI escape sequence, or empty string for 'none'
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```tsx
|
|
249
|
+
* const trueColorBlue = '\x1b[38;2;100;150;200m'
|
|
250
|
+
* const support = detectColorSupport()
|
|
251
|
+
*
|
|
252
|
+
* // Automatically degrade for the current terminal
|
|
253
|
+
* const blue = degradeColor(trueColorBlue, support)
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
export function degradeColor(ansi: string, support: ColorSupport): string {
|
|
257
|
+
if (support === 'none') {
|
|
258
|
+
return ''
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (support === 'truecolor') {
|
|
262
|
+
// Truecolor supports everything
|
|
263
|
+
return ansi
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const parsed = parseAnsiCode(ansi)
|
|
267
|
+
if (!parsed) {
|
|
268
|
+
return ansi
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (support === '256') {
|
|
272
|
+
// Convert truecolor to 256
|
|
273
|
+
if (parsed.format === 'truecolor' && parsed.rgb) {
|
|
274
|
+
const code = rgbToAnsi256(parsed.rgb.r, parsed.rgb.g, parsed.rgb.b)
|
|
275
|
+
const prefix = parsed.type === 'bg' ? '48' : '38'
|
|
276
|
+
return `\x1b[${prefix};5;${code}m`
|
|
277
|
+
}
|
|
278
|
+
return ansi
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (support === '16') {
|
|
282
|
+
// Convert 256 or truecolor to 16
|
|
283
|
+
if (parsed.format === '16') {
|
|
284
|
+
return ansi
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
let code256: number
|
|
288
|
+
if (parsed.format === 'truecolor' && parsed.rgb) {
|
|
289
|
+
code256 = rgbToAnsi256(parsed.rgb.r, parsed.rgb.g, parsed.rgb.b)
|
|
290
|
+
} else if (parsed.code !== undefined) {
|
|
291
|
+
code256 = parsed.code
|
|
292
|
+
} else {
|
|
293
|
+
return ansi
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const code16 = ansi256To16(code256)
|
|
297
|
+
return `\x1b[${code16}m`
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return ansi
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Adapts a color based on terminal support level.
|
|
305
|
+
*
|
|
306
|
+
* Alias for {@link degradeColor} - use this when the intent is to
|
|
307
|
+
* "adapt" colors for the current terminal rather than "degrade" them.
|
|
308
|
+
*
|
|
309
|
+
* @param color - ANSI color escape sequence
|
|
310
|
+
* @param level - Target color support level
|
|
311
|
+
* @returns Adapted color escape sequence
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```tsx
|
|
315
|
+
* const support = detectColorSupport()
|
|
316
|
+
* const color = adaptColor(theme.primary, support)
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
export function adaptColor(color: string, level: ColorSupport): string {
|
|
320
|
+
return degradeColor(color, level)
|
|
321
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal/theme
|
|
3
|
+
*
|
|
4
|
+
* Terminal theming system for mapping web design tokens to ANSI colors.
|
|
5
|
+
*
|
|
6
|
+
* This module provides:
|
|
7
|
+
* - ANSI escape code constants for styling terminal output
|
|
8
|
+
* - Tailwind CSS to ANSI color conversion
|
|
9
|
+
* - Theme creation and composition utilities
|
|
10
|
+
* - Color support detection and degradation
|
|
11
|
+
* - Box drawing character sets for UI borders
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* import { ANSI, styled, createTheme } from '@mdxui/terminal'
|
|
16
|
+
*
|
|
17
|
+
* // Apply styles to text
|
|
18
|
+
* console.log(styled('Hello', ANSI.bold, ANSI.cyan))
|
|
19
|
+
*
|
|
20
|
+
* // Create a custom theme
|
|
21
|
+
* const theme = createTheme({
|
|
22
|
+
* primary: ANSI.brightGreen,
|
|
23
|
+
* error: ANSI.brightRed,
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @packageDocumentation
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
// ANSI escape code constants
|
|
31
|
+
export { ANSI } from './ansi-codes'
|
|
32
|
+
|
|
33
|
+
// Color conversion utilities
|
|
34
|
+
export {
|
|
35
|
+
hexToRgb,
|
|
36
|
+
rgbToAnsi,
|
|
37
|
+
hexToAnsi,
|
|
38
|
+
ansi256,
|
|
39
|
+
ansi256Bg,
|
|
40
|
+
rgb,
|
|
41
|
+
rgbBg,
|
|
42
|
+
hex,
|
|
43
|
+
hexBg,
|
|
44
|
+
type RgbToAnsiOptions,
|
|
45
|
+
} from './color-convert'
|
|
46
|
+
|
|
47
|
+
// Tailwind to ANSI mapping
|
|
48
|
+
export {
|
|
49
|
+
TAILWIND_COLORS,
|
|
50
|
+
tailwindToAnsi,
|
|
51
|
+
tailwindToTerminal,
|
|
52
|
+
} from './tailwind-map'
|
|
53
|
+
|
|
54
|
+
// Theme system
|
|
55
|
+
export {
|
|
56
|
+
createTerminalTheme,
|
|
57
|
+
getThemeColor,
|
|
58
|
+
cssVarToAnsi,
|
|
59
|
+
applyThemeStyles,
|
|
60
|
+
defaultTheme,
|
|
61
|
+
colors,
|
|
62
|
+
themeTokens,
|
|
63
|
+
darkTheme,
|
|
64
|
+
lightTheme,
|
|
65
|
+
highContrastTheme,
|
|
66
|
+
themePresets,
|
|
67
|
+
createTheme,
|
|
68
|
+
extendTheme,
|
|
69
|
+
composeThemes,
|
|
70
|
+
createThemeVariant,
|
|
71
|
+
detectColorScheme,
|
|
72
|
+
type TerminalThemeColors,
|
|
73
|
+
type TerminalTheme,
|
|
74
|
+
type LegacyTerminalTheme,
|
|
75
|
+
type LegacyTerminalThemeWithExtras,
|
|
76
|
+
type CreateTerminalThemeOptions,
|
|
77
|
+
} from './theme-system'
|
|
78
|
+
|
|
79
|
+
// Text styling utilities
|
|
80
|
+
export {
|
|
81
|
+
fg,
|
|
82
|
+
bg,
|
|
83
|
+
bold,
|
|
84
|
+
dim,
|
|
85
|
+
italic,
|
|
86
|
+
underline,
|
|
87
|
+
strikethrough,
|
|
88
|
+
style,
|
|
89
|
+
styled,
|
|
90
|
+
} from './text-styles'
|
|
91
|
+
|
|
92
|
+
// Color support detection and degradation
|
|
93
|
+
export {
|
|
94
|
+
detectColorSupport,
|
|
95
|
+
degradeColor,
|
|
96
|
+
adaptColor,
|
|
97
|
+
type ColorSupport,
|
|
98
|
+
} from './color-support'
|
|
99
|
+
|
|
100
|
+
// ANSI stripping utilities
|
|
101
|
+
export {
|
|
102
|
+
stripAnsi,
|
|
103
|
+
visibleLength,
|
|
104
|
+
} from './strip-ansi'
|
|
105
|
+
|
|
106
|
+
// Box drawing characters
|
|
107
|
+
export {
|
|
108
|
+
boxChars,
|
|
109
|
+
drawBox,
|
|
110
|
+
type BoxStyle,
|
|
111
|
+
} from './box-drawing'
|
|
112
|
+
|
|
113
|
+
// Zod schemas for runtime validation
|
|
114
|
+
export {
|
|
115
|
+
HexColorSchema,
|
|
116
|
+
RgbComponentSchema,
|
|
117
|
+
RgbColorSchema,
|
|
118
|
+
Ansi256CodeSchema,
|
|
119
|
+
AnsiEscapeSchema,
|
|
120
|
+
ThemeModeSchema,
|
|
121
|
+
TerminalThemeColorsSchema,
|
|
122
|
+
CreateTerminalThemeOptionsSchema,
|
|
123
|
+
LegacyTerminalThemeSchema,
|
|
124
|
+
LegacyTerminalThemeWithExtrasSchema,
|
|
125
|
+
CreateThemeInputSchema,
|
|
126
|
+
RgbToAnsiOptionsSchema,
|
|
127
|
+
ColorSupportSchema,
|
|
128
|
+
type HexColor,
|
|
129
|
+
type RgbColor,
|
|
130
|
+
type Ansi256Code,
|
|
131
|
+
type ThemeMode,
|
|
132
|
+
type CreateTerminalThemeOptionsInput,
|
|
133
|
+
type CreateThemeInput,
|
|
134
|
+
} from '../schemas'
|