kodevu 0.1.17 → 0.1.19

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 +90 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodevu",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
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": {
@@ -1,6 +1,8 @@
1
1
  import readline from "node:readline";
2
2
 
3
3
  const SPINNER_FRAMES = ["|", "/", "-", "\\"];
4
+ const ELLIPSIS = "...";
5
+ const MIN_DYNAMIC_WIDTH = 60;
4
6
 
5
7
  function clampProgress(value) {
6
8
  if (!Number.isFinite(value)) {
@@ -10,12 +12,53 @@ function clampProgress(value) {
10
12
  return Math.max(0, Math.min(1, value));
11
13
  }
12
14
 
15
+ function getCharacterWidth(character) {
16
+ const codePoint = character.codePointAt(0);
17
+
18
+ if (!codePoint || codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f)) {
19
+ return 0;
20
+ }
21
+
22
+ if (
23
+ codePoint >= 0x1100 &&
24
+ (
25
+ codePoint <= 0x115f ||
26
+ codePoint === 0x2329 ||
27
+ codePoint === 0x232a ||
28
+ (codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||
29
+ (codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
30
+ (codePoint >= 0xf900 && codePoint <= 0xfaff) ||
31
+ (codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
32
+ (codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
33
+ (codePoint >= 0xff00 && codePoint <= 0xff60) ||
34
+ (codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
35
+ (codePoint >= 0x1f300 && codePoint <= 0x1f64f) ||
36
+ (codePoint >= 0x1f900 && codePoint <= 0x1f9ff) ||
37
+ (codePoint >= 0x20000 && codePoint <= 0x3fffd)
38
+ )
39
+ ) {
40
+ return 2;
41
+ }
42
+
43
+ return 1;
44
+ }
45
+
46
+ function getDisplayWidth(text) {
47
+ let width = 0;
48
+
49
+ for (const character of text) {
50
+ width += getCharacterWidth(character);
51
+ }
52
+
53
+ return width;
54
+ }
55
+
13
56
  function truncateLine(line, maxWidth) {
14
57
  if (!Number.isFinite(maxWidth) || maxWidth <= 0) {
15
58
  return "";
16
59
  }
17
60
 
18
- if (line.length <= maxWidth) {
61
+ if (getDisplayWidth(line) <= maxWidth) {
19
62
  return line;
20
63
  }
21
64
 
@@ -23,7 +66,22 @@ function truncateLine(line, maxWidth) {
23
66
  return ".".repeat(maxWidth);
24
67
  }
25
68
 
26
- return `${line.slice(0, maxWidth - 3)}...`;
69
+ const targetWidth = maxWidth - getDisplayWidth(ELLIPSIS);
70
+ let result = "";
71
+ let width = 0;
72
+
73
+ for (const character of line) {
74
+ const nextWidth = width + getCharacterWidth(character);
75
+
76
+ if (nextWidth > targetWidth) {
77
+ break;
78
+ }
79
+
80
+ result += character;
81
+ width = nextWidth;
82
+ }
83
+
84
+ return `${result}${ELLIPSIS}`;
27
85
  }
28
86
 
29
87
  class ProgressItem {
@@ -40,7 +98,7 @@ class ProgressItem {
40
98
  this.active = true;
41
99
  this.stage = stage;
42
100
 
43
- if (!this.display.enabled) {
101
+ if (!this.display.canRenderDynamically()) {
44
102
  this.writeFallback(`... ${this.label}: ${stage}`);
45
103
  return;
46
104
  }
@@ -56,11 +114,12 @@ class ProgressItem {
56
114
  this.stage = stage;
57
115
  }
58
116
 
59
- if (!this.display.enabled) {
117
+ if (!this.display.canRenderDynamically()) {
60
118
  this.writeFallback(`... ${this.label}: ${this.stage}`);
61
119
  return;
62
120
  }
63
121
 
122
+ this.display.start();
64
123
  this.display.activate(this);
65
124
  }
66
125
 
@@ -117,6 +176,7 @@ export class ProgressDisplay {
117
176
  this.items = [];
118
177
  this.currentItem = null;
119
178
  this.statusVisible = false;
179
+ this.lastStatusWidth = 0;
120
180
  this.resizeAttached = false;
121
181
  this.handleResize = this.handleResize.bind(this);
122
182
  }
@@ -158,7 +218,7 @@ export class ProgressDisplay {
158
218
  }
159
219
 
160
220
  log(message) {
161
- if (!this.enabled) {
221
+ if (!this.canRenderDynamically()) {
162
222
  this.stream.write(`${message}\n`);
163
223
  return;
164
224
  }
@@ -169,7 +229,7 @@ export class ProgressDisplay {
169
229
  }
170
230
 
171
231
  writeStaticLine(message) {
172
- if (!this.enabled) {
232
+ if (!this.canRenderDynamically()) {
173
233
  this.stream.write(`${message}\n`);
174
234
  return;
175
235
  }
@@ -180,13 +240,16 @@ export class ProgressDisplay {
180
240
  }
181
241
 
182
242
  render() {
183
- if (!this.enabled || !this.currentItem?.active) {
243
+ if (!this.canRenderDynamically() || !this.currentItem?.active) {
244
+ this.clearStatusLine();
184
245
  return;
185
246
  }
186
247
 
187
248
  this.clearStatusLine();
188
- this.stream.write(this.currentItem.renderLine(this.frameIndex));
249
+ const line = this.currentItem.renderLine(this.frameIndex);
250
+ this.stream.write(line);
189
251
  this.statusVisible = true;
252
+ this.lastStatusWidth = getDisplayWidth(line);
190
253
  }
191
254
 
192
255
  clearStatusLine() {
@@ -194,9 +257,23 @@ export class ProgressDisplay {
194
257
  return;
195
258
  }
196
259
 
197
- readline.clearLine(this.stream, 0);
260
+ const rows = Math.max(1, Math.ceil(this.lastStatusWidth / Math.max(this.stream.columns || 1, 1)));
261
+
262
+ readline.moveCursor(this.stream, 0, -Math.max(rows - 1, 0));
263
+
264
+ for (let index = 0; index < rows; index += 1) {
265
+ readline.clearLine(this.stream, 0);
266
+ readline.cursorTo(this.stream, 0);
267
+
268
+ if (index < rows - 1) {
269
+ readline.moveCursor(this.stream, 0, 1);
270
+ }
271
+ }
272
+
273
+ readline.moveCursor(this.stream, 0, -Math.max(rows - 1, 0));
198
274
  readline.cursorTo(this.stream, 0);
199
275
  this.statusVisible = false;
276
+ this.lastStatusWidth = 0;
200
277
  }
201
278
 
202
279
  stopIfIdle() {
@@ -217,6 +294,10 @@ export class ProgressDisplay {
217
294
  return Math.max(columns - 1, 1);
218
295
  }
219
296
 
297
+ canRenderDynamically() {
298
+ return this.enabled && this.getAvailableWidth() >= MIN_DYNAMIC_WIDTH;
299
+ }
300
+
220
301
  handleResize() {
221
302
  if (!this.enabled) {
222
303
  return;