@oxecli/oxe 1.0.108 → 1.0.109

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/dist/engine.js CHANGED
@@ -586,11 +586,11 @@ export class InferenceEngine {
586
586
  this.queryCalledTool = true;
587
587
  const started = toolCallLabel(c.name, c.arguments);
588
588
  // Print the tool label the instant the command starts, then show a
589
- // "╰─ Working..." dots line that swaps to the real result in place.
589
+ // "╰─ Running..." dots line that swaps to the real result in place.
590
590
  dockAppendContent(`${started}\n`);
591
591
  dockTransientStart("flush");
592
592
  const toolSpinner = new Spinner();
593
- toolSpinner.startDots("\x1b[90m╰─\x1b[0m Working");
593
+ toolSpinner.startDots("\x1b[90m╰─\x1b[0m Running");
594
594
  const rawResult = await this.runTool(c.name, c.arguments, this.activeAbort?.signal);
595
595
  // A tool call was made, which interrupts any current working phase,
596
596
  // so the next agent iteration may commit a fresh "Worked for …" block.
@@ -609,7 +609,7 @@ export class InferenceEngine {
609
609
  status: failed ? "failed" : "ok",
610
610
  diff,
611
611
  });
612
- // Swap the "╰─ Working..." line for the real result. If the tool
612
+ // Swap the "╰─ Running..." line for the real result. If the tool
613
613
  // produced a diff it renders directly below the action line with no
614
614
  // gap; otherwise the dock gap separates the action from what follows.
615
615
  toolSpinner.stop();
package/dist/ui.js CHANGED
@@ -117,7 +117,7 @@ export function terminalWidth() {
117
117
  return process.stdout.columns || 80;
118
118
  }
119
119
  /** Minimum width for boxes/cards so they don't collapse to nothing. */
120
- const MIN_BOX_WIDTH = 20;
120
+ const MIN_BOX_WIDTH = 40;
121
121
  /** Horizontal margin reserved on each side of a box/card from the terminal edge. */
122
122
  const BOX_MARGIN = 4;
