cli-menu-kit 0.2.0 → 0.2.1

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 (45) hide show
  1. package/dist/components/display/header.d.ts +40 -0
  2. package/dist/components/display/header.js +331 -18
  3. package/dist/components/display/headers.d.ts +1 -0
  4. package/dist/components/display/headers.js +15 -5
  5. package/dist/components/display/index.d.ts +1 -1
  6. package/dist/components/display/index.js +3 -1
  7. package/dist/components/display/messages.js +5 -5
  8. package/dist/components/display/progress.d.ts +17 -0
  9. package/dist/components/display/progress.js +18 -0
  10. package/dist/components/display/summary.js +72 -10
  11. package/dist/components/display/table.d.ts +2 -0
  12. package/dist/components/display/table.js +7 -6
  13. package/dist/components/inputs/language-input.js +8 -5
  14. package/dist/components/inputs/number-input.js +19 -14
  15. package/dist/components/inputs/text-input.js +50 -13
  16. package/dist/components/menus/boolean-menu.js +33 -20
  17. package/dist/components/menus/checkbox-menu.js +5 -2
  18. package/dist/components/menus/checkbox-table-menu.js +12 -9
  19. package/dist/components/menus/radio-menu-split.d.ts +1 -0
  20. package/dist/components/menus/radio-menu-split.js +26 -16
  21. package/dist/components/menus/radio-menu.js +67 -38
  22. package/dist/components.js +3 -3
  23. package/dist/config/index.d.ts +5 -0
  24. package/dist/config/index.js +21 -0
  25. package/dist/config/language-config.d.ts +73 -0
  26. package/dist/config/language-config.js +157 -0
  27. package/dist/config/user-config.d.ts +83 -0
  28. package/dist/config/user-config.js +185 -0
  29. package/dist/core/colors.d.ts +24 -18
  30. package/dist/core/colors.js +74 -7
  31. package/dist/core/renderer.js +26 -18
  32. package/dist/core/terminal.d.ts +13 -0
  33. package/dist/core/terminal.js +87 -0
  34. package/dist/features/commands.js +23 -22
  35. package/dist/index.d.ts +3 -1
  36. package/dist/index.js +21 -2
  37. package/dist/layout.d.ts +50 -51
  38. package/dist/layout.js +69 -117
  39. package/dist/page-layout.d.ts +31 -0
  40. package/dist/page-layout.js +46 -7
  41. package/dist/types/input.types.d.ts +8 -0
  42. package/dist/types/layout.types.d.ts +56 -0
  43. package/dist/types/layout.types.js +36 -0
  44. package/dist/types/menu.types.d.ts +4 -0
  45. package/package.json +4 -1
@@ -2,16 +2,39 @@
2
2
  * Header component for CLI applications
3
3
  * Displays ASCII art, title, description, version and URL
4
4
  */
5
+ type FigletSize = 'small' | 'medium' | 'large';
5
6
  /**
6
7
  * Header configuration
7
8
  */
