linecraft 0.2.0 → 0.2.2
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/LICENSE +0 -1
- package/README.md +34 -8
- package/lib/component.d.ts +34 -0
- package/lib/component.d.ts.map +1 -0
- package/lib/component.js +42 -0
- package/lib/component.js.map +1 -0
- package/lib/components/code-debug.d.ts +35 -0
- package/lib/components/code-debug.d.ts.map +1 -0
- package/lib/components/code-debug.js +294 -0
- package/lib/components/code-debug.js.map +1 -0
- package/lib/components/fill.d.ts +15 -0
- package/lib/components/fill.d.ts.map +1 -0
- package/lib/components/fill.js +37 -0
- package/lib/components/fill.js.map +1 -0
- package/lib/components/index.d.ts +2 -2
- package/lib/components/index.d.ts.map +1 -1
- package/lib/components/index.js +2 -2
- package/lib/components/index.js.map +1 -1
- package/lib/components/progress-bar-grid.d.ts +1 -1
- package/lib/components/progress-bar-grid.d.ts.map +1 -1
- package/lib/components/progress-bar-grid.js +6 -6
- package/lib/components/progress-bar-grid.js.map +1 -1
- package/lib/components/prompt.d.ts +4 -5
- package/lib/components/prompt.d.ts.map +1 -1
- package/lib/components/prompt.js +17 -69
- package/lib/components/prompt.js.map +1 -1
- package/lib/components/section.d.ts +33 -0
- package/lib/components/section.d.ts.map +1 -0
- package/lib/components/section.js +178 -0
- package/lib/components/section.js.map +1 -0
- package/lib/components/segments.d.ts +26 -0
- package/lib/components/segments.d.ts.map +1 -0
- package/lib/components/segments.js +105 -0
- package/lib/components/segments.js.map +1 -0
- package/lib/components/spinner.d.ts +18 -16
- package/lib/components/spinner.d.ts.map +1 -1
- package/lib/components/spinner.js +63 -47
- package/lib/components/spinner.js.map +1 -1
- package/lib/components/style.test.js +11 -11
- package/lib/components/style.test.js.map +1 -1
- package/lib/components/styled.d.ts +17 -0
- package/lib/components/styled.d.ts.map +1 -0
- package/lib/components/styled.js +107 -0
- package/lib/components/styled.js.map +1 -0
- package/lib/components/styled.test.d.ts +2 -0
- package/lib/components/styled.test.d.ts.map +1 -0
- package/lib/components/styled.test.js +135 -0
- package/lib/components/styled.test.js.map +1 -0
- package/lib/index.d.ts +17 -13
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +13 -13
- package/lib/index.js.map +1 -1
- package/lib/index.test.js +17 -11
- package/lib/index.test.js.map +1 -1
- package/lib/layout/grid.d.ts +31 -35
- package/lib/layout/grid.d.ts.map +1 -1
- package/lib/layout/grid.js +437 -216
- package/lib/layout/grid.js.map +1 -1
- package/lib/layout/grid.test.js +332 -36
- package/lib/layout/grid.test.js.map +1 -1
- package/lib/native/ansi.d.ts +9 -0
- package/lib/native/ansi.d.ts.map +1 -1
- package/lib/native/ansi.js +9 -0
- package/lib/native/ansi.js.map +1 -1
- package/lib/native/diff.d.ts +5 -1
- package/lib/native/diff.d.ts.map +1 -1
- package/lib/native/diff.js +25 -7
- package/lib/native/diff.js.map +1 -1
- package/lib/native/region-renderer-debug.test.d.ts +2 -0
- package/lib/native/region-renderer-debug.test.d.ts.map +1 -0
- package/lib/native/region-renderer-debug.test.js +45 -0
- package/lib/native/region-renderer-debug.test.js.map +1 -0
- package/lib/native/region-renderer.d.ts +57 -148
- package/lib/native/region-renderer.d.ts.map +1 -1
- package/lib/native/region-renderer.js +455 -1124
- package/lib/native/region-renderer.js.map +1 -1
- package/lib/native/region.test.js +2 -20
- package/lib/native/region.test.js.map +1 -1
- package/lib/region-resize.test.d.ts +2 -0
- package/lib/region-resize.test.d.ts.map +1 -0
- package/lib/region-resize.test.js +124 -0
- package/lib/region-resize.test.js.map +1 -0
- package/lib/region.d.ts +97 -9
- package/lib/region.d.ts.map +1 -1
- package/lib/region.js +591 -185
- package/lib/region.js.map +1 -1
- package/lib/region.test.js +3 -3
- package/lib/region.test.js.map +1 -1
- package/lib/types.d.ts +9 -0
- package/lib/types.d.ts.map +1 -1
- package/lib/utils/file-link.d.ts +16 -0
- package/lib/utils/file-link.d.ts.map +1 -0
- package/lib/utils/file-link.js +23 -0
- package/lib/utils/file-link.js.map +1 -0
- package/lib/utils/prompt.d.ts +15 -0
- package/lib/utils/prompt.d.ts.map +1 -0
- package/lib/utils/prompt.js +128 -0
- package/lib/utils/prompt.js.map +1 -0
- package/lib/utils/terminal-theme.d.ts +36 -0
- package/lib/utils/terminal-theme.d.ts.map +1 -0
- package/lib/utils/terminal-theme.js +61 -0
- package/lib/utils/terminal-theme.js.map +1 -0
- package/lib/utils/text.d.ts +53 -3
- package/lib/utils/text.d.ts.map +1 -1
- package/lib/utils/text.js +194 -36
- package/lib/utils/text.js.map +1 -1
- package/lib/utils/wait-for-spacebar.d.ts.map +1 -1
- package/lib/utils/wait-for-spacebar.js +9 -6
- package/lib/utils/wait-for-spacebar.js.map +1 -1
- package/package.json +17 -13
package/lib/layout/grid.js
CHANGED
|
@@ -1,17 +1,44 @@
|
|
|
1
1
|
// CSS Grid-based layout system for terminal components
|
|
2
2
|
// Simplified grid that eliminates circular measurement complexity
|
|
3
3
|
import { applyStyle } from '../utils/colors';
|
|
4
|
+
import { getTrimmedTextWidth, stripAnsi } from '../utils/text';
|
|
5
|
+
import { callComponent, createChildContext } from '../component';
|
|
6
|
+
/**
|
|
7
|
+
* Extract character and color from spaceBetween option for a specific gap index
|
|
8
|
+
* spaceBetween can be a FillChar, or an array of FillChar values
|
|
9
|
+
*/
|
|
10
|
+
function getSpaceBetweenChar(spaceBetween, gapIndex) {
|
|
11
|
+
if (typeof spaceBetween === 'string') {
|
|
12
|
+
return { char: spaceBetween };
|
|
13
|
+
}
|
|
14
|
+
if (Array.isArray(spaceBetween)) {
|
|
15
|
+
const item = spaceBetween[gapIndex] ?? spaceBetween[spaceBetween.length - 1];
|
|
16
|
+
if (typeof item === 'string') {
|
|
17
|
+
return { char: item };
|
|
18
|
+
}
|
|
19
|
+
return { char: item.char, color: item.color };
|
|
20
|
+
}
|
|
21
|
+
// Single object
|
|
22
|
+
return { char: spaceBetween.char, color: spaceBetween.color };
|
|
23
|
+
}
|
|
4
24
|
function parseTemplateEntry(entry) {
|
|
5
25
|
if (typeof entry === 'number') {
|
|
6
26
|
return { type: 'fixed', value: entry };
|
|
7
27
|
}
|
|
28
|
+
if (entry === 'auto') {
|
|
29
|
+
return { type: 'auto', value: 0 };
|
|
30
|
+
}
|
|
8
31
|
if (typeof entry === 'string') {
|
|
32
|
+
// Shorthand: '*' means '1*'
|
|
33
|
+
if (entry === '*') {
|
|
34
|
+
return { type: 'flex', value: 0, flexRatio: 1 };
|
|
35
|
+
}
|
|
9
36
|
// Parse flex unit: '1*', '2*', etc.
|
|
10
37
|
const match = entry.match(/^(\d+)\*$/);
|
|
11
38
|
if (match) {
|
|
12
39
|
return { type: 'flex', value: 0, flexRatio: parseInt(match[1], 10) };
|
|
13
40
|
}
|
|
14
|
-
throw new Error(`Invalid flex unit: ${entry}. Use format like '1*', '2*'`);
|
|
41
|
+
throw new Error(`Invalid flex unit: ${entry}. Use format like '*', '1*', '2*'`);
|
|
15
42
|
}
|
|
16
43
|
// Minmax: { min: 40, width: '2*' }
|
|
17
44
|
const match = entry.width.match(/^(\d+)\*$/);
|
|
@@ -28,24 +55,44 @@ function parseTemplateEntry(entry) {
|
|
|
28
55
|
* Calculate column widths from template
|
|
29
56
|
* Reference: https://www.w3.org/TR/css-grid-1/#track-sizing
|
|
30
57
|
*/
|
|
31
|
-
function
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
58
|
+
function expandTemplate(columns, autoColumns, template, count) {
|
|
59
|
+
// Priority: if columns/autoColumns are specified, use those (new API)
|
|
60
|
+
// Otherwise, if template is specified, use it where last value acts as autoColumns
|
|
61
|
+
// Otherwise, default to empty columns with '1*' autoColumns
|
|
62
|
+
let explicitColumns;
|
|
63
|
+
let autoColumn;
|
|
64
|
+
if (columns !== undefined || autoColumns !== undefined) {
|
|
65
|
+
// New API: explicit columns and/or autoColumns
|
|
66
|
+
explicitColumns = columns ?? [];
|
|
67
|
+
autoColumn = autoColumns ?? '1*';
|
|
68
|
+
}
|
|
69
|
+
else if (template !== undefined) {
|
|
70
|
+
// Deprecated API: template where last value acts as autoColumns
|
|
71
|
+
explicitColumns = template;
|
|
72
|
+
autoColumn = template.length > 0 ? template[template.length - 1] : '1*';
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// No columns specified: default to empty with '1*' autoColumns
|
|
76
|
+
explicitColumns = [];
|
|
77
|
+
autoColumn = '1*';
|
|
78
|
+
}
|
|
79
|
+
const expanded = [];
|
|
80
|
+
for (let i = 0; i < count; i++) {
|
|
81
|
+
if (i < explicitColumns.length) {
|
|
82
|
+
expanded.push(explicitColumns[i]);
|
|
37
83
|
}
|
|
38
84
|
else {
|
|
39
|
-
//
|
|
40
|
-
|
|
85
|
+
// Apply the same autoColumn value to all implicitly created columns
|
|
86
|
+
// (CSS Grid's grid-auto-columns is a single value applied to all auto columns)
|
|
87
|
+
expanded.push(autoColumn);
|
|
41
88
|
}
|
|
42
89
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
expandedTemplate.push('1*');
|
|
90
|
+
if (expanded.length === 0) {
|
|
91
|
+
expanded.push('1*');
|
|
46
92
|
}
|
|
47
|
-
|
|
48
|
-
|
|
93
|
+
return expanded;
|
|
94
|
+
}
|
|
95
|
+
function calculateColumnWidths(tracks, availableWidth, columnGap, autoContentWidths) {
|
|
49
96
|
// Step 1: Calculate fixed track sizes
|
|
50
97
|
let fixedTotal = 0;
|
|
51
98
|
let flexTotal = 0;
|
|
@@ -65,6 +112,11 @@ function calculateColumnWidths(template, availableWidth, columnGap, numChildren)
|
|
|
65
112
|
fixedTotal += track.value;
|
|
66
113
|
flexTotal += track.flexRatio ?? 1;
|
|
67
114
|
}
|
|
115
|
+
else if (track.type === 'auto') {
|
|
116
|
+
const autoWidth = autoContentWidths[i] ?? 0;
|
|
117
|
+
widths[i] = autoWidth;
|
|
118
|
+
fixedTotal += autoWidth;
|
|
119
|
+
}
|
|
68
120
|
}
|
|
69
121
|
// Step 2: Calculate gap space
|
|
70
122
|
const gapSpace = columnGap * (tracks.length - 1);
|
|
@@ -84,172 +136,213 @@ function calculateColumnWidths(template, availableWidth, columnGap, numChildren)
|
|
|
84
136
|
}
|
|
85
137
|
}
|
|
86
138
|
// Step 4: Round to integers
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (validChildren.length === 0) {
|
|
99
|
-
return 0;
|
|
139
|
+
const rounded = widths.map(w => Math.max(0, Math.floor(w)));
|
|
140
|
+
// Step 5: Clamp total width to available space (minus gaps)
|
|
141
|
+
// CRITICAL: Preserve auto column widths - only adjust flex columns
|
|
142
|
+
const maxContentWidth = Math.max(0, availableWidth - gapSpace);
|
|
143
|
+
let totalRounded = rounded.reduce((sum, w) => sum + w, 0);
|
|
144
|
+
if (totalRounded > maxContentWidth && totalRounded > 0) {
|
|
145
|
+
// Calculate auto/fixed total (these should not be scaled)
|
|
146
|
+
let autoFixedTotal = 0;
|
|
147
|
+
for (let i = 0; i < tracks.length; i++) {
|
|
148
|
+
if (tracks[i].type === 'auto' || tracks[i].type === 'fixed') {
|
|
149
|
+
autoFixedTotal += rounded[i];
|
|
100
150
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const result = child(childCtx);
|
|
113
|
-
if (result !== null) {
|
|
114
|
-
const lines = Array.isArray(result) ? result.length : 1;
|
|
115
|
-
maxLines = Math.max(maxLines, lines);
|
|
151
|
+
}
|
|
152
|
+
// Only scale flex columns if needed
|
|
153
|
+
const flexTotal = totalRounded - autoFixedTotal;
|
|
154
|
+
if (flexTotal > 0) {
|
|
155
|
+
const availableForFlex = Math.max(0, maxContentWidth - autoFixedTotal);
|
|
156
|
+
const scale = availableForFlex / flexTotal;
|
|
157
|
+
let adjustedTotal = autoFixedTotal;
|
|
158
|
+
for (let i = 0; i < rounded.length; i++) {
|
|
159
|
+
if (tracks[i].type === 'flex' || tracks[i].type === 'minmax') {
|
|
160
|
+
rounded[i] = Math.max(0, Math.floor(rounded[i] * scale));
|
|
161
|
+
adjustedTotal += rounded[i];
|
|
116
162
|
}
|
|
117
163
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
// Calculate column widths
|
|
128
|
-
const columnWidths = calculateColumnWidths(template, width, columnGap, validChildren.length);
|
|
129
|
-
// Handle justify: 'space-between'
|
|
130
|
-
let startX = x;
|
|
131
|
-
let actualWidths = columnWidths;
|
|
132
|
-
if (justify === 'space-between' && validChildren.length > 1) {
|
|
133
|
-
const firstWidth = columnWidths[0];
|
|
134
|
-
const lastWidth = columnWidths[columnWidths.length - 1];
|
|
135
|
-
const middleTotal = columnWidths.slice(1, -1).reduce((sum, w) => sum + w, 0);
|
|
136
|
-
const middleFlex = width - firstWidth - lastWidth - (columnGap * (validChildren.length - 1));
|
|
137
|
-
actualWidths = [firstWidth];
|
|
138
|
-
for (let i = 1; i < columnWidths.length - 1; i++) {
|
|
139
|
-
const ratio = middleTotal > 0 ? columnWidths[i] / middleTotal : 1 / (columnWidths.length - 2);
|
|
140
|
-
actualWidths.push(Math.floor(middleFlex * ratio));
|
|
164
|
+
let remainder = maxContentWidth - adjustedTotal;
|
|
165
|
+
let idx = 0;
|
|
166
|
+
while (remainder > 0 && rounded.length > 0) {
|
|
167
|
+
const targetIndex = idx % rounded.length;
|
|
168
|
+
// Only add to flex columns
|
|
169
|
+
if (tracks[targetIndex].type === 'flex' || tracks[targetIndex].type === 'minmax') {
|
|
170
|
+
rounded[targetIndex] += 1;
|
|
171
|
+
remainder -= 1;
|
|
141
172
|
}
|
|
142
|
-
|
|
173
|
+
idx++;
|
|
174
|
+
if (idx > rounded.length * 10)
|
|
175
|
+
break; // Safety limit
|
|
143
176
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
columnIndex: i,
|
|
155
|
-
rowIndex: 0,
|
|
156
|
-
};
|
|
157
|
-
// Render child
|
|
158
|
-
const result = child(childCtx);
|
|
159
|
-
results.push(result);
|
|
160
|
-
// Handle spaceBetween
|
|
161
|
-
if (i < validChildren.length - 1 && spaceBetween) {
|
|
162
|
-
const spaceChar = typeof spaceBetween === 'string'
|
|
163
|
-
? spaceBetween
|
|
164
|
-
: Array.isArray(spaceBetween)
|
|
165
|
-
? (spaceBetween[i] ?? spaceBetween[spaceBetween.length - 1])
|
|
166
|
-
: spaceBetween.char;
|
|
167
|
-
const spaceColor = typeof spaceBetween === 'object' && !Array.isArray(spaceBetween)
|
|
168
|
-
? spaceBetween.color
|
|
169
|
-
: undefined;
|
|
170
|
-
const gapText = spaceChar.repeat(columnGap);
|
|
171
|
-
results.push(spaceColor ? applyStyle(gapText, { color: spaceColor }) : gapText);
|
|
172
|
-
}
|
|
173
|
-
currentX += childWidth + columnGap;
|
|
174
|
-
}
|
|
175
|
-
// Handle multi-line: if any result is string[], expand grid vertically
|
|
176
|
-
const maxLines = Math.max(...results.map(r => Array.isArray(r) ? r.length : 1), 1);
|
|
177
|
-
// Render line by line
|
|
178
|
-
for (let lineIdx = 0; lineIdx < maxLines; lineIdx++) {
|
|
179
|
-
const lineParts = [];
|
|
180
|
-
let partIdx = 0;
|
|
181
|
-
for (let i = 0; i < validChildren.length; i++) {
|
|
182
|
-
const result = results[partIdx];
|
|
183
|
-
partIdx++;
|
|
184
|
-
const columnWidth = actualWidths[i] ?? 0;
|
|
185
|
-
if (result === null) {
|
|
186
|
-
// Null result - pad to column width
|
|
187
|
-
lineParts.push(' '.repeat(columnWidth));
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
let columnContent;
|
|
191
|
-
if (Array.isArray(result)) {
|
|
192
|
-
columnContent = result[lineIdx] ?? '';
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
columnContent = lineIdx === 0 ? result : '';
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
// No flex columns - scale down fixed and auto columns proportionally if needed
|
|
180
|
+
if (autoFixedTotal > maxContentWidth) {
|
|
181
|
+
const scale = maxContentWidth / autoFixedTotal;
|
|
182
|
+
let adjustedTotal = 0;
|
|
183
|
+
for (let i = 0; i < rounded.length; i++) {
|
|
184
|
+
if (tracks[i].type === 'auto' || tracks[i].type === 'fixed') {
|
|
185
|
+
rounded[i] = Math.max(0, Math.floor(rounded[i] * scale));
|
|
186
|
+
adjustedTotal += rounded[i];
|
|
196
187
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if (spaceBetween) {
|
|
207
|
-
const spaceChar = typeof spaceBetween === 'string'
|
|
208
|
-
? spaceBetween
|
|
209
|
-
: Array.isArray(spaceBetween)
|
|
210
|
-
? (spaceBetween[i] ?? spaceBetween[spaceBetween.length - 1])
|
|
211
|
-
: spaceBetween.char;
|
|
212
|
-
const spaceColor = typeof spaceBetween === 'object' && !Array.isArray(spaceBetween)
|
|
213
|
-
? spaceBetween.color
|
|
214
|
-
: undefined;
|
|
215
|
-
const gapText = spaceChar.repeat(columnGap);
|
|
216
|
-
lineParts.push(spaceColor ? applyStyle(gapText, { color: spaceColor }) : gapText);
|
|
217
|
-
partIdx++; // spaceBetween adds an extra result
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
// Just add spaces for columnGap
|
|
221
|
-
lineParts.push(' '.repeat(columnGap));
|
|
222
|
-
}
|
|
188
|
+
}
|
|
189
|
+
// Distribute remainder to fixed columns (prefer fixed over auto)
|
|
190
|
+
let remainder = maxContentWidth - adjustedTotal;
|
|
191
|
+
let idx = 0;
|
|
192
|
+
while (remainder > 0 && rounded.length > 0) {
|
|
193
|
+
const targetIndex = idx % rounded.length;
|
|
194
|
+
if (tracks[targetIndex].type === 'fixed' || tracks[targetIndex].type === 'auto') {
|
|
195
|
+
rounded[targetIndex] += 1;
|
|
196
|
+
remainder -= 1;
|
|
223
197
|
}
|
|
198
|
+
idx++;
|
|
199
|
+
if (idx > rounded.length * 10)
|
|
200
|
+
break; // Safety limit
|
|
224
201
|
}
|
|
225
|
-
const line = lineParts.join('');
|
|
226
|
-
const lineY = y + lineIdx;
|
|
227
|
-
// CRITICAL: Pad line to full width to ensure grid fills the region
|
|
228
|
-
// This ensures grids are always full-width by default
|
|
229
|
-
const plainLine = line.replace(/\x1b\[[0-9;]*m/g, '');
|
|
230
|
-
const paddedLine = plainLine.length < width
|
|
231
|
-
? line + ' '.repeat(width - plainLine.length)
|
|
232
|
-
: line;
|
|
233
|
-
// For now, just set the line (we can add merging later if needed)
|
|
234
|
-
region.setLine(lineY, paddedLine);
|
|
235
202
|
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return rounded;
|
|
206
|
+
}
|
|
207
|
+
function getRenderedWidth(result) {
|
|
208
|
+
if (result === null) {
|
|
209
|
+
return 0;
|
|
210
|
+
}
|
|
211
|
+
if (Array.isArray(result)) {
|
|
212
|
+
return result.reduce((max, line) => Math.max(max, getTrimmedTextWidth(line)), 0);
|
|
213
|
+
}
|
|
214
|
+
return getTrimmedTextWidth(result);
|
|
215
|
+
}
|
|
216
|
+
function measureAutoContentWidths(tracks, children, ctxFactory) {
|
|
217
|
+
const widths = new Array(tracks.length).fill(0);
|
|
218
|
+
for (let i = 0; i < tracks.length; i++) {
|
|
219
|
+
if (tracks[i].type !== 'auto') {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const result = children[i] ? callComponent(children[i], ctxFactory(i)) : null;
|
|
223
|
+
widths[i] = getRenderedWidth(result);
|
|
224
|
+
}
|
|
225
|
+
return widths;
|
|
238
226
|
}
|
|
239
227
|
/**
|
|
240
228
|
* Create a grid component (function-based API)
|
|
241
229
|
* This returns a Component that can be used in other grids
|
|
230
|
+
* Accepts Component children, strings (converted to Styled components), or objects with render methods
|
|
242
231
|
*/
|
|
243
232
|
export function grid(options, ...children) {
|
|
233
|
+
// Convert children to Components
|
|
234
|
+
const convertedChildren = children.map(child => {
|
|
235
|
+
if (typeof child === 'string') {
|
|
236
|
+
// Import Styled dynamically to avoid circular dependency
|
|
237
|
+
const { Styled } = require('../components/styled');
|
|
238
|
+
return Styled({}, child);
|
|
239
|
+
}
|
|
240
|
+
// If it's an object with a render method, extract the render function
|
|
241
|
+
if (typeof child === 'object' && child !== null && 'render' in child && typeof child.render === 'function') {
|
|
242
|
+
return child.render;
|
|
243
|
+
}
|
|
244
|
+
// Otherwise it's already a Component
|
|
245
|
+
return child;
|
|
246
|
+
});
|
|
244
247
|
return (ctx) => {
|
|
245
|
-
const { template, columnGap = 0, spaceBetween, justify = 'start' } = options;
|
|
248
|
+
const { columns, autoColumns, template, rows, autoRows = 1, columnGap = 0, rowGap = 0, spaceBetween, justify = 'start' } = options;
|
|
246
249
|
// Filter out null children (from when conditions)
|
|
247
|
-
const validChildren =
|
|
250
|
+
const validChildren = convertedChildren.filter(c => c !== null && c !== undefined);
|
|
248
251
|
if (validChildren.length === 0) {
|
|
249
252
|
return null;
|
|
250
253
|
}
|
|
254
|
+
// Determine number of explicit columns
|
|
255
|
+
const explicitColumns = columns ?? template ?? [];
|
|
256
|
+
const numColumns = explicitColumns.length > 0 ? explicitColumns.length : 1;
|
|
257
|
+
// Group children into rows (wrap when exceeding numColumns)
|
|
258
|
+
const rowsData = [];
|
|
259
|
+
for (let i = 0; i < validChildren.length; i += numColumns) {
|
|
260
|
+
rowsData.push(validChildren.slice(i, i + numColumns));
|
|
261
|
+
}
|
|
262
|
+
// If we have multiple rows, render them separately and stack vertically
|
|
263
|
+
if (rowsData.length > 1) {
|
|
264
|
+
// Calculate column widths based on the first row (all rows use same column widths for alignment)
|
|
265
|
+
const firstRowChildren = rowsData[0] ?? [];
|
|
266
|
+
const expandedTemplate = expandTemplate(columns, autoColumns, template, firstRowChildren.length);
|
|
267
|
+
const tracks = expandedTemplate.map(parseTemplateEntry);
|
|
268
|
+
// Measure content widths for column sizing (use first row as reference)
|
|
269
|
+
const autoContentWidths = measureAutoContentWidths(tracks, firstRowChildren, (index) => createChildContext(ctx, {
|
|
270
|
+
availableWidth: Number.POSITIVE_INFINITY,
|
|
271
|
+
columnIndex: index,
|
|
272
|
+
rowIndex: 0,
|
|
273
|
+
}));
|
|
274
|
+
const columnWidths = calculateColumnWidths(tracks, ctx.availableWidth, columnGap, autoContentWidths);
|
|
275
|
+
// Render each row
|
|
276
|
+
const allRowLines = [];
|
|
277
|
+
for (let rowIdx = 0; rowIdx < rowsData.length; rowIdx++) {
|
|
278
|
+
const rowChildren = rowsData[rowIdx];
|
|
279
|
+
const rowHeight = rows?.[rowIdx] ?? autoRows;
|
|
280
|
+
// Render this row's children
|
|
281
|
+
const rowResults = [];
|
|
282
|
+
for (let colIdx = 0; colIdx < rowChildren.length; colIdx++) {
|
|
283
|
+
const child = rowChildren[colIdx];
|
|
284
|
+
const width = columnWidths[colIdx] ?? 0;
|
|
285
|
+
const childCtx = createChildContext(ctx, {
|
|
286
|
+
availableWidth: width,
|
|
287
|
+
columnIndex: colIdx,
|
|
288
|
+
rowIndex: rowIdx,
|
|
289
|
+
});
|
|
290
|
+
const result = callComponent(child, childCtx);
|
|
291
|
+
rowResults.push(result);
|
|
292
|
+
}
|
|
293
|
+
// Build row lines (handle multi-line children)
|
|
294
|
+
const maxRowLines = Math.max(...rowResults.map(r => Array.isArray(r) ? r.length : 1), rowHeight);
|
|
295
|
+
for (let lineIdx = 0; lineIdx < maxRowLines; lineIdx++) {
|
|
296
|
+
const lineParts = [];
|
|
297
|
+
for (let colIdx = 0; colIdx < rowChildren.length; colIdx++) {
|
|
298
|
+
const result = rowResults[colIdx];
|
|
299
|
+
const columnWidth = columnWidths[colIdx] ?? 0;
|
|
300
|
+
let columnContent;
|
|
301
|
+
if (result === null) {
|
|
302
|
+
columnContent = ' '.repeat(columnWidth);
|
|
303
|
+
}
|
|
304
|
+
else if (Array.isArray(result)) {
|
|
305
|
+
columnContent = result[lineIdx] ?? '';
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
columnContent = lineIdx === 0 ? result : '';
|
|
309
|
+
}
|
|
310
|
+
const plainContent = stripAnsi(columnContent);
|
|
311
|
+
const paddedContent = plainContent.length < columnWidth
|
|
312
|
+
? columnContent + ' '.repeat(columnWidth - plainContent.length)
|
|
313
|
+
: columnContent;
|
|
314
|
+
lineParts.push(paddedContent);
|
|
315
|
+
// Add column gap
|
|
316
|
+
if (colIdx < rowChildren.length - 1 && columnGap > 0) {
|
|
317
|
+
lineParts.push(' '.repeat(columnGap));
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const line = lineParts.join('');
|
|
321
|
+
const plainLine = stripAnsi(line);
|
|
322
|
+
const paddedLine = plainLine.length < ctx.availableWidth
|
|
323
|
+
? line + ' '.repeat(ctx.availableWidth - plainLine.length)
|
|
324
|
+
: line;
|
|
325
|
+
allRowLines.push(paddedLine);
|
|
326
|
+
}
|
|
327
|
+
// Add row gap (except after last row)
|
|
328
|
+
if (rowIdx < rowsData.length - 1 && rowGap > 0) {
|
|
329
|
+
for (let i = 0; i < rowGap; i++) {
|
|
330
|
+
allRowLines.push(' '.repeat(ctx.availableWidth));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return allRowLines;
|
|
335
|
+
}
|
|
336
|
+
// Single row rendering (original logic)
|
|
251
337
|
// Calculate column widths
|
|
252
|
-
const
|
|
338
|
+
const expandedTemplate = expandTemplate(columns, autoColumns, template, validChildren.length);
|
|
339
|
+
const tracks = expandedTemplate.map(parseTemplateEntry);
|
|
340
|
+
const autoContentWidths = measureAutoContentWidths(tracks, validChildren, (index) => createChildContext(ctx, {
|
|
341
|
+
availableWidth: Number.POSITIVE_INFINITY,
|
|
342
|
+
columnIndex: index,
|
|
343
|
+
rowIndex: 0,
|
|
344
|
+
}));
|
|
345
|
+
const columnWidths = calculateColumnWidths(tracks, ctx.availableWidth, columnGap, autoContentWidths);
|
|
253
346
|
// Handle justify: 'space-between'
|
|
254
347
|
let startX = 0;
|
|
255
348
|
let actualWidths = columnWidths;
|
|
@@ -269,31 +362,27 @@ export function grid(options, ...children) {
|
|
|
269
362
|
}
|
|
270
363
|
// Render each child
|
|
271
364
|
const results = [];
|
|
365
|
+
let anyRenderableChild = false;
|
|
272
366
|
let currentX = startX;
|
|
273
367
|
for (let i = 0; i < validChildren.length; i++) {
|
|
274
368
|
const child = validChildren[i];
|
|
275
369
|
const width = actualWidths[i] ?? 0;
|
|
276
370
|
// Create context for child
|
|
277
|
-
const childCtx = {
|
|
371
|
+
const childCtx = createChildContext(ctx, {
|
|
278
372
|
availableWidth: width,
|
|
279
|
-
region: ctx.region,
|
|
280
373
|
columnIndex: i,
|
|
281
374
|
rowIndex: 0,
|
|
282
|
-
};
|
|
375
|
+
});
|
|
283
376
|
// Render child
|
|
284
|
-
const result = child
|
|
377
|
+
const result = callComponent(child, childCtx);
|
|
285
378
|
results.push(result);
|
|
379
|
+
if (result !== null) {
|
|
380
|
+
anyRenderableChild = true;
|
|
381
|
+
}
|
|
286
382
|
// Handle gap (spaceBetween or spaces)
|
|
287
383
|
if (i < validChildren.length - 1 && columnGap > 0) {
|
|
288
384
|
if (spaceBetween) {
|
|
289
|
-
const spaceChar =
|
|
290
|
-
? spaceBetween
|
|
291
|
-
: Array.isArray(spaceBetween)
|
|
292
|
-
? (spaceBetween[i] ?? spaceBetween[spaceBetween.length - 1])
|
|
293
|
-
: spaceBetween.char;
|
|
294
|
-
const spaceColor = typeof spaceBetween === 'object' && !Array.isArray(spaceBetween)
|
|
295
|
-
? spaceBetween.color
|
|
296
|
-
: undefined;
|
|
385
|
+
const { char: spaceChar, color: spaceColor } = getSpaceBetweenChar(spaceBetween, i);
|
|
297
386
|
// Fill gap with space character
|
|
298
387
|
const gapText = spaceChar.repeat(columnGap);
|
|
299
388
|
results.push(spaceColor ? applyStyle(gapText, { color: spaceColor }) : gapText);
|
|
@@ -305,47 +394,149 @@ export function grid(options, ...children) {
|
|
|
305
394
|
}
|
|
306
395
|
currentX += width + columnGap;
|
|
307
396
|
}
|
|
397
|
+
if (!anyRenderableChild) {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
308
400
|
// Handle multi-line: if any result is string[], expand grid vertically
|
|
309
401
|
const maxLines = Math.max(...results.map(r => Array.isArray(r) ? r.length : 1), 1);
|
|
310
402
|
if (maxLines === 1) {
|
|
311
|
-
//
|
|
403
|
+
// Special handling for spaceBetween with auto columns (CSS justify-content: space-between)
|
|
404
|
+
const hasAutoColumns = tracks.some(t => t.type === 'auto');
|
|
405
|
+
const allAuto = tracks.every(t => t.type === 'auto' || t.type === 'flex');
|
|
406
|
+
if (spaceBetween && hasAutoColumns && allAuto) {
|
|
407
|
+
// Special case: spaceBetween with auto columns (CSS justify-content: space-between)
|
|
408
|
+
// Collect all auto column contents (skip gap results in results array)
|
|
409
|
+
const autoContents = [];
|
|
410
|
+
let totalAutoWidth = 0;
|
|
411
|
+
let partIdx = 0;
|
|
412
|
+
// Debug: log what we're working with
|
|
413
|
+
if (process.env.DEBUG_GRID) {
|
|
414
|
+
console.log('DEBUG: spaceBetween with auto columns');
|
|
415
|
+
console.log('DEBUG: validChildren.length:', validChildren.length);
|
|
416
|
+
console.log('DEBUG: results.length:', results.length);
|
|
417
|
+
console.log('DEBUG: tracks:', tracks.map(t => t.type));
|
|
418
|
+
}
|
|
419
|
+
for (let i = 0; i < validChildren.length; i++) {
|
|
420
|
+
const track = tracks[i];
|
|
421
|
+
if (track.type === 'auto') {
|
|
422
|
+
if (process.env.DEBUG_GRID) {
|
|
423
|
+
console.log(`DEBUG: Processing auto column ${i}, partIdx=${partIdx}, results[partIdx]=`, results[partIdx]);
|
|
424
|
+
}
|
|
425
|
+
const result = results[partIdx];
|
|
426
|
+
partIdx++;
|
|
427
|
+
const content = result === null ? '' : (typeof result === 'string' ? result : '');
|
|
428
|
+
const plainContent = stripAnsi(content);
|
|
429
|
+
totalAutoWidth += plainContent.length;
|
|
430
|
+
autoContents.push(content);
|
|
431
|
+
if (process.env.DEBUG_GRID) {
|
|
432
|
+
console.log(`DEBUG: Collected auto column ${i}: "${plainContent}", totalAutoWidth=${totalAutoWidth}, autoContents.length=${autoContents.length}`);
|
|
433
|
+
}
|
|
434
|
+
// Skip gap result if present (spaceBetween adds gap results between columns)
|
|
435
|
+
// Only skip if this is not the last column and there's a gap result
|
|
436
|
+
if (i < validChildren.length - 1 && columnGap > 0 && partIdx < results.length) {
|
|
437
|
+
if (process.env.DEBUG_GRID) {
|
|
438
|
+
console.log(`DEBUG: Skipping gap result at partIdx=${partIdx}`);
|
|
439
|
+
}
|
|
440
|
+
partIdx++; // Skip the gap result
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
// Not an auto column, skip it
|
|
445
|
+
partIdx++;
|
|
446
|
+
if (i < validChildren.length - 1 && columnGap > 0 && partIdx < results.length) {
|
|
447
|
+
partIdx++; // Skip gap if present
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (process.env.DEBUG_GRID) {
|
|
452
|
+
console.log('DEBUG: Final autoContents.length:', autoContents.length);
|
|
453
|
+
console.log('DEBUG: Final totalAutoWidth:', totalAutoWidth);
|
|
454
|
+
}
|
|
455
|
+
// Calculate spaceBetween fill width
|
|
456
|
+
const spaceBetweenWidth = Math.max(0, ctx.availableWidth - totalAutoWidth);
|
|
457
|
+
const { char: spaceChar, color: spaceColor } = getSpaceBetweenChar(spaceBetween, 0);
|
|
458
|
+
const fillText = spaceChar.repeat(spaceBetweenWidth);
|
|
459
|
+
const spaceBetweenContent = spaceColor ? applyStyle(fillText, { color: spaceColor }) : fillText;
|
|
460
|
+
// Build line: first column + spaceBetween + remaining columns
|
|
461
|
+
const lineParts = [];
|
|
462
|
+
if (autoContents.length > 0) {
|
|
463
|
+
lineParts.push(autoContents[0]);
|
|
464
|
+
if (autoContents.length > 1) {
|
|
465
|
+
lineParts.push(spaceBetweenContent);
|
|
466
|
+
for (let i = 1; i < autoContents.length; i++) {
|
|
467
|
+
lineParts.push(autoContents[i]);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const line = lineParts.join('');
|
|
472
|
+
// Pad to full width to ensure right column is at the end
|
|
473
|
+
const plainLine = stripAnsi(line);
|
|
474
|
+
const paddedLine = plainLine.length < ctx.availableWidth
|
|
475
|
+
? line + ' '.repeat(ctx.availableWidth - plainLine.length)
|
|
476
|
+
: line;
|
|
477
|
+
return paddedLine;
|
|
478
|
+
}
|
|
479
|
+
// Standard rendering for other cases
|
|
312
480
|
const lineParts = [];
|
|
313
481
|
let partIdx = 0;
|
|
314
482
|
for (let i = 0; i < validChildren.length; i++) {
|
|
315
483
|
const result = results[partIdx];
|
|
316
484
|
partIdx++;
|
|
317
485
|
const columnWidth = actualWidths[i] ?? 0;
|
|
486
|
+
const track = tracks[i];
|
|
487
|
+
const isAuto = track?.type === 'auto';
|
|
488
|
+
const isFlex = track?.type === 'flex' || track?.type === 'minmax';
|
|
318
489
|
if (result === null) {
|
|
319
490
|
// Null result - pad to column width
|
|
320
|
-
|
|
491
|
+
// If spaceBetween is set, use it to fill empty columns (especially flex columns)
|
|
492
|
+
if (!isAuto && spaceBetween && columnWidth > 0) {
|
|
493
|
+
const { char: spaceChar, color: spaceColor } = getSpaceBetweenChar(spaceBetween, i);
|
|
494
|
+
const fillText = spaceChar.repeat(columnWidth);
|
|
495
|
+
lineParts.push(spaceColor ? applyStyle(fillText, { color: spaceColor }) : fillText);
|
|
496
|
+
}
|
|
497
|
+
else if (!isAuto) {
|
|
498
|
+
lineParts.push(' '.repeat(columnWidth));
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
lineParts.push('');
|
|
502
|
+
}
|
|
321
503
|
}
|
|
322
504
|
else {
|
|
323
505
|
const columnContent = typeof result === 'string' ? result : '';
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
506
|
+
if (isAuto) {
|
|
507
|
+
lineParts.push(columnContent);
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
const plainContent = stripAnsi(columnContent);
|
|
511
|
+
let paddedContent;
|
|
512
|
+
// If content is empty and this is a flex column with spaceBetween, fill it
|
|
513
|
+
if (plainContent.length === 0 && spaceBetween && columnWidth > 0 && isFlex) {
|
|
514
|
+
const { char: spaceChar, color: spaceColor } = getSpaceBetweenChar(spaceBetween, i);
|
|
515
|
+
const fillText = spaceChar.repeat(columnWidth);
|
|
516
|
+
paddedContent = spaceColor ? applyStyle(fillText, { color: spaceColor }) : fillText;
|
|
517
|
+
}
|
|
518
|
+
else if (plainContent.length === 0 && spaceBetween && columnWidth > 0) {
|
|
519
|
+
const { char: spaceChar, color: spaceColor } = getSpaceBetweenChar(spaceBetween, i);
|
|
520
|
+
const fillText = spaceChar.repeat(columnWidth);
|
|
521
|
+
paddedContent = spaceColor ? applyStyle(fillText, { color: spaceColor }) : fillText;
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
paddedContent = plainContent.length < columnWidth
|
|
525
|
+
? columnContent + ' '.repeat(columnWidth - plainContent.length)
|
|
526
|
+
: columnContent;
|
|
527
|
+
}
|
|
528
|
+
lineParts.push(paddedContent);
|
|
529
|
+
}
|
|
331
530
|
}
|
|
332
|
-
//
|
|
531
|
+
// Skip gap result if present (gap results are added to results array between columns)
|
|
532
|
+
// Then add gap to lineParts if not using spaceBetween
|
|
333
533
|
if (i < validChildren.length - 1 && columnGap > 0) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
: Array.isArray(spaceBetween)
|
|
338
|
-
? (spaceBetween[i] ?? spaceBetween[spaceBetween.length - 1])
|
|
339
|
-
: spaceBetween.char;
|
|
340
|
-
const spaceColor = typeof spaceBetween === 'object' && !Array.isArray(spaceBetween)
|
|
341
|
-
? spaceBetween.color
|
|
342
|
-
: undefined;
|
|
343
|
-
const gapText = spaceChar.repeat(columnGap);
|
|
344
|
-
lineParts.push(spaceColor ? applyStyle(gapText, { color: spaceColor }) : gapText);
|
|
345
|
-
partIdx++; // spaceBetween adds an extra result
|
|
534
|
+
// Skip the gap result in the results array
|
|
535
|
+
if (partIdx < results.length) {
|
|
536
|
+
partIdx++; // Skip the gap result
|
|
346
537
|
}
|
|
347
|
-
|
|
348
|
-
|
|
538
|
+
// Add gap to line (unless spaceBetween is set, which handles gaps differently)
|
|
539
|
+
if (!spaceBetween) {
|
|
349
540
|
lineParts.push(' '.repeat(columnGap));
|
|
350
541
|
}
|
|
351
542
|
}
|
|
@@ -367,9 +558,27 @@ export function grid(options, ...children) {
|
|
|
367
558
|
const result = results[partIdx];
|
|
368
559
|
partIdx++;
|
|
369
560
|
const columnWidth = actualWidths[i] ?? 0;
|
|
561
|
+
const track = tracks[i];
|
|
562
|
+
const isAuto = track?.type === 'auto';
|
|
563
|
+
const isFlex = track?.type === 'flex' || track?.type === 'minmax';
|
|
370
564
|
if (result === null) {
|
|
371
565
|
// Null result - pad to column width
|
|
372
|
-
|
|
566
|
+
// If spaceBetween is set, use it to fill empty columns (especially flex columns)
|
|
567
|
+
if (!isAuto && spaceBetween && columnWidth > 0) {
|
|
568
|
+
const { char: spaceChar, color: spaceColor } = getSpaceBetweenChar(spaceBetween, i);
|
|
569
|
+
const fillText = spaceChar.repeat(columnWidth);
|
|
570
|
+
lineParts.push(spaceColor ? applyStyle(fillText, { color: spaceColor }) : fillText);
|
|
571
|
+
}
|
|
572
|
+
else if (!isAuto) {
|
|
573
|
+
lineParts.push(' '.repeat(columnWidth));
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
lineParts.push('');
|
|
577
|
+
}
|
|
578
|
+
// Skip gap result if present
|
|
579
|
+
if (i < validChildren.length - 1 && columnGap > 0 && partIdx < results.length) {
|
|
580
|
+
partIdx++;
|
|
581
|
+
}
|
|
373
582
|
continue;
|
|
374
583
|
}
|
|
375
584
|
let columnContent;
|
|
@@ -381,28 +590,40 @@ export function grid(options, ...children) {
|
|
|
381
590
|
}
|
|
382
591
|
// CRITICAL: Pad each column to its allocated width
|
|
383
592
|
// This ensures flex columns actually fill their allocated space
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
lineParts.push(spaceColor ? applyStyle(gapText, { color: spaceColor }) : gapText);
|
|
402
|
-
partIdx++; // spaceBetween adds an extra result
|
|
593
|
+
// If spaceBetween is set and content is empty, fill with spaceBetween character
|
|
594
|
+
if (isAuto) {
|
|
595
|
+
lineParts.push(columnContent);
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
const plainContent = columnContent.replace(/\x1b\[[0-9;]*m/g, '');
|
|
599
|
+
let paddedContent;
|
|
600
|
+
// If content is empty and this is a flex column with spaceBetween, fill it
|
|
601
|
+
if (plainContent.length === 0 && spaceBetween && columnWidth > 0 && isFlex) {
|
|
602
|
+
const { char: spaceChar, color: spaceColor } = getSpaceBetweenChar(spaceBetween, i);
|
|
603
|
+
const fillText = spaceChar.repeat(columnWidth);
|
|
604
|
+
paddedContent = spaceColor ? applyStyle(fillText, { color: spaceColor }) : fillText;
|
|
605
|
+
}
|
|
606
|
+
else if (plainContent.length === 0 && spaceBetween && columnWidth > 0) {
|
|
607
|
+
const { char: spaceChar, color: spaceColor } = getSpaceBetweenChar(spaceBetween, i);
|
|
608
|
+
const fillText = spaceChar.repeat(columnWidth);
|
|
609
|
+
paddedContent = spaceColor ? applyStyle(fillText, { color: spaceColor }) : fillText;
|
|
403
610
|
}
|
|
404
611
|
else {
|
|
405
|
-
|
|
612
|
+
paddedContent = plainContent.length < columnWidth
|
|
613
|
+
? columnContent + ' '.repeat(columnWidth - plainContent.length)
|
|
614
|
+
: columnContent;
|
|
615
|
+
}
|
|
616
|
+
lineParts.push(paddedContent);
|
|
617
|
+
}
|
|
618
|
+
// Skip gap result if present (gap results are added to results array between columns)
|
|
619
|
+
// Then add gap to lineParts if not using spaceBetween
|
|
620
|
+
if (i < validChildren.length - 1 && columnGap > 0) {
|
|
621
|
+
// Skip the gap result in the results array
|
|
622
|
+
if (partIdx < results.length) {
|
|
623
|
+
partIdx++; // Skip the gap result
|
|
624
|
+
}
|
|
625
|
+
// Add gap to line (unless spaceBetween is set, which handles gaps differently)
|
|
626
|
+
if (!spaceBetween) {
|
|
406
627
|
lineParts.push(' '.repeat(columnGap));
|
|
407
628
|
}
|
|
408
629
|
}
|