cli-menu-kit 0.1.23 → 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,55 +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
|
-
//
|
|
85
|
-
const maxKeyLength = Math.max(...section.items.map(item => item.key.length));
|
|
86
|
-
const keyPadding = maxKeyLength + 3; // +3 for colon and spacing
|
|
87
|
-
// Section items
|
|
182
|
+
// Section items (using global keyPadding)
|
|
88
183
|
section.items.forEach(item => {
|
|
89
184
|
const valueMaxWidth = contentWidth - keyPadding - 2; // 2 for leading spaces
|
|
90
185
|
// Check if value needs wrapping
|
|
91
186
|
const plainValue = item.value.replace(/\x1b\[[0-9;]*m/g, '');
|
|
92
187
|
if (plainValue.length > valueMaxWidth) {
|
|
93
|
-
// Wrap the value
|
|
94
|
-
const wrappedLines =
|
|
188
|
+
// Wrap the value (preserving colors if present)
|
|
189
|
+
const wrappedLines = wrapTextWithColors(item.value, valueMaxWidth);
|
|
95
190
|
// First line with key
|
|
96
|
-
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]}`;
|
|
97
195
|
const plainFirstLine = firstLine.replace(/\x1b\[[0-9;]*m/g, '');
|
|
98
196
|
const remainingSpace = boxWidth - plainFirstLine.length - 2;
|
|
99
197
|
(0, terminal_js_1.writeLine)(`│${firstLine}${' '.repeat(Math.max(0, remainingSpace))}│`);
|
|
100
198
|
// Subsequent lines with indentation
|
|
101
199
|
for (let i = 1; i < wrappedLines.length; i++) {
|
|
102
|
-
const continuationLine = ` ${' '.repeat(keyPadding)}${wrappedLines[i]}`;
|
|
200
|
+
const continuationLine = ` ${' '.repeat(keyPadding + 1)}${wrappedLines[i]}`;
|
|
103
201
|
const plainContinuationLine = continuationLine.replace(/\x1b\[[0-9;]*m/g, '');
|
|
104
202
|
const contRemainingSpace = boxWidth - plainContinuationLine.length - 2;
|
|
105
203
|
(0, terminal_js_1.writeLine)(`│${continuationLine}${' '.repeat(Math.max(0, contRemainingSpace))}│`);
|
|
@@ -107,7 +205,13 @@ function renderSummaryTable(config) {
|
|
|
107
205
|
}
|
|
108
206
|
else {
|
|
109
207
|
// No wrapping needed
|
|
110
|
-
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}`;
|
|
111
215
|
const plainItemLine = itemLine.replace(/\x1b\[[0-9;]*m/g, '');
|
|
112
216
|
const remainingSpace = boxWidth - plainItemLine.length - 2;
|
|
113
217
|
(0, terminal_js_1.writeLine)(`│${itemLine}${' '.repeat(Math.max(0, remainingSpace))}│`);
|
package/package.json
CHANGED