cli-menu-kit 0.1.22 → 0.1.26
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.
|
@@ -11,7 +11,83 @@ const terminal_js_1 = require("../../core/terminal.js");
|
|
|
11
11
|
const colors_js_1 = require("../../core/colors.js");
|
|
12
12
|
const terminal_js_2 = require("../../core/terminal.js");
|
|
13
13
|
/**
|
|
14
|
-
* Wrap text to fit within a specific width
|
|
14
|
+
* Wrap text to fit within a specific width, preserving ANSI color codes
|
|
15
|
+
*/
|
|
16
|
+
function wrapTextWithColors(text, maxWidth) {
|
|
17
|
+
// Extract ANSI codes and plain text
|
|
18
|
+
const ansiRegex = /\x1b\[[0-9;]*m/g;
|
|
19
|
+
const plainText = text.replace(ansiRegex, '');
|
|
20
|
+
// If plain text fits, return as-is
|
|
21
|
+
if (plainText.length <= maxWidth) {
|
|
22
|
+
return [text];
|
|
23
|
+
}
|
|
24
|
+
// Find all ANSI codes and their positions in the original text
|
|
25
|
+
const codes = [];
|
|
26
|
+
let match;
|
|
27
|
+
let offset = 0;
|
|
28
|
+
const textCopy = text;
|
|
29
|
+
while ((match = ansiRegex.exec(textCopy)) !== null) {
|
|
30
|
+
// Calculate position in plain text
|
|
31
|
+
const plainPos = match.index - offset;
|
|
32
|
+
codes.push({ pos: plainPos, code: match[0] });
|
|
33
|
+
offset += match[0].length;
|
|
34
|
+
}
|
|
35
|
+
// Wrap plain text by words
|
|
36
|
+
const words = plainText.split(' ');
|
|
37
|
+
const lines = [];
|
|
38
|
+
let currentLine = '';
|
|
39
|
+
let currentPos = 0;
|
|
40
|
+
for (const word of words) {
|
|
41
|
+
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
|
42
|
+
if (testLine.length <= maxWidth) {
|
|
43
|
+
currentLine = testLine;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
if (currentLine) {
|
|
47
|
+
lines.push(currentLine);
|
|
48
|
+
currentPos += currentLine.length + 1; // +1 for space
|
|
49
|
+
}
|
|
50
|
+
currentLine = word;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (currentLine) {
|
|
54
|
+
lines.push(currentLine);
|
|
55
|
+
}
|
|
56
|
+
// Re-insert ANSI codes into wrapped lines
|
|
57
|
+
const result = [];
|
|
58
|
+
let lineStart = 0;
|
|
59
|
+
let activeColor = '';
|
|
60
|
+
for (const line of lines) {
|
|
61
|
+
const lineEnd = lineStart + line.length;
|
|
62
|
+
let coloredLine = activeColor; // Start with active color from previous line
|
|
63
|
+
// Find codes that apply to this line
|
|
64
|
+
let lastPos = 0;
|
|
65
|
+
for (const { pos, code } of codes) {
|
|
66
|
+
if (pos >= lineStart && pos < lineEnd) {
|
|
67
|
+
const relPos = pos - lineStart;
|
|
68
|
+
coloredLine += line.substring(lastPos, relPos) + code;
|
|
69
|
+
lastPos = relPos;
|
|
70
|
+
// Track active color (reset or new color)
|
|
71
|
+
if (code === '\x1b[0m') {
|
|
72
|
+
activeColor = '';
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
activeColor = code;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
coloredLine += line.substring(lastPos);
|
|
80
|
+
// Add reset at end if there's an active color
|
|
81
|
+
if (activeColor && activeColor !== '\x1b[0m') {
|
|
82
|
+
coloredLine += '\x1b[0m';
|
|
83
|
+
}
|
|
84
|
+
result.push(coloredLine);
|
|
85
|
+
lineStart = lineEnd + 1; // +1 for space between words
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Wrap text to fit within a specific width (plain text only)
|
|
15
91
|
*/
|
|
16
92
|
function wrapText(text, maxWidth) {
|
|
17
93
|
const words = text.split(' ');
|
|
@@ -39,7 +115,16 @@ function wrapText(text, maxWidth) {
|
|
|
39
115
|
* @param config - Summary table configuration
|
|
40
116
|
*/
|
|
41
117
|
function renderSummaryTable(config) {
|
|
42
|
-
const { title, titleAlign = 'center', sections, width } = config;
|
|
118
|
+
const { title, titleAlign = 'center', sections, width, colors: colorConfig } = config;
|
|
119
|
+
// Default colors
|
|
120
|
+
const defaultColors = {
|
|
121
|
+
title: 'cyan+bold',
|
|
122
|
+
sectionHeader: '', // No color (default/black)
|
|
123
|
+
key: 'cyan',
|
|
124
|
+
value: '' // No color (default/black)
|
|
125
|
+
};
|
|
126
|
+
// Merge with provided colors
|
|
127
|
+
const finalColors = { ...defaultColors, ...colorConfig };
|
|
43
128
|
const termWidth = (0, terminal_js_2.getTerminalWidth)();
|
|
44
129
|
// Use full terminal width minus padding, or specified width
|
|
45
130
|
const boxWidth = width || Math.max(60, termWidth - 4);
|
|
@@ -51,53 +136,68 @@ function renderSummaryTable(config) {
|
|
|
51
136
|
(0, terminal_js_1.writeLine)(`│${' '.repeat(boxWidth - 2)}│`);
|
|
52
137
|
// Title if provided
|
|
53
138
|
if (title) {
|
|
139
|
+
// Parse color configuration (supports "color" or "color+style" format)
|
|
140
|
+
let titleColor = '';
|
|
141
|
+
if (finalColors.title) {
|
|
142
|
+
const parts = finalColors.title.split('+');
|
|
143
|
+
titleColor = parts.map(part => colors_js_1.colors[part.trim()] || '').join('');
|
|
144
|
+
}
|
|
145
|
+
const resetColor = titleColor ? colors_js_1.colors.reset : '';
|
|
54
146
|
let titleLine;
|
|
55
147
|
let remainingSpace;
|
|
56
148
|
if (titleAlign === 'left') {
|
|
57
|
-
titleLine = ` ${
|
|
149
|
+
titleLine = ` ${titleColor}${title}${resetColor}`;
|
|
58
150
|
const plainTitle = title;
|
|
59
151
|
remainingSpace = boxWidth - plainTitle.length - 4;
|
|
60
152
|
}
|
|
61
153
|
else if (titleAlign === 'right') {
|
|
62
154
|
const plainTitle = title;
|
|
63
155
|
const rightPadding = contentWidth - plainTitle.length;
|
|
64
|
-
titleLine = ' '.repeat(rightPadding + 2) +
|
|
156
|
+
titleLine = ' '.repeat(rightPadding + 2) + titleColor + title + resetColor;
|
|
65
157
|
remainingSpace = 2;
|
|
66
158
|
}
|
|
67
159
|
else {
|
|
68
160
|
// center (default)
|
|
69
161
|
const titlePadding = Math.floor((contentWidth - title.length) / 2);
|
|
70
|
-
titleLine = ' '.repeat(titlePadding + 2) +
|
|
162
|
+
titleLine = ' '.repeat(titlePadding + 2) + titleColor + title + resetColor;
|
|
71
163
|
remainingSpace = boxWidth - titlePadding - title.length - 4;
|
|
72
164
|
}
|
|
73
165
|
(0, terminal_js_1.writeLine)(`│${titleLine}${' '.repeat(remainingSpace)}│`);
|
|
74
166
|
(0, terminal_js_1.writeLine)(`│${' '.repeat(boxWidth - 2)}│`);
|
|
75
167
|
}
|
|
168
|
+
// Calculate global keyPadding based on longest key across ALL sections
|
|
169
|
+
const allKeys = sections.flatMap(section => section.items.map(item => item.key));
|
|
170
|
+
const maxKeyLength = Math.max(...allKeys.map(key => key.length));
|
|
171
|
+
const keyPadding = maxKeyLength + 3; // +3 for colon and spacing
|
|
76
172
|
// Sections
|
|
77
173
|
sections.forEach((section, sectionIndex) => {
|
|
78
174
|
// Section header if provided
|
|
79
175
|
if (section.header) {
|
|
80
|
-
const
|
|
176
|
+
const headerColor = finalColors.sectionHeader ? colors_js_1.colors[finalColors.sectionHeader] || '' : '';
|
|
177
|
+
const resetColor = headerColor ? colors_js_1.colors.reset : '';
|
|
178
|
+
const headerLine = ` ${headerColor}${section.header}${resetColor}`;
|
|
81
179
|
const remainingSpace = boxWidth - section.header.length - 4;
|
|
82
180
|
(0, terminal_js_1.writeLine)(`│${headerLine}${' '.repeat(remainingSpace)}│`);
|
|
83
181
|
}
|
|
84
|
-
// Section items
|
|
182
|
+
// Section items (using global keyPadding)
|
|
85
183
|
section.items.forEach(item => {
|
|
86
|
-
const keyPadding = 15;
|
|
87
184
|
const valueMaxWidth = contentWidth - keyPadding - 2; // 2 for leading spaces
|
|
88
185
|
// Check if value needs wrapping
|
|
89
186
|
const plainValue = item.value.replace(/\x1b\[[0-9;]*m/g, '');
|
|
90
187
|
if (plainValue.length > valueMaxWidth) {
|
|
91
|
-
// Wrap the value
|
|
92
|
-
const wrappedLines =
|
|
188
|
+
// Wrap the value (preserving colors if present)
|
|
189
|
+
const wrappedLines = wrapTextWithColors(item.value, valueMaxWidth);
|
|
93
190
|
// First line with key
|
|
94
|
-
const
|
|
191
|
+
const keyColor = finalColors.key ? colors_js_1.colors[finalColors.key] || '' : '';
|
|
192
|
+
const keyResetColor = keyColor ? colors_js_1.colors.reset : '';
|
|
193
|
+
// wrappedLines already contain colors, don't add valueColor
|
|
194
|
+
const firstLine = ` ${keyColor}${item.key}:${keyResetColor}${' '.repeat(Math.max(1, keyPadding - item.key.length))}${wrappedLines[0]}`;
|
|
95
195
|
const plainFirstLine = firstLine.replace(/\x1b\[[0-9;]*m/g, '');
|
|
96
196
|
const remainingSpace = boxWidth - plainFirstLine.length - 2;
|
|
97
197
|
(0, terminal_js_1.writeLine)(`│${firstLine}${' '.repeat(Math.max(0, remainingSpace))}│`);
|
|
98
198
|
// Subsequent lines with indentation
|
|
99
199
|
for (let i = 1; i < wrappedLines.length; i++) {
|
|
100
|
-
const continuationLine = ` ${' '.repeat(keyPadding)}${wrappedLines[i]}`;
|
|
200
|
+
const continuationLine = ` ${' '.repeat(keyPadding + 1)}${wrappedLines[i]}`;
|
|
101
201
|
const plainContinuationLine = continuationLine.replace(/\x1b\[[0-9;]*m/g, '');
|
|
102
202
|
const contRemainingSpace = boxWidth - plainContinuationLine.length - 2;
|
|
103
203
|
(0, terminal_js_1.writeLine)(`│${continuationLine}${' '.repeat(Math.max(0, contRemainingSpace))}│`);
|
|
@@ -105,7 +205,13 @@ function renderSummaryTable(config) {
|
|
|
105
205
|
}
|
|
106
206
|
else {
|
|
107
207
|
// No wrapping needed
|
|
108
|
-
const
|
|
208
|
+
const keyColor = finalColors.key ? colors_js_1.colors[finalColors.key] || '' : '';
|
|
209
|
+
const keyResetColor = keyColor ? colors_js_1.colors.reset : '';
|
|
210
|
+
const valueColor = finalColors.value ? colors_js_1.colors[finalColors.value] || '' : '';
|
|
211
|
+
const valueResetColor = valueColor ? colors_js_1.colors.reset : '';
|
|
212
|
+
// Only wrap value with color if valueColor is set, otherwise preserve original colors in item.value
|
|
213
|
+
const coloredValue = valueColor ? `${valueColor}${item.value}${valueResetColor}` : item.value;
|
|
214
|
+
const itemLine = ` ${keyColor}${item.key}:${keyResetColor}${' '.repeat(Math.max(1, keyPadding - item.key.length))}${coloredValue}`;
|
|
109
215
|
const plainItemLine = itemLine.replace(/\x1b\[[0-9;]*m/g, '');
|
|
110
216
|
const remainingSpace = boxWidth - plainItemLine.length - 2;
|
|
111
217
|
(0, terminal_js_1.writeLine)(`│${itemLine}${' '.repeat(Math.max(0, remainingSpace))}│`);
|
package/package.json
CHANGED