8
9
  export interface HeaderConfig {
9
10
  /** ASCII art lines (array of strings) */
10
11
  asciiArt?: string[];
12
+ /** Generate ASCII art from figlet text (overrides asciiArt when provided) */
13
+ figletText?: string;
14
+ /** Figlet font name (raw figlet name, e.g. "ANSI Shadow", "Standard", "Block") */
15
+ figletFont?: string;
16
+ /** Figlet size preset (default: medium) */
17
+ figletSize?: FigletSize;
18
+ /** Scale figlet output (1-4, default: 1) */
19
+ figletScale?: number;
20
+ /** Optional vertical scale override (0.4-4) */
21
+ figletScaleY?: number;
11
22
  /** Application title */
12
23
  title?: string;
24
+ /** Optional explicit color for title line */
25
+ titleColor?: string;
26
+ /** Optional ANSI start color for title gradient */
27
+ titleGradientStart?: string;
28
+ /** Optional ANSI end color for title gradient */
29
+ titleGradientEnd?: string;
13
30
  /** Application description */
14
31
  description?: string;
32
+ /** Optional explicit color for description line */
33
+ descriptionColor?: string;
34
+ /** Optional ANSI start color for description gradient */
35
+ descriptionGradientStart?: string;
36
+ /** Optional ANSI end color for description gradient */
37
+ descriptionGradientEnd?: string;
15
38
  /** Version number */
16
39
  version?: string;
17
40
  /** Project URL */
@@ -20,11 +43,28 @@ export interface HeaderConfig {
20
43
  menuTitle?: string;
21
44
  /** Box width (default: 60) */
22
45
  boxWidth?: number;
46
+ /** Toggle outer border box (default: true) */
47
+ showBoxBorder?: boolean;
48
+ /** Fill header box content area background (default: false) */
49
+ fillBox?: boolean;
50
+ /** ANSI background color for box fill (default: bgBlack) */
51
+ fillBoxColor?: string;
52
+ /** Optional ANSI background gradient start color for box fill */
53
+ fillBoxGradientStart?: string;
54
+ /** Optional ANSI background gradient end color for box fill */
55
+ fillBoxGradientEnd?: string;
23
56
  /** Header color (default: cyan) */
24
57
  color?: string;
58
+ /** Optional explicit color for ASCII art/logo lines */
59
+ asciiArtColor?: string;
60
+ /** Optional ANSI start color for ASCII art gradient */
61
+ asciiArtGradientStart?: string;
62
+ /** Optional ANSI end color for ASCII art gradient */
63
+ asciiArtGradientEnd?: string;
25
64
  }
26
65
  /**
27
66
  * Render a boxed header with ASCII art, title, and description
28
67
  * @param config - Header configuration
29
68
  */
30
69
  export declare function renderHeader(config: HeaderConfig): void;
70
+ export {};
@@ -3,45 +3,358 @@
3
3
  * Header component for CLI applications
4
4
  * Displays ASCII art, title, description, version and URL
5
5
  */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
6
9
  Object.defineProperty(exports, "__esModule", { value: true });
7
10
  exports.renderHeader = renderHeader;
8
11
  const colors_js_1 = require("../../core/colors.js");
9
12
  const terminal_js_1 = require("../../core/terminal.js");
13
+ const figlet_1 = __importDefault(require("figlet"));
14
+ function visibleLength(value) {
15
+ return value.replace(/\x1b\[[0-9;]*m/g, '').length;
16
+ }
17
+ function padToVisibleWidth(value, width) {
18
+ const current = visibleLength(value);
19
+ if (current >= width) {
20
+ return value;
21
+ }
22
+ return value + ' '.repeat(width - current);
23
+ }
24
+ function parseBackgroundAnsiToRgb(value) {
25
+ const trueColor = /^\x1b\[48;2;(\d{1,3});(\d{1,3});(\d{1,3})m$/.exec(value);
26
+ if (trueColor) {
27
+ return {
28
+ r: Math.max(0, Math.min(255, Number.parseInt(trueColor[1], 10))),
29
+ g: Math.max(0, Math.min(255, Number.parseInt(trueColor[2], 10))),
30
+ b: Math.max(0, Math.min(255, Number.parseInt(trueColor[3], 10)))
31
+ };
32
+ }
33
+ const ansi256 = /^\x1b\[48;5;(\d{1,3})m$/.exec(value);
34
+ if (ansi256) {
35
+ const code = Math.max(0, Math.min(255, Number.parseInt(ansi256[1], 10)));
36
+ if (code >= 16 && code <= 231) {
37
+ const n = code - 16;
38
+ const r = Math.floor(n / 36);
39
+ const g = Math.floor((n % 36) / 6);
40
+ const b = n % 6;
41
+ const steps = [0, 95, 135, 175, 215, 255];
42
+ return { r: steps[r], g: steps[g], b: steps[b] };
43
+ }
44
+ if (code >= 232) {
45
+ const gray = 8 + (code - 232) * 10;
46
+ return { r: gray, g: gray, b: gray };
47
+ }
48
+ const basicMap = [
49
+ { r: 0, g: 0, b: 0 },
50
+ { r: 170, g: 0, b: 0 },
51
+ { r: 0, g: 170, b: 0 },
52
+ { r: 170, g: 85, b: 0 },
53
+ { r: 0, g: 0, b: 170 },
54
+ { r: 170, g: 0, b: 170 },
55
+ { r: 0, g: 170, b: 170 },
56
+ { r: 170, g: 170, b: 170 },
57
+ { r: 85, g: 85, b: 85 },
58
+ { r: 255, g: 85, b: 85 },
59
+ { r: 85, g: 255, b: 85 },
60
+ { r: 255, g: 255, b: 85 },
61
+ { r: 85, g: 85, b: 255 },
62
+ { r: 255, g: 85, b: 255 },
63
+ { r: 85, g: 255, b: 255 },
64
+ { r: 255, g: 255, b: 255 }
65
+ ];
66
+ return basicMap[code] ?? null;
67
+ }
68
+ return null;
69
+ }
70
+ function isTrueColorBackground(value) {
71
+ return /^\x1b\[48;2;\d{1,3};\d{1,3};\d{1,3}m$/.test(value);
72
+ }
73
+ function supportsTrueColorTerminal() {
74
+ const colorterm = (process.env.COLORTERM ?? '').toLowerCase();
75
+ if (colorterm.includes('truecolor') || colorterm.includes('24bit')) {
76
+ return true;
77
+ }
78
+ const term = (process.env.TERM ?? '').toLowerCase();
79
+ if (term.includes('direct')) {
80
+ return true;
81
+ }
82
+ const program = (process.env.TERM_PROGRAM ?? '').toLowerCase();
83
+ if (program.includes('ghostty') || program.includes('wezterm') || program.includes('iterm') || program.includes('vscode')) {
84
+ return true;
85
+ }
86
+ return false;
87
+ }
88
+ function applyBackgroundGradient(content, startColor, endColor) {
89
+ // Compatibility fallback:
90
+ // only use per-character background gradients on truecolor terminals.
91
+ if (!isTrueColorBackground(startColor) || !isTrueColorBackground(endColor) || !supportsTrueColorTerminal()) {
92
+ return `${startColor}${content}${colors_js_1.colors.reset}`;
93
+ }
94
+ const start = parseBackgroundAnsiToRgb(startColor);
95
+ const end = parseBackgroundAnsiToRgb(endColor);
96
+ if (!start || !end) {
97
+ return `${startColor}${content}${colors_js_1.colors.reset}`;
98
+ }
99
+ const total = Math.max(1, visibleLength(content));
100
+ let out = '';
101
+ let visibleIndex = 0;
102
+ for (let i = 0; i < content.length; i += 1) {
103
+ const ch = content[i] ?? '';
104
+ if (ch === '\x1b') {
105
+ const endIdx = content.indexOf('m', i);
106
+ if (endIdx >= 0) {
107
+ out += content.slice(i, endIdx + 1);
108
+ i = endIdx;
109
+ continue;
110
+ }
111
+ }
112
+ const t = total <= 1 ? 0 : visibleIndex / (total - 1);
113
+ const r = Math.round(start.r + (end.r - start.r) * t);
114
+ const g = Math.round(start.g + (end.g - start.g) * t);
115
+ const b = Math.round(start.b + (end.b - start.b) * t);
116
+ out += `\x1b[48;2;${String(r)};${String(g)};${String(b)}m${ch}`;
117
+ visibleIndex += 1;
118
+ }
119
+ return `${out}${colors_js_1.colors.reset}`;
120
+ }
121
+ const figletCache = new Map();
122
+ function inkCount(line) {
123
+ let count = 0;
124
+ for (const ch of line) {
125
+ if (ch !== ' ' && ch !== '\t') {
126
+ count += 1;
127
+ }
128
+ }
129
+ return count;
130
+ }
131
+ function trimFigletTailNoise(lines) {
132
+ const out = [...lines];
133
+ while (out.length > 1) {
134
+ const last = out[out.length - 1] ?? '';
135
+ const prev = out[out.length - 2] ?? '';
136
+ const lastInk = inkCount(last);
137
+ const prevInk = inkCount(prev);
138
+ // Remove very small dangling tail rows (common in Bloody-like fonts).
139
+ if (lastInk <= 1 && prevInk >= 4) {
140
+ out.pop();
141
+ continue;
142
+ }
143
+ break;
144
+ }
145
+ return out;
146
+ }
147
+ function resolveFigletFont(font, size = 'medium') {
148
+ if (font && font.trim().length > 0) {
149
+ return font;
150
+ }
151
+ if (size === 'small') {
152
+ return 'Small';
153
+ }
154
+ if (size === 'large') {
155
+ return 'Big';
156
+ }
157
+ return 'Standard';
158
+ }
159
+ function resolveFigletLines(text, font) {
160
+ const cacheKey = `${font}\u0000${text}`;
161
+ const cached = figletCache.get(cacheKey);
162
+ if (cached) {
163
+ return cached;
164
+ }
165
+ try {
166
+ const rendered = figlet_1.default.textSync(text, { font });
167
+ const lines = rendered.split('\n');
168
+ while (lines.length > 0 && lines[lines.length - 1].trim().length === 0) {
169
+ lines.pop();
170
+ }
171
+ const cleaned = trimFigletTailNoise(lines);
172
+ figletCache.set(cacheKey, cleaned);
173
+ return cleaned;
174
+ }
175
+ catch {
176
+ const fallback = figlet_1.default.textSync(text, { font: 'Standard' }).split('\n');
177
+ while (fallback.length > 0 && fallback[fallback.length - 1].trim().length === 0) {
178
+ fallback.pop();
179
+ }
180
+ const cleanedFallback = trimFigletTailNoise(fallback);
181
+ figletCache.set(cacheKey, cleanedFallback);
182
+ return cleanedFallback;
183
+ }
184
+ }
185
+ function scaleLineX(line, outCols) {
186
+ const srcCols = line.length;
187
+ if (srcCols === 0 || outCols <= 0) {
188
+ return '';
189
+ }
190
+ if (outCols === srcCols) {
191
+ return line;
192
+ }
193
+ let scaled = '';
194
+ for (let c = 0; c < outCols; c += 1) {
195
+ if (outCols === 1 || srcCols === 1) {
196
+ scaled += line[0] ?? ' ';
197
+ continue;
198
+ }
199
+ const pos = (c * (srcCols - 1)) / (outCols - 1);
200
+ const srcCol = Math.max(0, Math.min(srcCols - 1, Math.round(pos)));
201
+ scaled += line[srcCol] ?? ' ';
202
+ }
203
+ return scaled;
204
+ }
205
+ function scaleAsciiLines(lines, scaleX, scaleY) {
206
+ const sx = Number.isFinite(scaleX) ? Math.max(0.4, Math.min(4, scaleX)) : 1;
207
+ const sy = Number.isFinite(scaleY) ? Math.max(0.4, Math.min(4, scaleY)) : sx;
208
+ if (Math.abs(sx - 1) < 0.001 && Math.abs(sy - 1) < 0.001) {
209
+ return lines;
210
+ }
211
+ const srcRows = lines.length;
212
+ if (srcRows === 0) {
213
+ return lines;
214
+ }
215
+ const outRows = Math.max(1, Math.round(srcRows * sy));
216
+ const out = [];
217
+ const sampleIndex = (outIndex, outCount, srcCount) => {
218
+ if (srcCount <= 1 || outCount <= 1) {
219
+ return 0;
220
+ }
221
+ const ratio = outIndex / (outCount - 1);
222
+ return Math.max(0, Math.min(srcCount - 1, Math.round(ratio * (srcCount - 1))));
223
+ };
224
+ for (let r = 0; r < outRows; r += 1) {
225
+ const srcRow = sampleIndex(r, outRows, srcRows);
226
+ const srcLine = lines[srcRow] ?? '';
227
+ const outCols = Math.max(1, Math.round(srcLine.length * sx));
228
+ out.push(scaleLineX(srcLine, outCols).replace(/\s+$/g, ''));
229
+ }
230
+ return out;
231
+ }
232
+ function resolveFigletScale(scale, _explicitFont, _size) {
233
+ if (typeof scale === 'number' && Number.isFinite(scale)) {
234
+ return Math.max(0.4, Math.min(4, scale));
235
+ }
236
+ return 1;
237
+ }
238
+ function resolveFigletScaleY(scaleY, scaleX, explicitFont) {
239
+ if (typeof scaleY === 'number' && Number.isFinite(scaleY)) {
240
+ return Math.max(0.4, Math.min(4, scaleY));
241
+ }
242
+ const normalizedFont = (explicitFont || '').trim().toLowerCase();
243
+ // Keep Pagga crisp: no default Y stretch (its native height is 3 rows).
244
+ if (normalizedFont === 'pagga' && scaleX > 1) {
245
+ return 1;
246
+ }
247
+ return scaleX;
248
+ }
10
249
  /**
11
250
  * Render a boxed header with ASCII art, title, and description
12
251
  * @param config - Header configuration
13
252
  */
14
253
  function renderHeader(config) {
15
- const { asciiArt = [], title = '', description = '', version, url, menuTitle, boxWidth = 60, color = colors_js_1.uiColors.border } = config;
254
+ const { asciiArt = [], figletText, figletFont, figletSize = 'medium', figletScale, figletScaleY, title = '', titleColor, titleGradientStart, titleGradientEnd, description = '', descriptionColor, descriptionGradientStart, descriptionGradientEnd, version, url, menuTitle, boxWidth = 60, showBoxBorder = true, fillBox = false, fillBoxColor = colors_js_1.colors.bgBlack, fillBoxGradientStart, fillBoxGradientEnd, color = colors_js_1.uiColors.border, asciiArtColor, asciiArtGradientStart, asciiArtGradientEnd } = config;
255
+ const resolvedFigletFont = resolveFigletFont(figletFont, figletSize);
256
+ const effectiveFigletScale = resolveFigletScale(figletScale, figletFont, figletSize);
257
+ const effectiveScaleY = resolveFigletScaleY(figletScaleY, effectiveFigletScale, resolvedFigletFont);
258
+ const resolvedAsciiArt = figletText
259
+ ? scaleAsciiLines(resolveFigletLines(figletText, resolvedFigletFont), effectiveFigletScale, effectiveScaleY)
260
+ : asciiArt;
261
+ const requestedInnerWidth = Math.max(10, boxWidth - 2);
262
+ const contentLengths = [];
263
+ for (const line of resolvedAsciiArt) {
264
+ contentLengths.push(visibleLength(` ${line}`));
265
+ }
266
+ if (title) {
267
+ contentLengths.push(visibleLength(` ${title}`));
268
+ }
269
+ if (description) {
270
+ contentLengths.push(visibleLength(` ${description}`));
271
+ }
272
+ const innerWidth = Math.max(requestedInnerWidth, ...contentLengths, 10);
16
273
  const boldColor = `${color}${colors_js_1.colors.bold}`;
274
+ const applyBoxFill = (content) => {
275
+ if (!fillBox) {
276
+ return content;
277
+ }
278
+ if (fillBoxGradientStart && fillBoxGradientEnd) {
279
+ return applyBackgroundGradient(content, fillBoxGradientStart, fillBoxGradientEnd);
280
+ }
281
+ const replayed = content.replace(/\x1b\[0m/g, `${colors_js_1.colors.reset}${fillBoxColor}`);
282
+ return `${fillBoxColor}${replayed}${colors_js_1.colors.reset}`;
283
+ };
284
+ const writeSpacer = () => {
285
+ if (showBoxBorder) {
286
+ const spacerContent = fillBox ? applyBoxFill(' '.repeat(innerWidth)) : ' '.repeat(innerWidth);
287
+ (0, terminal_js_1.writeLine)(`${boldColor}║${spacerContent}${boldColor}║${colors_js_1.colors.reset}`);
288
+ }
289
+ else if (fillBox) {
290
+ (0, terminal_js_1.writeLine)(` ${applyBoxFill(' '.repeat(innerWidth))}`);
291
+ }
292
+ else {
293
+ (0, terminal_js_1.writeLine)('');
294
+ }
295
+ };
296
+ const writeContentLine = (content, tint) => {
297
+ const normalized = showBoxBorder || fillBox ? padToVisibleWidth(content, innerWidth) : content;
298
+ if (showBoxBorder) {
299
+ const inner = tint ? `${tint}${normalized}${colors_js_1.colors.reset}` : `${colors_js_1.colors.reset}${normalized}`;
300
+ const body = fillBox ? applyBoxFill(inner) : inner;
301
+ if (tint) {
302
+ (0, terminal_js_1.writeLine)(`${boldColor}║${body}${boldColor}║${colors_js_1.colors.reset}`);
303
+ }
304
+ else {
305
+ (0, terminal_js_1.writeLine)(`${boldColor}║${body}${boldColor}║${colors_js_1.colors.reset}`);
306
+ }
307
+ return;
308
+ }
309
+ const plainLine = tint ? `${tint}${normalized}${colors_js_1.colors.reset}` : normalized;
310
+ if (fillBox) {
311
+ (0, terminal_js_1.writeLine)(` ${applyBoxFill(plainLine)}`);
312
+ return;
313
+ }
314
+ (0, terminal_js_1.writeLine)(plainLine);
315
+ };
17
316
  // Top border
18
317
  (0, terminal_js_1.writeLine)('');
19
- (0, terminal_js_1.writeLine)(`${boldColor}╔${'═'.repeat(boxWidth - 2)}╗${colors_js_1.colors.reset}`);
20
- // Empty line
21
- (0, terminal_js_1.writeLine)(`${boldColor}║${' '.repeat(boxWidth - 2)}║${colors_js_1.colors.reset}`);
318
+ if (showBoxBorder) {
319
+ (0, terminal_js_1.writeLine)(`${boldColor}╔${'═'.repeat(innerWidth)}╗${colors_js_1.colors.reset}`);
320
+ }
321
+ writeSpacer();
22
322
  // ASCII art (left-aligned with 2 spaces padding)
23
- if (asciiArt.length > 0) {
24
- asciiArt.forEach(line => {
25
- const paddedLine = ` ${line}`.padEnd(boxWidth - 2, ' ');
26
- (0, terminal_js_1.writeLine)(`${boldColor}║${paddedLine}║${colors_js_1.colors.reset}`);
323
+ if (resolvedAsciiArt.length > 0) {
324
+ resolvedAsciiArt.forEach(line => {
325
+ const paddedLine = showBoxBorder ? ` ${line}`.padEnd(innerWidth, ' ') : ` ${line}`;
326
+ const artLine = asciiArtGradientStart && asciiArtGradientEnd
327
+ ? `${(0, colors_js_1.applyGradient)(paddedLine, asciiArtGradientStart, asciiArtGradientEnd)}`
328
+ : `${(asciiArtColor || color)}${paddedLine}${colors_js_1.colors.reset}`;
329
+ writeContentLine(artLine);
27
330
  });
28
- (0, terminal_js_1.writeLine)(`${boldColor}║${' '.repeat(boxWidth - 2)}║${colors_js_1.colors.reset}`);
331
+ writeSpacer();
29
332
  }
30
- // Title (left-aligned with 2 spaces padding, black text)
333
+ // Title (left-aligned with 2 spaces padding)
31
334
  if (title) {
32
- const paddedTitle = ` ${title}`.padEnd(boxWidth - 2, ' ');
33
- (0, terminal_js_1.writeLine)(`${boldColor}║${colors_js_1.colors.reset}${paddedTitle}${boldColor}║${colors_js_1.colors.reset}`);
34
- (0, terminal_js_1.writeLine)(`${boldColor}║${' '.repeat(boxWidth - 2)}║${colors_js_1.colors.reset}`);
335
+ const paddedTitle = showBoxBorder ? ` ${title}`.padEnd(innerWidth, ' ') : ` ${title}`;
336
+ const titleLine = titleGradientStart && titleGradientEnd
337
+ ? (0, colors_js_1.applyGradient)(paddedTitle, titleGradientStart, titleGradientEnd)
338
+ : paddedTitle;
339
+ writeContentLine(titleLine, titleGradientStart && titleGradientEnd ? undefined : titleColor);
340
+ writeSpacer();
35
341
  }
36
- // Description (left-aligned with 2 spaces padding, gray text)
342
+ // Description (left-aligned with 2 spaces padding)
37
343
  if (description) {
38
344
  const textContent = ` ${description}`;
39
- const paddedText = textContent.padEnd(boxWidth - 2, ' ');
40
- (0, terminal_js_1.writeLine)(`${boldColor}║${colors_js_1.uiColors.textSecondary}${paddedText}${colors_js_1.colors.reset}${boldColor}║${colors_js_1.colors.reset}`);
41
- (0, terminal_js_1.writeLine)(`${boldColor}║${' '.repeat(boxWidth - 2)}║${colors_js_1.colors.reset}`);
345
+ const paddedText = showBoxBorder ? textContent.padEnd(innerWidth, ' ') : textContent;
346
+ const descLine = descriptionGradientStart && descriptionGradientEnd
347
+ ? (0, colors_js_1.applyGradient)(paddedText, descriptionGradientStart, descriptionGradientEnd)
348
+ : paddedText;
349
+ writeContentLine(descLine, descriptionGradientStart && descriptionGradientEnd
350
+ ? undefined
351
+ : (descriptionColor || colors_js_1.uiColors.textSecondary));
352
+ writeSpacer();
42
353
  }
43
354
  // Bottom border
44
- (0, terminal_js_1.writeLine)(`${boldColor}╚${'═'.repeat(boxWidth - 2)}╝${colors_js_1.colors.reset}`);
355
+ if (showBoxBorder) {
356
+ (0, terminal_js_1.writeLine)(`${boldColor}╚${'═'.repeat(innerWidth)}╝${colors_js_1.colors.reset}`);
357
+ }
45
358
  // Blank line after box
46
359
  (0, terminal_js_1.writeLine)('');
47
360
  // Version and URL (outside the box, with colors)
@@ -10,6 +10,7 @@ import { AsciiHeaderConfig } from '../../types/display.types.js';
10
10
  * @param color - Optional color (default: cyan)
11
11
  */
12
12
  export declare function renderSimpleHeader(text: string, color?: string): void;
13
+ export declare function renderSimpleHeaderWithSpacing(text: string, color?: string, spacingAfter?: number): void;
13
14
  /**
14
15
  * Render a section header with double-line borders
15
16
  * Format:
@@ -5,6 +5,7 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.renderSimpleHeader = renderSimpleHeader;
8
+ exports.renderSimpleHeaderWithSpacing = renderSimpleHeaderWithSpacing;
8
9
  exports.renderSectionHeader = renderSectionHeader;
9
10
  exports.renderAsciiHeader = renderAsciiHeader;
10
11
  exports.createSimpleHeader = createSimpleHeader;
@@ -21,10 +22,19 @@ const terminal_js_2 = require("../../core/terminal.js");
21
22
  */
22
23
  function renderSimpleHeader(text, color) {
23
24
  const headerColor = color || colors_js_1.uiColors.primary;
24
- const line = `=== ${text} ===`;
25
+ const line = ` === ${text} ===`;
25
26
  (0, terminal_js_1.writeLine)(`${headerColor}${line}${colors_js_1.colors.reset}`);
26
27
  (0, terminal_js_1.writeLine)('');
27
28
  }
29
+ function renderSimpleHeaderWithSpacing(text, color, spacingAfter = 1) {
30
+ const headerColor = color || colors_js_1.uiColors.primary;
31
+ const line = ` === ${text} ===`;
32
+ (0, terminal_js_1.writeLine)(`${headerColor}${line}${colors_js_1.colors.reset}`);
33
+ const spacerLines = Math.max(0, Math.floor(spacingAfter));
34
+ for (let i = 0; i < spacerLines; i += 1) {
35
+ (0, terminal_js_1.writeLine)('');
36
+ }
37
+ }
28
38
  /**
29
39
  * Render a section header with double-line borders
30
40
  * Format:
@@ -53,22 +63,22 @@ function renderAsciiHeader(config) {
53
63
  const termWidth = (0, terminal_js_2.getTerminalWidth)();
54
64
  const border = borderChar.repeat(termWidth);
55
65
  // Top border
56
- (0, terminal_js_1.writeLine)(colors_js_1.colors.cyan + border + colors_js_1.colors.reset);
66
+ (0, terminal_js_1.writeLine)(colors_js_1.uiColors.border + border + colors_js_1.colors.reset);
57
67
  (0, terminal_js_1.writeLine)('');
58
68
  // ASCII art (centered)
59
69
  const artLines = asciiArt.split('\n');
60
70
  artLines.forEach(line => {
61
- (0, terminal_js_1.writeLine)(colors_js_1.colors.cyan + line + colors_js_1.colors.reset);
71
+ (0, terminal_js_1.writeLine)(colors_js_1.uiColors.primary + line + colors_js_1.colors.reset);
62
72
  });
63
73
  // Subtitle if provided
64
74
  if (subtitle) {
65
75
  (0, terminal_js_1.writeLine)('');
66
76
  const padding = Math.floor((termWidth - subtitle.length) / 2);
67
- (0, terminal_js_1.writeLine)(' '.repeat(padding) + colors_js_1.colors.brightCyan + subtitle + colors_js_1.colors.reset);
77
+ (0, terminal_js_1.writeLine)(' '.repeat(padding) + colors_js_1.uiColors.accent + subtitle + colors_js_1.colors.reset);
68
78
  }
69
79
  (0, terminal_js_1.writeLine)('');
70
80
  // Bottom border
71
- (0, terminal_js_1.writeLine)(colors_js_1.colors.cyan + border + colors_js_1.colors.reset);
81
+ (0, terminal_js_1.writeLine)(colors_js_1.uiColors.border + border + colors_js_1.colors.reset);
72
82
  // Footer info (version and URL)
73
83
  if (version || url) {
74
84
  const footerParts = [];
@@ -3,7 +3,7 @@
3
3
  * Exports all display component functions
4
4
  */
5
5
  export { renderSimpleHeader, renderSectionHeader, renderAsciiHeader, createSimpleHeader, createSectionHeader, createAsciiHeader } from './headers.js';
6
- export { renderProgressIndicator, renderStageHeader, renderStageSeparator, createProgressIndicator, createStageHeader, createStageSeparator } from './progress.js';
6
+ export { renderProgressIndicator, renderStageHeader, renderStageSeparator, createProgressIndicator, createStageHeader, createStageSeparator, renderProgressCheckmark, createProgressCheckmark } from './progress.js';
7
7
  export { renderMessage, showSuccess, showError, showWarning, showInfo, showQuestion, createMessage } from './messages.js';
8
8
  export { renderSummaryTable, createSummaryTable, createSimpleSummary } from './summary.js';
9
9
  export { renderHeader, type HeaderConfig } from './header.js';
@@ -4,7 +4,7 @@
4
4
  * Exports all display component functions
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.createNumberedList = exports.createBulletList = exports.createList = exports.renderList = exports.createTable = exports.renderTable = exports.HintTypes = exports.generateInputHints = exports.generateMenuHints = exports.createHints = exports.renderHintsComponent = exports.renderHeader = exports.createSimpleSummary = exports.createSummaryTable = exports.renderSummaryTable = exports.createMessage = exports.showQuestion = exports.showInfo = exports.showWarning = exports.showError = exports.showSuccess = exports.renderMessage = exports.createStageSeparator = exports.createStageHeader = exports.createProgressIndicator = exports.renderStageSeparator = exports.renderStageHeader = exports.renderProgressIndicator = exports.createAsciiHeader = exports.createSectionHeader = exports.createSimpleHeader = exports.renderAsciiHeader = exports.renderSectionHeader = exports.renderSimpleHeader = void 0;
7
+ exports.createNumberedList = exports.createBulletList = exports.createList = exports.renderList = exports.createTable = exports.renderTable = exports.HintTypes = exports.generateInputHints = exports.generateMenuHints = exports.createHints = exports.renderHintsComponent = exports.renderHeader = exports.createSimpleSummary = exports.createSummaryTable = exports.renderSummaryTable = exports.createMessage = exports.showQuestion = exports.showInfo = exports.showWarning = exports.showError = exports.showSuccess = exports.renderMessage = exports.createProgressCheckmark = exports.renderProgressCheckmark = exports.createStageSeparator = exports.createStageHeader = exports.createProgressIndicator = exports.renderStageSeparator = exports.renderStageHeader = exports.renderProgressIndicator = exports.createAsciiHeader = exports.createSectionHeader = exports.createSimpleHeader = exports.renderAsciiHeader = exports.renderSectionHeader = exports.renderSimpleHeader = void 0;
8
8
  var headers_js_1 = require("./headers.js");
9
9
  Object.defineProperty(exports, "renderSimpleHeader", { enumerable: true, get: function () { return headers_js_1.renderSimpleHeader; } });
10
10
  Object.defineProperty(exports, "renderSectionHeader", { enumerable: true, get: function () { return headers_js_1.renderSectionHeader; } });
@@ -19,6 +19,8 @@ Object.defineProperty(exports, "renderStageSeparator", { enumerable: true, get:
19
19
  Object.defineProperty(exports, "createProgressIndicator", { enumerable: true, get: function () { return progress_js_1.createProgressIndicator; } });
20
20
  Object.defineProperty(exports, "createStageHeader", { enumerable: true, get: function () { return progress_js_1.createStageHeader; } });
21
21
  Object.defineProperty(exports, "createStageSeparator", { enumerable: true, get: function () { return progress_js_1.createStageSeparator; } });
22
+ Object.defineProperty(exports, "renderProgressCheckmark", { enumerable: true, get: function () { return progress_js_1.renderProgressCheckmark; } });
23
+ Object.defineProperty(exports, "createProgressCheckmark", { enumerable: true, get: function () { return progress_js_1.createProgressCheckmark; } });
22
24
  var messages_js_1 = require("./messages.js");
23
25
  Object.defineProperty(exports, "renderMessage", { enumerable: true, get: function () { return messages_js_1.renderMessage; } });
24
26
  Object.defineProperty(exports, "showSuccess", { enumerable: true, get: function () { return messages_js_1.showSuccess; } });
@@ -24,23 +24,23 @@ function renderMessage(config) {
24
24
  switch (type) {
25
25
  case 'success':
26
26
  icon = '✓';
27
- color = colors_js_1.colors.green;
27
+ color = colors_js_1.uiColors.success;
28
28
  break;
29
29
  case 'error':
30
30
  icon = '✗';
31
- color = colors_js_1.colors.red;
31
+ color = colors_js_1.uiColors.error;
32
32
  break;
33
33
  case 'warning':
34
34
  icon = '⚠';
35
- color = colors_js_1.colors.yellow;
35
+ color = colors_js_1.uiColors.warning;
36
36
  break;
37
37
  case 'info':
38
38
  icon = 'ℹ';
39
- color = colors_js_1.colors.blue;
39
+ color = colors_js_1.uiColors.info;
40
40
  break;
41
41
  case 'question':
42
42
  icon = '?';
43
- color = colors_js_1.colors.yellow;
43
+ color = colors_js_1.uiColors.warning;
44
44
  break;
45
45
  }
46
46
  (0, terminal_js_1.writeLine)(`${color}${icon}${colors_js_1.colors.reset} ${message}`);
@@ -39,3 +39,20 @@ export declare function createStageHeader(stageName: string, stepNumber: number)
39
39
  * @param width - Optional width
40
40
  */
41
41
  export declare function createStageSeparator(char?: string, width?: number): void;
42
+ /**
43
+ * Single-line completed progress marker.
44
+ * Use this for execution flows that print one completed step at a time.
45
+ */
46
+ export declare function renderProgressCheckmark(step: string, options?: {
47
+ icon?: string;
48
+ indent?: string;
49
+ color?: string;
50
+ }): void;
51
+ /**
52
+ * Alias factory for renderProgressCheckmark
53
+ */
54
+ export declare function createProgressCheckmark(step: string, options?: {
55
+ icon?: string;
56
+ indent?: string;
57
+ color?: string;
58
+ }): void;
@@ -10,6 +10,8 @@ exports.renderStageSeparator = renderStageSeparator;
10
10
  exports.createProgressIndicator = createProgressIndicator;
11
11
  exports.createStageHeader = createStageHeader;
12
12
  exports.createStageSeparator = createStageSeparator;
13
+ exports.renderProgressCheckmark = renderProgressCheckmark;
14
+ exports.createProgressCheckmark = createProgressCheckmark;
13
15
  const terminal_js_1 = require("../../core/terminal.js");
14
16
  const colors_js_1 = require("../../core/colors.js");
15
17
  const terminal_js_2 = require("../../core/terminal.js");
@@ -80,3 +82,19 @@ function createStageHeader(stageName, stepNumber) {
80
82
  function createStageSeparator(char, width) {
81
83
  renderStageSeparator(char, width);
82
84
  }
85
+ /**
86
+ * Single-line completed progress marker.
87
+ * Use this for execution flows that print one completed step at a time.
88
+ */
89
+ function renderProgressCheckmark(step, options) {
90
+ const icon = options?.icon ?? '✓';
91
+ const indent = options?.indent ?? ' ';
92
+ const color = options?.color ?? colors_js_1.uiColors.primary;
93
+ (0, terminal_js_1.writeLine)(`${indent}${color}${icon}${colors_js_1.colors.reset} ${step}`);
94
+ }
95
+ /**
96
+ * Alias factory for renderProgressCheckmark
97
+ */
98
+ function createProgressCheckmark(step, options) {
99
+ renderProgressCheckmark(step, options);
100
+ }