kodevu 0.1.14 → 0.1.15
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/package.json +1 -1
- package/src/progress-ui.js +70 -8
package/package.json
CHANGED
package/src/progress-ui.js
CHANGED
|
@@ -2,6 +2,7 @@ import readline from "node:readline";
|
|
|
2
2
|
|
|
3
3
|
const SPINNER_FRAMES = ["|", "/", "-", "\\"];
|
|
4
4
|
const DEFAULT_BAR_WIDTH = 24;
|
|
5
|
+
const MIN_BAR_WIDTH = 8;
|
|
5
6
|
|
|
6
7
|
function clampProgress(value) {
|
|
7
8
|
if (!Number.isFinite(value)) {
|
|
@@ -26,6 +27,22 @@ function buildBar(progress, width) {
|
|
|
26
27
|
return `[${"=".repeat(Math.max(filled - 1, 0))}>${" ".repeat(width - filled)}]`;
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
function truncateLine(line, maxWidth) {
|
|
31
|
+
if (!Number.isFinite(maxWidth) || maxWidth <= 0) {
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (line.length <= maxWidth) {
|
|
36
|
+
return line;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (maxWidth <= 3) {
|
|
40
|
+
return ".".repeat(maxWidth);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return `${line.slice(0, maxWidth - 3)}...`;
|
|
44
|
+
}
|
|
45
|
+
|
|
29
46
|
class ProgressItem {
|
|
30
47
|
constructor(display, label, options = {}) {
|
|
31
48
|
this.display = display;
|
|
@@ -81,18 +98,27 @@ class ProgressItem {
|
|
|
81
98
|
this.progress = clampProgress(progress);
|
|
82
99
|
this.active = false;
|
|
83
100
|
this.display.stopIfIdle();
|
|
84
|
-
this.display.writeStaticLine(
|
|
85
|
-
`${prefix} ${this.label} ${buildBar(this.progress, this.barWidth)} ${Math.round(this.progress * 100)
|
|
86
|
-
.toString()
|
|
87
|
-
.padStart(3, " ")}% ${message}`
|
|
88
|
-
);
|
|
101
|
+
this.display.writeStaticLine(this.buildLine(prefix, message));
|
|
89
102
|
}
|
|
90
103
|
|
|
91
104
|
renderLine(frameIndex) {
|
|
92
105
|
const spinner = SPINNER_FRAMES[frameIndex];
|
|
93
|
-
|
|
106
|
+
return this.buildLine(spinner, this.stage);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
buildLine(prefix, suffix) {
|
|
110
|
+
const availableWidth = this.display.getAvailableWidth();
|
|
94
111
|
const pct = `${Math.round(this.progress * 100)}`.padStart(3, " ");
|
|
95
|
-
|
|
112
|
+
const reservedWidth = prefix.length + this.label.length + pct.length + 6;
|
|
113
|
+
const dynamicBarWidth = Math.min(
|
|
114
|
+
this.barWidth,
|
|
115
|
+
Math.max(MIN_BAR_WIDTH, availableWidth - reservedWidth - (suffix ? suffix.length + 1 : 0))
|
|
116
|
+
);
|
|
117
|
+
const bar = buildBar(this.progress, dynamicBarWidth);
|
|
118
|
+
return truncateLine(
|
|
119
|
+
`${prefix} ${this.label} ${bar} ${pct}%${suffix ? ` ${suffix}` : ""}`,
|
|
120
|
+
availableWidth
|
|
121
|
+
);
|
|
96
122
|
}
|
|
97
123
|
|
|
98
124
|
writeFallback(line) {
|
|
@@ -113,6 +139,7 @@ export class ProgressDisplay {
|
|
|
113
139
|
this.timer = null;
|
|
114
140
|
this.items = [];
|
|
115
141
|
this.renderedLineCount = 0;
|
|
142
|
+
this.handleResize = this.handleResize.bind(this);
|
|
116
143
|
}
|
|
117
144
|
|
|
118
145
|
createItem(label, options = {}) {
|
|
@@ -126,6 +153,8 @@ export class ProgressDisplay {
|
|
|
126
153
|
return;
|
|
127
154
|
}
|
|
128
155
|
|
|
156
|
+
this.attachResizeHandler();
|
|
157
|
+
|
|
129
158
|
this.timer = setInterval(() => {
|
|
130
159
|
this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;
|
|
131
160
|
this.render();
|
|
@@ -150,7 +179,7 @@ export class ProgressDisplay {
|
|
|
150
179
|
}
|
|
151
180
|
|
|
152
181
|
this.clearRender();
|
|
153
|
-
this.stream.write(`${message}\n`);
|
|
182
|
+
this.stream.write(`${truncateLine(message, this.getAvailableWidth())}\n`);
|
|
154
183
|
this.render();
|
|
155
184
|
}
|
|
156
185
|
|
|
@@ -202,5 +231,38 @@ export class ProgressDisplay {
|
|
|
202
231
|
clearInterval(this.timer);
|
|
203
232
|
this.timer = null;
|
|
204
233
|
}
|
|
234
|
+
|
|
235
|
+
this.detachResizeHandler();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
getAvailableWidth() {
|
|
239
|
+
const columns = this.stream.columns || 80;
|
|
240
|
+
return Math.max(columns - 1, 20);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
handleResize() {
|
|
244
|
+
if (!this.enabled) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
this.render();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
attachResizeHandler() {
|
|
252
|
+
if (typeof this.stream.on !== "function" || this.resizeAttached) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
this.stream.on("resize", this.handleResize);
|
|
257
|
+
this.resizeAttached = true;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
detachResizeHandler() {
|
|
261
|
+
if (typeof this.stream.off !== "function" || !this.resizeAttached) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
this.stream.off("resize", this.handleResize);
|
|
266
|
+
this.resizeAttached = false;
|
|
205
267
|
}
|
|
206
268
|
}
|