cli-menu-kit 0.1.26 → 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.
- package/dist/api.d.ts +23 -5
- package/dist/api.js +16 -4
- package/dist/component-factories.d.ts +59 -0
- package/dist/component-factories.js +141 -0
- package/dist/components/display/header-v2.d.ts +13 -0
- package/dist/components/display/header-v2.js +43 -0
- package/dist/components/display/header.d.ts +40 -0
- package/dist/components/display/header.js +331 -18
- package/dist/components/display/headers.d.ts +1 -0
- package/dist/components/display/headers.js +15 -5
- package/dist/components/display/hints-v2.d.ts +10 -0
- package/dist/components/display/hints-v2.js +34 -0
- package/dist/components/display/hints.d.ts +56 -0
- package/dist/components/display/hints.js +81 -0
- package/dist/components/display/index.d.ts +4 -1
- package/dist/components/display/index.js +17 -1
- package/dist/components/display/input-prompt.d.ts +35 -0
- package/dist/components/display/input-prompt.js +36 -0
- package/dist/components/display/list.d.ts +49 -0
- package/dist/components/display/list.js +86 -0
- package/dist/components/display/messages.js +5 -5
- package/dist/components/display/progress.d.ts +17 -0
- package/dist/components/display/progress.js +18 -0
- package/dist/components/display/summary.js +72 -10
- package/dist/components/display/table.d.ts +44 -0
- package/dist/components/display/table.js +108 -0
- package/dist/components/inputs/language-input.js +8 -5
- package/dist/components/inputs/number-input.js +19 -14
- package/dist/components/inputs/text-input.js +50 -13
- package/dist/components/menus/boolean-menu.js +34 -20
- package/dist/components/menus/checkbox-menu.d.ts +2 -1
- package/dist/components/menus/checkbox-menu.js +35 -61
- package/dist/components/menus/checkbox-table-menu.d.ts +12 -0
- package/dist/components/menus/checkbox-table-menu.js +398 -0
- package/dist/components/menus/index.d.ts +1 -0
- package/dist/components/menus/index.js +3 -1
- package/dist/components/menus/radio-menu-split.d.ts +34 -0
- package/dist/components/menus/radio-menu-split.js +258 -0
- package/dist/components/menus/radio-menu-v2.d.ts +11 -0
- package/dist/components/menus/radio-menu-v2.js +150 -0
- package/dist/components/menus/radio-menu.d.ts +2 -1
- package/dist/components/menus/radio-menu.js +100 -134
- package/dist/components.js +3 -3
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.js +21 -0
- package/dist/config/language-config.d.ts +73 -0
- package/dist/config/language-config.js +157 -0
- package/dist/config/user-config.d.ts +83 -0
- package/dist/config/user-config.js +185 -0
- package/dist/core/colors.d.ts +24 -18
- package/dist/core/colors.js +74 -7
- package/dist/core/hint-manager.d.ts +29 -0
- package/dist/core/hint-manager.js +65 -0
- package/dist/core/renderer.d.ts +2 -1
- package/dist/core/renderer.js +46 -22
- package/dist/core/screen-manager.d.ts +54 -0
- package/dist/core/screen-manager.js +119 -0
- package/dist/core/state-manager.d.ts +27 -0
- package/dist/core/state-manager.js +56 -0
- package/dist/core/terminal.d.ts +17 -1
- package/dist/core/terminal.js +124 -4
- package/dist/core/virtual-scroll.d.ts +65 -0
- package/dist/core/virtual-scroll.js +120 -0
- package/dist/features/commands.js +23 -22
- package/dist/i18n/languages/en.js +4 -1
- package/dist/i18n/languages/zh.js +4 -1
- package/dist/i18n/registry.d.ts +4 -3
- package/dist/i18n/registry.js +12 -4
- package/dist/i18n/types.d.ts +3 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.js +49 -4
- package/dist/layout.d.ts +67 -0
- package/dist/layout.js +86 -0
- package/dist/page-layout.d.ts +123 -0
- package/dist/page-layout.js +195 -0
- package/dist/types/input.types.d.ts +8 -0
- package/dist/types/menu.types.d.ts +61 -5
- package/package.json +4 -1
|
@@ -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
|
-
(
|
|
20
|
-
|
|
21
|
-
|
|
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 (
|
|
24
|
-
|
|
25
|
-
const paddedLine = ` ${line}`.padEnd(
|
|
26
|
-
|
|
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
|
-
(
|
|
331
|
+
writeSpacer();
|
|
29
332
|
}
|
|
30
|
-
// Title (left-aligned with 2 spaces padding
|
|
333
|
+
// Title (left-aligned with 2 spaces padding)
|
|
31
334
|
if (title) {
|
|
32
|
-
const paddedTitle = ` ${title}`.padEnd(
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
342
|
+
// Description (left-aligned with 2 spaces padding)
|
|
37
343
|
if (description) {
|
|
38
344
|
const textContent = ` ${description}`;
|
|
39
|
-
const paddedText = textContent.padEnd(
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
(
|
|
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 =
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 = [];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hints Component - New Architecture Version
|
|
3
|
+
* Returns string arrays and listens to HintManager
|
|
4
|
+
*/
|
|
5
|
+
import { Component } from '../../layout.js';
|
|
6
|
+
export declare function createHintsComponentV2(hints: string[]): Component;
|
|
7
|
+
/**
|
|
8
|
+
* Create a dynamic hints component that listens to HintManager
|
|
9
|
+
*/
|
|
10
|
+
export declare function createDynamicHintsComponent(): Component;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hints Component - New Architecture Version
|
|
4
|
+
* Returns string arrays and listens to HintManager
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.createHintsComponentV2 = createHintsComponentV2;
|
|
8
|
+
exports.createDynamicHintsComponent = createDynamicHintsComponent;
|
|
9
|
+
const layout_js_1 = require("../../layout.js");
|
|
10
|
+
const colors_js_1 = require("../../core/colors.js");
|
|
11
|
+
function createHintsComponentV2(hints) {
|
|
12
|
+
return {
|
|
13
|
+
type: 'hints',
|
|
14
|
+
regionId: 'footerHints',
|
|
15
|
+
render: (rect) => {
|
|
16
|
+
// Join hints with separator
|
|
17
|
+
const hintsText = hints.join(' • ');
|
|
18
|
+
return [`${colors_js_1.colors.dim}${hintsText}${colors_js_1.colors.reset}`];
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a dynamic hints component that listens to HintManager
|
|
24
|
+
*/
|
|
25
|
+
function createDynamicHintsComponent() {
|
|
26
|
+
return {
|
|
27
|
+
type: 'hints',
|
|
28
|
+
regionId: 'footerHints',
|
|
29
|
+
render: (rect) => {
|
|
30
|
+
const currentHint = layout_js_1.hintManager.current();
|
|
31
|
+
return [currentHint ? `${colors_js_1.colors.dim}${currentHint}${colors_js_1.colors.reset}` : ''];
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hints Component
|
|
3
|
+
* Displays operation hints at the bottom of the page
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Hints configuration
|
|
7
|
+
*/
|
|
8
|
+
export interface HintsConfig {
|
|
9
|
+
hints: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Standard hint types
|
|
13
|
+
*/
|
|
14
|
+
export declare const HintTypes: {
|
|
15
|
+
/** Arrow keys navigation */
|
|
16
|
+
readonly ARROWS: () => string;
|
|
17
|
+
/** Number keys selection */
|
|
18
|
+
readonly NUMBERS: () => string;
|
|
19
|
+
/** Letter keys selection */
|
|
20
|
+
readonly LETTERS: () => string;
|
|
21
|
+
/** Space key toggle */
|
|
22
|
+
readonly SPACE: () => string;
|
|
23
|
+
/** Enter key confirm */
|
|
24
|
+
readonly ENTER: () => string;
|
|
25
|
+
/** Escape key cancel */
|
|
26
|
+
readonly ESC: () => string;
|
|
27
|
+
/** Select all */
|
|
28
|
+
readonly SELECT_ALL: () => string;
|
|
29
|
+
/** Invert selection */
|
|
30
|
+
readonly INVERT: () => string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Generate hints for menu interactions
|
|
34
|
+
*/
|
|
35
|
+
export declare function generateMenuHints(options: {
|
|
36
|
+
hasMultipleOptions?: boolean;
|
|
37
|
+
allowNumberKeys?: boolean;
|
|
38
|
+
allowLetterKeys?: boolean;
|
|
39
|
+
allowSelectAll?: boolean;
|
|
40
|
+
allowInvert?: boolean;
|
|
41
|
+
}): string[];
|
|
42
|
+
/**
|
|
43
|
+
* Generate hints for input interactions
|
|
44
|
+
*/
|
|
45
|
+
export declare function generateInputHints(): string[];
|
|
46
|
+
/**
|
|
47
|
+
* Render hints component
|
|
48
|
+
* @param config - Hints configuration
|
|
49
|
+
*/
|
|
50
|
+
export declare function renderHintsComponent(config: HintsConfig): void;
|
|
51
|
+
/**
|
|
52
|
+
* Create hints component (factory function)
|
|
53
|
+
* @param hints - Array of hint strings
|
|
54
|
+
* @returns Hints configuration
|
|
55
|
+
*/
|
|
56
|
+
export declare function createHints(hints: string[]): HintsConfig;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hints Component
|
|
4
|
+
* Displays operation hints at the bottom of the page
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.HintTypes = void 0;
|
|
8
|
+
exports.generateMenuHints = generateMenuHints;
|
|
9
|
+
exports.generateInputHints = generateInputHints;
|
|
10
|
+
exports.renderHintsComponent = renderHintsComponent;
|
|
11
|
+
exports.createHints = createHints;
|
|
12
|
+
const renderer_js_1 = require("../../core/renderer.js");
|
|
13
|
+
const registry_js_1 = require("../../i18n/registry.js");
|
|
14
|
+
/**
|
|
15
|
+
* Standard hint types
|
|
16
|
+
*/
|
|
17
|
+
exports.HintTypes = {
|
|
18
|
+
/** Arrow keys navigation */
|
|
19
|
+
ARROWS: () => (0, registry_js_1.t)('hints.arrows'),
|
|
20
|
+
/** Number keys selection */
|
|
21
|
+
NUMBERS: () => (0, registry_js_1.t)('hints.numbers'),
|
|
22
|
+
/** Letter keys selection */
|
|
23
|
+
LETTERS: () => (0, registry_js_1.t)('hints.letters'),
|
|
24
|
+
/** Space key toggle */
|
|
25
|
+
SPACE: () => (0, registry_js_1.t)('hints.space'),
|
|
26
|
+
/** Enter key confirm */
|
|
27
|
+
ENTER: () => (0, registry_js_1.t)('hints.enter'),
|
|
28
|
+
/** Escape key cancel */
|
|
29
|
+
ESC: () => 'Esc Cancel',
|
|
30
|
+
/** Select all */
|
|
31
|
+
SELECT_ALL: () => (0, registry_js_1.t)('hints.selectAll'),
|
|
32
|
+
/** Invert selection */
|
|
33
|
+
INVERT: () => (0, registry_js_1.t)('hints.invert')
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Generate hints for menu interactions
|
|
37
|
+
*/
|
|
38
|
+
function generateMenuHints(options) {
|
|
39
|
+
const hints = [];
|
|
40
|
+
if (options.hasMultipleOptions) {
|
|
41
|
+
hints.push(exports.HintTypes.ARROWS());
|
|
42
|
+
}
|
|
43
|
+
if (options.allowNumberKeys) {
|
|
44
|
+
hints.push(exports.HintTypes.NUMBERS());
|
|
45
|
+
}
|
|
46
|
+
if (options.allowLetterKeys) {
|
|
47
|
+
hints.push(exports.HintTypes.LETTERS());
|
|
48
|
+
}
|
|
49
|
+
if (options.allowSelectAll) {
|
|
50
|
+
hints.push(exports.HintTypes.SELECT_ALL());
|
|
51
|
+
}
|
|
52
|
+
if (options.allowInvert) {
|
|
53
|
+
hints.push(exports.HintTypes.INVERT());
|
|
54
|
+
}
|
|
55
|
+
hints.push(exports.HintTypes.ENTER());
|
|
56
|
+
return hints;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Generate hints for input interactions
|
|
60
|
+
*/
|
|
61
|
+
function generateInputHints() {
|
|
62
|
+
return [exports.HintTypes.ENTER(), exports.HintTypes.ESC()];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Render hints component
|
|
66
|
+
* @param config - Hints configuration
|
|
67
|
+
*/
|
|
68
|
+
function renderHintsComponent(config) {
|
|
69
|
+
if (!config.hints || config.hints.length === 0) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
(0, renderer_js_1.renderHints)(config.hints);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Create hints component (factory function)
|
|
76
|
+
* @param hints - Array of hint strings
|
|
77
|
+
* @returns Hints configuration
|
|
78
|
+
*/
|
|
79
|
+
function createHints(hints) {
|
|
80
|
+
return { hints };
|
|
81
|
+
}
|
|
@@ -3,7 +3,10 @@
|
|
|
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';
|
|
10
|
+
export { renderHintsComponent, createHints, generateMenuHints, generateInputHints, HintTypes, type HintsConfig } from './hints.js';
|
|
11
|
+
export { renderTable, createTable, type TableConfig, type TableColumn } from './table.js';
|
|
12
|
+
export { renderList, createList, createBulletList, createNumberedList, type ListConfig, type ListItem } from './list.js';
|