@zakmandhro/bunti 0.1.3 → 0.1.5
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 +66 -25
- package/dist/colors.d.ts +74 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +227 -0
- package/dist/colors.js.map +1 -0
- package/dist/components/Box.d.ts +5 -0
- package/dist/components/Box.d.ts.map +1 -0
- package/dist/components/Box.js +4 -0
- package/dist/components/Box.js.map +1 -0
- package/dist/components/Button.d.ts +14 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +91 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/Card.d.ts +11 -0
- package/dist/components/Card.d.ts.map +1 -0
- package/dist/components/Card.js +37 -0
- package/dist/components/Card.js.map +1 -0
- package/dist/components/Header.d.ts +13 -0
- package/dist/components/Header.d.ts.map +1 -0
- package/dist/components/Header.js +54 -0
- package/dist/components/Header.js.map +1 -0
- package/dist/components/Input.d.ts +16 -0
- package/dist/components/Input.d.ts.map +1 -0
- package/dist/components/Input.js +101 -0
- package/dist/components/Input.js.map +1 -0
- package/dist/components/Modal.d.ts +13 -0
- package/dist/components/Modal.d.ts.map +1 -0
- package/dist/components/Modal.js +28 -0
- package/dist/components/Modal.js.map +1 -0
- package/dist/components/index.d.ts +7 -0
- package/dist/components/index.d.ts.map +1 -0
- package/{src/components/index.ts → dist/components/index.js} +3 -0
- package/dist/components/index.js.map +1 -0
- package/dist/data/glyphs.d.ts +5 -0
- package/dist/data/glyphs.d.ts.map +1 -0
- package/dist/data/glyphs.js +29 -0
- package/dist/data/glyphs.js.map +1 -0
- package/dist/detect.d.ts +15 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +49 -0
- package/dist/detect.js.map +1 -0
- package/dist/dsl.d.ts +159 -0
- package/dist/dsl.d.ts.map +1 -0
- package/dist/dsl.js +663 -0
- package/dist/dsl.js.map +1 -0
- package/dist/easing.d.ts +5 -0
- package/dist/easing.d.ts.map +1 -0
- package/dist/easing.js +18 -0
- package/dist/easing.js.map +1 -0
- package/dist/geometry.d.ts +38 -0
- package/dist/geometry.d.ts.map +1 -0
- package/dist/geometry.js +110 -0
- package/dist/geometry.js.map +1 -0
- package/dist/icons.d.ts +21 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +185 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +157 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +89 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +533 -0
- package/dist/layout.js.map +1 -0
- package/dist/render.d.ts +17 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +335 -0
- package/dist/render.js.map +1 -0
- package/dist/state.d.ts +92 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +105 -0
- package/dist/state.js.map +1 -0
- package/dist/utils.d.ts +27 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +157 -0
- package/dist/utils.js.map +1 -0
- package/package.json +33 -8
- package/src/colors.ts +0 -255
- package/src/components/Button.ts +0 -104
- package/src/components/Card.ts +0 -53
- package/src/components/Header.ts +0 -65
- package/src/components/Input.ts +0 -124
- package/src/data/glyphs.ts +0 -30
- package/src/detect.ts +0 -60
- package/src/dsl.ts +0 -661
- package/src/icons.ts +0 -186
- package/src/index.ts +0 -72
- package/src/layout.ts +0 -640
- package/src/render.ts +0 -302
- package/src/state.ts +0 -148
- package/src/utils.ts +0 -165
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQhD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,SAAM,GAAG,MAAM,CAkDvE;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA+D7D;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAOpE"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bunti Utility Suite - Bun-Native High Performance Edition
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Strips ANSI escape sequences from a string to allow accurate width measurement.
|
|
6
|
+
*/
|
|
7
|
+
export function stripAnsi(str) {
|
|
8
|
+
if (!str)
|
|
9
|
+
return '';
|
|
10
|
+
return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '');
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Calculates the visible width of a string using Bun's SIMD-optimized API.
|
|
14
|
+
*/
|
|
15
|
+
export function visibleWidth(str) {
|
|
16
|
+
if (!str)
|
|
17
|
+
return 0;
|
|
18
|
+
const lines = str.split('\n');
|
|
19
|
+
if (lines.length > 1) {
|
|
20
|
+
return Math.max(...lines.map(visibleWidth));
|
|
21
|
+
}
|
|
22
|
+
return Bun.stringWidth(stripAnsi(str));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Returns the width of a single character or grapheme.
|
|
26
|
+
*/
|
|
27
|
+
export function charWidth(char) {
|
|
28
|
+
const clean = stripAnsi(char);
|
|
29
|
+
if (clean.length === 0)
|
|
30
|
+
return 0;
|
|
31
|
+
return Bun.stringWidth(clean);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Truncates a string to a visible width while preserving ANSI codes.
|
|
35
|
+
* GUARANTEE: Never slices an ANSI escape sequence in half.
|
|
36
|
+
*/
|
|
37
|
+
export function truncate(str, width, tail = '…') {
|
|
38
|
+
const visible = visibleWidth(str);
|
|
39
|
+
if (visible <= width)
|
|
40
|
+
return str;
|
|
41
|
+
const targetWidth = width - visibleWidth(tail);
|
|
42
|
+
if (targetWidth <= 0)
|
|
43
|
+
return '';
|
|
44
|
+
let currentWidth = 0;
|
|
45
|
+
let out = '';
|
|
46
|
+
// Custom parsing loop to isolate ANSI sequences from text
|
|
47
|
+
const regex = /\x1B\[[0-9;]*[a-zA-Z]/g;
|
|
48
|
+
let lastIndex = 0;
|
|
49
|
+
let match = null;
|
|
50
|
+
while ((match = regex.exec(str)) !== null) {
|
|
51
|
+
// 1. Process text before the ANSI code
|
|
52
|
+
const textSegment = str.substring(lastIndex, match.index);
|
|
53
|
+
if (textSegment.length > 0) {
|
|
54
|
+
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
|
|
55
|
+
for (const { segment } of segmenter.segment(textSegment)) {
|
|
56
|
+
const w = charWidth(segment);
|
|
57
|
+
if (currentWidth + w > targetWidth) {
|
|
58
|
+
return `${out}\x1b[0m${tail}`; // Return immediately upon hitting limit
|
|
59
|
+
}
|
|
60
|
+
out += segment;
|
|
61
|
+
currentWidth += w;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// 2. Append the ANSI code itself (zero width)
|
|
65
|
+
out += match[0];
|
|
66
|
+
lastIndex = regex.lastIndex;
|
|
67
|
+
}
|
|
68
|
+
// 3. Process remaining text after the last ANSI code
|
|
69
|
+
const remainingText = str.substring(lastIndex);
|
|
70
|
+
if (remainingText.length > 0) {
|
|
71
|
+
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
|
|
72
|
+
for (const { segment } of segmenter.segment(remainingText)) {
|
|
73
|
+
const w = charWidth(segment);
|
|
74
|
+
if (currentWidth + w > targetWidth) {
|
|
75
|
+
return `${out}\x1b[0m${tail}`;
|
|
76
|
+
}
|
|
77
|
+
out += segment;
|
|
78
|
+
currentWidth += w;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return `${out}\x1b[0m`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Wraps text to a specific visible width, preserving ANSI codes.
|
|
85
|
+
* Implements word-based wrapping with fallback to char-breaking for long tokens.
|
|
86
|
+
*/
|
|
87
|
+
export function wrapText(str, width) {
|
|
88
|
+
if (width <= 0)
|
|
89
|
+
return [str];
|
|
90
|
+
const result = [];
|
|
91
|
+
const rawLines = str.split('\n');
|
|
92
|
+
for (const rawLine of rawLines) {
|
|
93
|
+
if (visibleWidth(rawLine) <= width) {
|
|
94
|
+
result.push(rawLine);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
// Split into tokens: words and whitespaces
|
|
98
|
+
const tokens = rawLine.split(/(\s+)/).filter((t) => t.length > 0);
|
|
99
|
+
let currentLine = '';
|
|
100
|
+
let currentWidth = 0;
|
|
101
|
+
for (const token of tokens) {
|
|
102
|
+
const tokenWidth = visibleWidth(token);
|
|
103
|
+
const isWhitespace = /^\s+$/.test(stripAnsi(token));
|
|
104
|
+
// If token itself is too wide, we MUST break it by character
|
|
105
|
+
if (tokenWidth > width) {
|
|
106
|
+
// Flush current line if it exists
|
|
107
|
+
if (currentLine) {
|
|
108
|
+
result.push(`${currentLine}\x1b[0m`);
|
|
109
|
+
currentLine = '';
|
|
110
|
+
currentWidth = 0;
|
|
111
|
+
}
|
|
112
|
+
// Segmented break of the long token
|
|
113
|
+
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
|
|
114
|
+
for (const { segment } of segmenter.segment(token)) {
|
|
115
|
+
const sw = charWidth(segment);
|
|
116
|
+
if (currentWidth + sw > width) {
|
|
117
|
+
result.push(`${currentLine}\x1b[0m`);
|
|
118
|
+
currentLine = segment;
|
|
119
|
+
currentWidth = sw;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
currentLine += segment;
|
|
123
|
+
currentWidth += sw;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
// Normal word wrap
|
|
129
|
+
if (currentWidth + tokenWidth > width) {
|
|
130
|
+
if (isWhitespace) {
|
|
131
|
+
// Swallow leading whitespace on new lines
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
result.push(`${currentLine.trimEnd()}\x1b[0m`);
|
|
135
|
+
currentLine = token;
|
|
136
|
+
currentWidth = tokenWidth;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
currentLine += token;
|
|
140
|
+
currentWidth += tokenWidth;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (currentLine)
|
|
144
|
+
result.push(currentLine.trimEnd());
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
export function indentBlock(content, columns) {
|
|
149
|
+
if (columns <= 0)
|
|
150
|
+
return content;
|
|
151
|
+
const prefix = ' '.repeat(columns);
|
|
152
|
+
return content
|
|
153
|
+
.split('\n')
|
|
154
|
+
.map((line) => prefix + line)
|
|
155
|
+
.join('\n');
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,20 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zakmandhro/bunti",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "A Bun-native terminal layout engine with zero dependencies, 60fps rendering, and an agent-optimized functional API.",
|
|
5
|
-
"
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
6
8
|
"exports": {
|
|
7
|
-
".":
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"./
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./components": {
|
|
14
|
+
"types": "./dist/components/index.d.ts",
|
|
15
|
+
"import": "./dist/components/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./geometry": {
|
|
18
|
+
"types": "./dist/geometry.d.ts",
|
|
19
|
+
"import": "./dist/geometry.js"
|
|
20
|
+
},
|
|
21
|
+
"./icons": {
|
|
22
|
+
"types": "./dist/icons.d.ts",
|
|
23
|
+
"import": "./dist/icons.js"
|
|
24
|
+
},
|
|
25
|
+
"./layout": {
|
|
26
|
+
"types": "./dist/layout.d.ts",
|
|
27
|
+
"import": "./dist/layout.js"
|
|
28
|
+
},
|
|
29
|
+
"./render": {
|
|
30
|
+
"types": "./dist/render.d.ts",
|
|
31
|
+
"import": "./dist/render.js"
|
|
32
|
+
}
|
|
12
33
|
},
|
|
13
34
|
"type": "module",
|
|
14
35
|
"engines": {
|
|
15
36
|
"bun": ">=1.0.0"
|
|
16
37
|
},
|
|
17
38
|
"scripts": {
|
|
39
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
40
|
+
"prepublishOnly": "bun run typecheck && bun run lint && bun run test && bun run build",
|
|
18
41
|
"test": "bun test",
|
|
19
42
|
"bench": "bun scripts/bench.ts",
|
|
20
43
|
"typecheck": "tsc --noEmit",
|
|
@@ -39,7 +62,9 @@
|
|
|
39
62
|
"author": "Zak Mandhro <zakmandhro@gmail.com>",
|
|
40
63
|
"license": "MIT",
|
|
41
64
|
"files": [
|
|
42
|
-
"
|
|
65
|
+
"dist",
|
|
66
|
+
"README.md",
|
|
67
|
+
"LICENSE"
|
|
43
68
|
],
|
|
44
69
|
"peerDependencies": {
|
|
45
70
|
"picocolors": "^1.0.0"
|
package/src/colors.ts
DELETED
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bunti Semantic & Palette Color System
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { RGB } from './state';
|
|
6
|
-
|
|
7
|
-
export interface Gradient {
|
|
8
|
-
colors: RGB[];
|
|
9
|
-
direction: 'vertical' | 'horizontal';
|
|
10
|
-
steps: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const PALETTE = {
|
|
14
|
-
// Greyscale
|
|
15
|
-
slate: '235',
|
|
16
|
-
ash: '240',
|
|
17
|
-
gray: '244',
|
|
18
|
-
silver: '247',
|
|
19
|
-
white: '255',
|
|
20
|
-
black: '0',
|
|
21
|
-
|
|
22
|
-
// Deep Blues & Space
|
|
23
|
-
midnight: '17',
|
|
24
|
-
ocean: '24',
|
|
25
|
-
sky: '33',
|
|
26
|
-
'bunti-blue': '38',
|
|
27
|
-
'deep-navy': '17',
|
|
28
|
-
nebula: '61',
|
|
29
|
-
plasma: '165',
|
|
30
|
-
|
|
31
|
-
// Semantic
|
|
32
|
-
success: '40',
|
|
33
|
-
warning: '214',
|
|
34
|
-
error: '196',
|
|
35
|
-
info: '39',
|
|
36
|
-
|
|
37
|
-
// Accents
|
|
38
|
-
gold: '220',
|
|
39
|
-
rose: '211',
|
|
40
|
-
mint: '121',
|
|
41
|
-
} as const;
|
|
42
|
-
|
|
43
|
-
const RGB_REGISTRY: Record<string, RGB> = {
|
|
44
|
-
black: { r: 0, g: 0, b: 0 },
|
|
45
|
-
red: { r: 200, g: 50, b: 50 },
|
|
46
|
-
green: { r: 50, g: 200, b: 50 },
|
|
47
|
-
yellow: { r: 200, g: 200, b: 50 },
|
|
48
|
-
blue: { r: 50, g: 50, b: 200 },
|
|
49
|
-
magenta: { r: 200, g: 50, b: 200 },
|
|
50
|
-
cyan: { r: 50, g: 200, b: 200 },
|
|
51
|
-
white: { r: 255, g: 255, b: 255 },
|
|
52
|
-
gray: { r: 128, g: 128, b: 128 },
|
|
53
|
-
// Greyscale
|
|
54
|
-
slate: { r: 40, g: 44, b: 52 },
|
|
55
|
-
ash: { r: 75, g: 82, b: 99 },
|
|
56
|
-
silver: { r: 188, g: 192, b: 204 },
|
|
57
|
-
// Deep Blues & Space
|
|
58
|
-
midnight: { r: 15, g: 15, b: 35 },
|
|
59
|
-
ocean: { r: 20, g: 40, b: 80 },
|
|
60
|
-
sky: { r: 0, g: 135, b: 255 },
|
|
61
|
-
'bunti-blue': { r: 59, g: 188, b: 225 },
|
|
62
|
-
'deep-navy': { r: 0, g: 51, b: 102 },
|
|
63
|
-
nebula: { r: 95, g: 95, b: 175 },
|
|
64
|
-
plasma: { r: 201, g: 0, b: 255 },
|
|
65
|
-
// Semantic
|
|
66
|
-
success: { r: 0, g: 215, b: 0 },
|
|
67
|
-
warning: { r: 255, g: 175, b: 0 },
|
|
68
|
-
error: { r: 215, g: 0, b: 0 },
|
|
69
|
-
info: { r: 0, g: 175, b: 255 },
|
|
70
|
-
// Accents
|
|
71
|
-
gold: { r: 255, g: 215, b: 0 },
|
|
72
|
-
rose: { r: 255, g: 175, b: 175 },
|
|
73
|
-
mint: { r: 175, g: 255, b: 175 },
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// Map standard names to ANSI-256 for basic compat
|
|
77
|
-
const NAME_TO_ANSI: Record<string, string> = {
|
|
78
|
-
red: '160',
|
|
79
|
-
green: '40',
|
|
80
|
-
yellow: '220',
|
|
81
|
-
blue: '33',
|
|
82
|
-
magenta: '165',
|
|
83
|
-
cyan: '39',
|
|
84
|
-
white: '255',
|
|
85
|
-
black: '0',
|
|
86
|
-
gray: '244',
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
export type PaletteColor = keyof typeof PALETTE;
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Parses a hex color string (#RRGGBB or #RGB) to an RGB object.
|
|
93
|
-
*/
|
|
94
|
-
export function hexToRGB(hex: string): RGB {
|
|
95
|
-
const h = hex.replace('#', '');
|
|
96
|
-
if (h.length === 3) {
|
|
97
|
-
return {
|
|
98
|
-
r: parseInt(h[0]! + h[0]!, 16),
|
|
99
|
-
g: parseInt(h[1]! + h[1]!, 16),
|
|
100
|
-
b: parseInt(h[2]! + h[2]!, 16),
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
return {
|
|
104
|
-
r: parseInt(h.substring(0, 2), 16),
|
|
105
|
-
g: parseInt(h.substring(2, 4), 16),
|
|
106
|
-
b: parseInt(h.substring(4, 6), 16),
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Resolves a palette name, color code, or RGB object to a string for ANSI.
|
|
112
|
-
*/
|
|
113
|
-
export function resolveColor(
|
|
114
|
-
color: PaletteColor | string | number | RGB,
|
|
115
|
-
): string | number {
|
|
116
|
-
if (typeof color === 'object' && 'r' in color) {
|
|
117
|
-
return `2;${color.r};${color.g};${color.b}`;
|
|
118
|
-
}
|
|
119
|
-
if (typeof color === 'string') {
|
|
120
|
-
if (color.startsWith('#')) {
|
|
121
|
-
const rgb = hexToRGB(color);
|
|
122
|
-
return `2;${rgb.r};${rgb.g};${rgb.b}`;
|
|
123
|
-
}
|
|
124
|
-
return (PALETTE as any)[color] || NAME_TO_ANSI[color] || color;
|
|
125
|
-
}
|
|
126
|
-
return color;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Creates a multi-stop gradient (array of RGB objects) between multiple colors.
|
|
131
|
-
*/
|
|
132
|
-
export function createGradient(
|
|
133
|
-
arg1: (string | number | RGB)[] | string | number | RGB,
|
|
134
|
-
arg2?: string | number | RGB | number,
|
|
135
|
-
arg3?: number,
|
|
136
|
-
): RGB[] {
|
|
137
|
-
let colors: (string | number | RGB)[] = [];
|
|
138
|
-
let steps = 5;
|
|
139
|
-
|
|
140
|
-
if (Array.isArray(arg1)) {
|
|
141
|
-
colors = arg1;
|
|
142
|
-
steps = typeof arg2 === 'number' ? arg2 : 5;
|
|
143
|
-
} else if (arg1 && arg2) {
|
|
144
|
-
colors = [arg1, arg2 as any];
|
|
145
|
-
steps = typeof arg3 === 'number' ? arg3 : 5;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (colors.length === 0) return [];
|
|
149
|
-
if (colors.length === 1)
|
|
150
|
-
return Array(steps).fill(resolveColorToRGB(colors[0]));
|
|
151
|
-
if (steps <= 1) return [resolveColorToRGB(colors[0])];
|
|
152
|
-
|
|
153
|
-
const result: RGB[] = [];
|
|
154
|
-
const rgbColors = colors.map(resolveColorToRGB);
|
|
155
|
-
|
|
156
|
-
for (let i = 0; i < steps; i++) {
|
|
157
|
-
const globalT = i / (steps - 1);
|
|
158
|
-
const segmentCount = rgbColors.length - 1;
|
|
159
|
-
const segmentIdx = Math.min(
|
|
160
|
-
Math.floor(globalT * segmentCount),
|
|
161
|
-
segmentCount - 1,
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
const start = rgbColors[segmentIdx];
|
|
165
|
-
const end = rgbColors[segmentIdx + 1];
|
|
166
|
-
const t = globalT * segmentCount - segmentIdx;
|
|
167
|
-
|
|
168
|
-
result.push({
|
|
169
|
-
r: Math.round(start!.r + (end!.r - start!.r) * t),
|
|
170
|
-
g: Math.round(start!.g + (end!.g - start!.g) * t),
|
|
171
|
-
b: Math.round(start!.b + (end!.b - start!.b) * t),
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return result;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Helper to resolve any color type to an RGB object.
|
|
180
|
-
*/
|
|
181
|
-
function resolveColorToRGB(color: any): RGB {
|
|
182
|
-
if (typeof color === 'object' && 'r' in color) return color;
|
|
183
|
-
if (typeof color === 'string' && color.startsWith('#'))
|
|
184
|
-
return hexToRGB(color);
|
|
185
|
-
|
|
186
|
-
// 1. Check registry
|
|
187
|
-
if (typeof color === 'string' && RGB_REGISTRY[color])
|
|
188
|
-
return RGB_REGISTRY[color];
|
|
189
|
-
|
|
190
|
-
// 2. Check Palette
|
|
191
|
-
const paletteValue = (PALETTE as any)[color];
|
|
192
|
-
if (paletteValue) {
|
|
193
|
-
// If palette value is a name, look it up in registry
|
|
194
|
-
const registered = RGB_REGISTRY[color];
|
|
195
|
-
if (registered) return registered;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Fallback
|
|
199
|
-
return { r: 128, g: 128, b: 128 };
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Adjusts the brightness of a color.
|
|
204
|
-
*/
|
|
205
|
-
export function adjustBrightness(color: any, amount: number): RGB {
|
|
206
|
-
const rgb = resolveColorToRGB(color);
|
|
207
|
-
const factor = 1 + amount / 100;
|
|
208
|
-
return {
|
|
209
|
-
r: Math.max(0, Math.min(255, Math.round(rgb.r * factor))),
|
|
210
|
-
g: Math.max(0, Math.min(255, Math.round(rgb.g * factor))),
|
|
211
|
-
b: Math.max(0, Math.min(255, Math.round(rgb.b * factor))),
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Returns a function that darkens a color and can be used as a style wrapper.
|
|
217
|
-
*/
|
|
218
|
-
export function darken(color: any, amount: number = 20) {
|
|
219
|
-
const adjusted = adjustBrightness(color, -amount);
|
|
220
|
-
return (text: string) => fg(adjusted, text);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Returns a function that lightens a color and can be used as a style wrapper.
|
|
225
|
-
*/
|
|
226
|
-
export function lighten(color: any, amount: number = 20) {
|
|
227
|
-
const adjusted = adjustBrightness(color, amount);
|
|
228
|
-
return (text: string) => fg(adjusted, text);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Returns an RGB object for TrueColor rendering.
|
|
233
|
-
*/
|
|
234
|
-
export function rgb(r: number, g: number, b: number) {
|
|
235
|
-
return { r, g, b };
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Returns an ANSI escape sequence for a color.
|
|
240
|
-
*/
|
|
241
|
-
export function fg(color: any, text: string): string {
|
|
242
|
-
const code = resolveColor(color);
|
|
243
|
-
const codeStr = String(code);
|
|
244
|
-
const prefix =
|
|
245
|
-
typeof color === 'object' || codeStr.startsWith('2;') ? '38' : '38;5';
|
|
246
|
-
return `\x1b[${prefix};${codeStr}m${text}\x1b[0m`;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
export function bg(color: any, text: string): string {
|
|
250
|
-
const code = resolveColor(color);
|
|
251
|
-
const codeStr = String(code);
|
|
252
|
-
const prefix =
|
|
253
|
-
typeof color === 'object' || codeStr.startsWith('2;') ? '48' : '48;5';
|
|
254
|
-
return `\x1b[${prefix};${codeStr}m${text}\x1b[0m`;
|
|
255
|
-
}
|
package/src/components/Button.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import type { BuntiContext } from '../dsl';
|
|
2
|
-
import type { StyleOptions } from '../layout';
|
|
3
|
-
|
|
4
|
-
export interface ButtonProps extends StyleOptions {
|
|
5
|
-
id: string;
|
|
6
|
-
label: string;
|
|
7
|
-
icon?: string;
|
|
8
|
-
variant?: 'primary' | 'secondary' | 'danger';
|
|
9
|
-
onClick?: () => void;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Tactical Button Component
|
|
14
|
-
* Fully reactive to keyboard focus and action events.
|
|
15
|
-
*/
|
|
16
|
-
export function Button(ctx: BuntiContext, props: ButtonProps) {
|
|
17
|
-
const {
|
|
18
|
-
box,
|
|
19
|
-
color,
|
|
20
|
-
focusable,
|
|
21
|
-
state,
|
|
22
|
-
offsetX,
|
|
23
|
-
offsetY,
|
|
24
|
-
mouseX,
|
|
25
|
-
mouseY,
|
|
26
|
-
isMouseDown,
|
|
27
|
-
} = ctx;
|
|
28
|
-
|
|
29
|
-
// 1. Register in the global focus loop (for TAB navigation)
|
|
30
|
-
const isSelected = focusable(props.id);
|
|
31
|
-
|
|
32
|
-
// 2. Resolve final dimensions to perform Mouse Hit-Testing
|
|
33
|
-
const finalLabel = props.icon ? `${props.icon} ${props.label}` : props.label;
|
|
34
|
-
const w = props.width || Math.max(12, finalLabel.length + 4);
|
|
35
|
-
const h = props.height || 3;
|
|
36
|
-
|
|
37
|
-
const resolvedW = typeof w === 'number' ? w : ctx.width;
|
|
38
|
-
const absX =
|
|
39
|
-
props.width === '100%'
|
|
40
|
-
? offsetX
|
|
41
|
-
: offsetX + Math.max(0, Math.floor((ctx.width - resolvedW) / 2));
|
|
42
|
-
const absY = offsetY + ctx.cursorY;
|
|
43
|
-
|
|
44
|
-
const isHovered =
|
|
45
|
-
mouseX >= absX &&
|
|
46
|
-
mouseX < absX + resolvedW &&
|
|
47
|
-
mouseY >= absY &&
|
|
48
|
-
mouseY < absY + (h as number);
|
|
49
|
-
|
|
50
|
-
// 3. Resolve Theme & State
|
|
51
|
-
let bg: any = 254; // Near white
|
|
52
|
-
let fg: any = 'black';
|
|
53
|
-
let borderCol: any = { r: 217, g: 216, b: 213 };
|
|
54
|
-
|
|
55
|
-
if (props.variant === 'primary') {
|
|
56
|
-
bg = 'black';
|
|
57
|
-
fg = 'white';
|
|
58
|
-
borderCol = 'black';
|
|
59
|
-
} else if (props.variant === 'danger') {
|
|
60
|
-
bg = 'error';
|
|
61
|
-
borderCol = color.lighten('error', 20);
|
|
62
|
-
fg = 'white';
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Active Interactive State
|
|
66
|
-
const isActive = isSelected || isHovered;
|
|
67
|
-
|
|
68
|
-
if (isActive) {
|
|
69
|
-
if (props.variant === 'primary') {
|
|
70
|
-
bg = 'ash'; // Hover state for primary
|
|
71
|
-
} else {
|
|
72
|
-
borderCol = 'black';
|
|
73
|
-
if (isMouseDown && isHovered) {
|
|
74
|
-
bg = color.darken(bg, 5); // Pressed state
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// 4. Handle Interaction
|
|
80
|
-
const wasClicked = isHovered && isMouseDown && state.mouseButton === 0;
|
|
81
|
-
const wasActivated =
|
|
82
|
-
isSelected && (state.lastKey === 'enter' || state.lastKey === ' ');
|
|
83
|
-
|
|
84
|
-
if (wasClicked || wasActivated) {
|
|
85
|
-
if (props.onClick) props.onClick();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// 5. Render the Primitive
|
|
89
|
-
return box(
|
|
90
|
-
{
|
|
91
|
-
width: w,
|
|
92
|
-
height: h,
|
|
93
|
-
border: props.variant === 'primary' ? 'none' : props.border || 'rounded',
|
|
94
|
-
borderColor: borderCol,
|
|
95
|
-
bgColor: bg,
|
|
96
|
-
align: props.align || 'center',
|
|
97
|
-
valign: props.valign || 'middle',
|
|
98
|
-
padding: props.padding || [0, 1],
|
|
99
|
-
},
|
|
100
|
-
({ text }) => {
|
|
101
|
-
text(color.fg(fg, isActive ? color.bold(finalLabel) : finalLabel));
|
|
102
|
-
},
|
|
103
|
-
);
|
|
104
|
-
}
|
package/src/components/Card.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { BuntiContext, DSLBoxOptions } from '../dsl';
|
|
2
|
-
|
|
3
|
-
export interface CardProps extends DSLBoxOptions {
|
|
4
|
-
title?: string;
|
|
5
|
-
theme?: 'default' | 'accent' | 'danger';
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Tactical Card Component
|
|
10
|
-
* A structural container with an integrated header.
|
|
11
|
-
*/
|
|
12
|
-
export function Card(
|
|
13
|
-
ctx: BuntiContext,
|
|
14
|
-
props: CardProps,
|
|
15
|
-
callback: (sub: BuntiContext) => void,
|
|
16
|
-
) {
|
|
17
|
-
const { box, color } = ctx;
|
|
18
|
-
|
|
19
|
-
const neutralGray = { r: 217, g: 216, b: 213 };
|
|
20
|
-
let borderColor: any = neutralGray;
|
|
21
|
-
let titleColor: any = neutralGray;
|
|
22
|
-
|
|
23
|
-
if (props.theme === 'accent') {
|
|
24
|
-
titleColor = 'black';
|
|
25
|
-
// Border remains neutral gray
|
|
26
|
-
} else if (props.theme === 'danger') {
|
|
27
|
-
borderColor = 'error';
|
|
28
|
-
titleColor = 'error';
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return box(
|
|
32
|
-
{
|
|
33
|
-
x: props.x,
|
|
34
|
-
y: props.y,
|
|
35
|
-
anchor: props.anchor,
|
|
36
|
-
width: props.width || '100%',
|
|
37
|
-
height: props.height,
|
|
38
|
-
minHeight: props.minHeight,
|
|
39
|
-
border: props.border || 'frame',
|
|
40
|
-
borderColor: props.borderColor || borderColor,
|
|
41
|
-
padding: props.padding || [1, 2],
|
|
42
|
-
bgColor: props.bgColor,
|
|
43
|
-
},
|
|
44
|
-
(sub) => {
|
|
45
|
-
if (props.title) {
|
|
46
|
-
sub.text(
|
|
47
|
-
color.fg(titleColor, color.bold(`${props.title.toUpperCase()}\n\n`)),
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
callback(sub);
|
|
51
|
-
},
|
|
52
|
-
);
|
|
53
|
-
}
|