123
123
  /**
@@ -198,55 +198,62 @@ function wrapStyledToWidth(line, width) {
198
198
  pending = "";
199
199
  }
200
200
  }
201
- const out = [];
202
- let cur = "";
201
+ const render = (atoms) => atoms.map((a) => a.pre + a.ch).join("");
202
+ const rows = [];
203
+ let cur = [];
203
204
  let curLen = 0;
204
- let word = [];
205
- const emitChunk = (chunk, prependSpace) => {
206
- const add = (prependSpace && curLen > 0 ? 1 : 0) + chunk.length;
207
- if (curLen + add > width)
208
- return false;
209
- if (prependSpace && curLen > 0) {
210
- cur += " ";
205
+ // Index (in `cur`) of the whitespace atom that is a valid break point, or -1.
206
+ let breakAt = -1;
207
+ // The last SGR open code currently in effect, so a word split mid-way can
208
+ // re-apply the style on its continuation.
209
+ let activeStyle = "";
210
+ for (const a of units) {
211
+ // Track the active style from this glyph's ANSI prefix.
212
+ if (a.pre) {
213
+ const sgr = a.pre.match(/\x1b\[[0-9;]*m/g);
214
+ if (sgr && sgr.length) {
215
+ const last = sgr[sgr.length - 1];
216
+ activeStyle = last === "\x1b[0m" ? "" : last;
217
+ }
218
+ }
219
+ const isSpace = /\s/.test(a.ch);
220
+ if (isSpace) {
221
+ cur.push(a);
211
222
  curLen++;
223
+ breakAt = cur.length - 1;
224
+ continue;
212
225
  }
213
- for (const u of chunk)
214
- cur += u.pre + u.ch;
215
- curLen += chunk.length;
216
- return true;
217
- };
218
- const flushWord = () => {
219
- if (!word.length)
220
- return;
221
- const wlen = word.length;
222
- // Split overlong words into <= width chunks so nothing is ever cut off.
223
- const chunks = [];
224
- if (wlen <= width)
225
- chunks.push(word);
226
- else
227
- for (let i = 0; i < wlen; i += width)
228
- chunks.push(word.slice(i, i + width));
229
- for (let ci = 0; ci < chunks.length; ci++) {
230
- const chunk = chunks[ci];
231
- if (!emitChunk(chunk, ci === 0)) {
232
- out.push(cur);
233
- cur = "";
226
+ if (curLen + 1 > width) {
227
+ if (breakAt >= 0) {
228
+ // Break after the whitespace at breakAt: keep the whitespace's ANSI
229
+ // prefix (often a reset) so style never leaks onto the continuation.
230
+ const space = cur[breakAt];
231
+ const lead = space.pre; // e.g. \x1b[0m — applied at start of next row
232
+ const kept = cur.slice(breakAt + 1);
233
+ rows.push(cur.slice(0, breakAt));
234
+ cur = [{ ch: "", pre: lead }, ...kept];
235
+ curLen = kept.length;
236
+ breakAt = -1;
237
+ }
238
+ else if (cur.length) {
239
+ // No whitespace break point: split the word mid-way, re-applying the
240
+ // active style on the continuation so a styled word keeps its style.
241
+ rows.push(cur);
242
+ const lead = activeStyle ? activeStyle : "";
243
+ cur = lead ? [{ ch: "", pre: lead }] : [];
234
244
  curLen = 0;
235
- emitChunk(chunk, false);
236
245
  }
237
246
  }
238
- word = [];
239
- };
240
- for (const u of units) {
241
- if (/\s/.test(u.ch))
242
- flushWord();
243
- else
244
- word.push(u);
247
+ cur.push(a);
248
+ curLen++;
245
249
  }
246
- flushWord();
247
- if (cur)
248
- out.push(cur);
249
- return out.length ? out : [line];
250
+ if (cur.length)
251
+ rows.push(cur);
252
+ // Emit each row, ending with a reset so no style bleeds across rows.
253
+ return rows.map((r) => {
254
+ const rendered = render(r);
255
+ return rendered + (plainLen(rendered) ? "\x1b[0m" : "");
256
+ });
250
257
  }
251
258
  // ---------------------------------------------------------------------------
252
259
  // Markup tag -> ANSI
@@ -948,7 +955,7 @@ export function showCursor() {
948
955
  }
949
956
  }
950
957
  const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
951
- const DOT_FRAMES = [". ", ".. ", "..."];
958
+ const DOT_FRAMES = [".", "..", "..."];
952
959
  export class Spinner {
953
960
  timer = null;
954
961
  frame = 0;
@@ -1008,12 +1015,15 @@ export class Spinner {
1008
1015
  }
1009
1016
  writeDots(initial) {
1010
1017
  const dots = DOT_FRAMES[this.dotsFrame];
1018
+ // Always emit a fixed 3-cell field so packed dots (., .., ...) animate
1019
+ // without leaving residue when the frame shrinks (e.g. ... -> .).
1020
+ const padded = dots.padEnd(3, " ");
1011
1021
  if (initial) {
1012
- process.stdout.write("\r" + this.dotsText + dots + "\r");
1022
+ process.stdout.write("\r" + this.dotsText + padded + "\r");
1013
1023
  }
1014
1024
  else {
1015
1025
  // Jump to the dots column (1-based) and rewrite only the 3-cell field.
1016
- process.stdout.write(`\x1b[${plainLen(this.dotsText) + 1}G` + dots + "\r");
1026
+ process.stdout.write(`\x1b[${plainLen(this.dotsText) + 1}G` + padded + "\r");
1017
1027
  }
1018
1028
  }
1019
1029
  /** Overwrite the dots line in place with `text` and advance to the next
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxecli/oxe",
3
- "version": "1.0.108",
3
+ "version": "1.0.109",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },