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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/progress-ui.js +70 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodevu",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "type": "module",
5
5
  "description": "Poll SVN revisions or Git commits, send each change diff to a reviewer CLI, and write configurable review reports.",
6
6
  "bin": {
@@ -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
- const bar = buildBar(this.progress, this.barWidth);
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
- return `${spinner} ${this.label} ${bar} ${pct}% ${this.stage}`;
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
  }