@zakmandhro/bunti 0.1.4 → 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.
Files changed (92) hide show
  1. package/README.md +66 -25
  2. package/dist/colors.d.ts +74 -0
  3. package/dist/colors.d.ts.map +1 -0
  4. package/dist/colors.js +227 -0
  5. package/dist/colors.js.map +1 -0
  6. package/dist/components/Box.d.ts +5 -0
  7. package/dist/components/Box.d.ts.map +1 -0
  8. package/dist/components/Box.js +4 -0
  9. package/dist/components/Box.js.map +1 -0
  10. package/dist/components/Button.d.ts +14 -0
  11. package/dist/components/Button.d.ts.map +1 -0
  12. package/dist/components/Button.js +91 -0
  13. package/dist/components/Button.js.map +1 -0
  14. package/dist/components/Card.d.ts +11 -0
  15. package/dist/components/Card.d.ts.map +1 -0
  16. package/dist/components/Card.js +37 -0
  17. package/dist/components/Card.js.map +1 -0
  18. package/dist/components/Header.d.ts +13 -0
  19. package/dist/components/Header.d.ts.map +1 -0
  20. package/dist/components/Header.js +54 -0
  21. package/dist/components/Header.js.map +1 -0
  22. package/dist/components/Input.d.ts +16 -0
  23. package/dist/components/Input.d.ts.map +1 -0
  24. package/dist/components/Input.js +101 -0
  25. package/dist/components/Input.js.map +1 -0
  26. package/dist/components/Modal.d.ts +13 -0
  27. package/dist/components/Modal.d.ts.map +1 -0
  28. package/dist/components/Modal.js +28 -0
  29. package/dist/components/Modal.js.map +1 -0
  30. package/dist/components/index.d.ts +7 -0
  31. package/dist/components/index.d.ts.map +1 -0
  32. package/{src/components/index.ts → dist/components/index.js} +3 -0
  33. package/dist/components/index.js.map +1 -0
  34. package/dist/data/glyphs.d.ts +5 -0
  35. package/dist/data/glyphs.d.ts.map +1 -0
  36. package/dist/data/glyphs.js +29 -0
  37. package/dist/data/glyphs.js.map +1 -0
  38. package/dist/detect.d.ts +15 -0
  39. package/dist/detect.d.ts.map +1 -0
  40. package/dist/detect.js +49 -0
  41. package/dist/detect.js.map +1 -0
  42. package/dist/dsl.d.ts +159 -0
  43. package/dist/dsl.d.ts.map +1 -0
  44. package/dist/dsl.js +663 -0
  45. package/dist/dsl.js.map +1 -0
  46. package/dist/easing.d.ts +5 -0
  47. package/dist/easing.d.ts.map +1 -0
  48. package/dist/easing.js +18 -0
  49. package/dist/easing.js.map +1 -0
  50. package/dist/geometry.d.ts +38 -0
  51. package/dist/geometry.d.ts.map +1 -0
  52. package/dist/geometry.js +110 -0
  53. package/dist/geometry.js.map +1 -0
  54. package/dist/icons.d.ts +21 -0
  55. package/dist/icons.d.ts.map +1 -0
  56. package/dist/icons.js +185 -0
  57. package/dist/icons.js.map +1 -0
  58. package/dist/index.d.ts +157 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +36 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/layout.d.ts +89 -0
  63. package/dist/layout.d.ts.map +1 -0
  64. package/dist/layout.js +533 -0
  65. package/dist/layout.js.map +1 -0
  66. package/dist/render.d.ts +17 -0
  67. package/dist/render.d.ts.map +1 -0
  68. package/dist/render.js +335 -0
  69. package/dist/render.js.map +1 -0
  70. package/dist/state.d.ts +92 -0
  71. package/dist/state.d.ts.map +1 -0
  72. package/dist/state.js +105 -0
  73. package/dist/state.js.map +1 -0
  74. package/dist/utils.d.ts +27 -0
  75. package/dist/utils.d.ts.map +1 -0
  76. package/dist/utils.js +157 -0
  77. package/dist/utils.js.map +1 -0
  78. package/package.json +33 -8
  79. package/src/colors.ts +0 -255
  80. package/src/components/Button.ts +0 -104
  81. package/src/components/Card.ts +0 -53
  82. package/src/components/Header.ts +0 -65
  83. package/src/components/Input.ts +0 -124
  84. package/src/data/glyphs.ts +0 -30
  85. package/src/detect.ts +0 -60
  86. package/src/dsl.ts +0 -661
  87. package/src/icons.ts +0 -186
  88. package/src/index.ts +0 -72
  89. package/src/layout.ts +0 -640
  90. package/src/render.ts +0 -302
  91. package/src/state.ts +0 -148
  92. 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.4",
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
- "module": "src/index.ts",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
6
8
  "exports": {
7
- ".": "./src/index.ts",
8
- "./components": "./src/components/index.ts",
9
- "./icons": "./src/icons.ts",
10
- "./layout": "./src/layout.ts",
11
- "./render": "./src/render.ts"
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
- "src"
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
- }
@@ -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
- }
@@ -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
- }