@oxecli/oxe 1.0.77 → 1.0.78

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
@@ -578,11 +578,12 @@ export class InferenceEngine {
578
578
  this.workActive = false;
579
579
  const started = toolCallLabel(c.name, c.arguments);
580
580
  const toolSpinner = new Spinner();
581
- toolSpinner.start(started);
581
+ toolSpinner.startDots("Running");
582
582
  const rawResult = await this.runTool(c.name, c.arguments);
583
- toolSpinner.stop();
584
- if (this.interrupted)
583
+ if (this.interrupted) {
584
+ toolSpinner.stop();
585
585
  throw new Error("interrupt");
586
+ }
586
587
  const failed = toolOutputFailed(c.name, rawResult);
587
588
  const action = formatToolResult(rawResult, failed);
588
589
  story.push({
@@ -591,7 +592,10 @@ export class InferenceEngine {
591
592
  text: action,
592
593
  status: failed ? "failed" : "ok",
593
594
  });
594
- process.stdout.write(`${started}\n${action}\n\n`);
595
+ // Replace the "Running..." line in place with the tool label so the
596
+ // transition is seamless, then print the result beneath it.
597
+ toolSpinner.replaceWith(started);
598
+ process.stdout.write(`${action}\n\n`);
595
599
  // Any diff the tool produced is flushed only now, after the spinner
596
600
  // has stopped, so the box renders cleanly below the action line.
597
601
  flushPendingDiffOutput();
package/dist/tools.js CHANGED
@@ -133,14 +133,17 @@ function displayDiff(pathName, oldContent, newContent) {
133
133
  const num = v.kind === "+" ? v.newNum : v.oldNum;
134
134
  const numStr = num ? String(num).padStart(numW, " ") : gap;
135
135
  if (v.kind === "+" || v.kind === "-") {
136
- const bg = v.kind === "+" ? "\x1b[42m" : "\x1b[41m";
136
+ const bg = v.kind === "+" ? "\x1b[48;2;2;40;0m" : "\x1b[48;2;61;1;0m";
137
137
  const sign = v.kind === "+" ? "+" : "-";
138
138
  const body = truncateStyled(v.body, bodyMax);
139
139
  const fill = Math.max(0, termW - numW - 4 - plainLen(body));
140
140
  pendingDiffOutput.push(`${bg}\x1b[97m${numStr} ${sign} ${body}${" ".repeat(fill)}\x1b[0m`);
141
141
  }
142
142
  else {
143
- const body = truncateStyled(v.kind === "\\" ? v.body : highlightCodeLine(v.body), bodyMax);
143
+ const raw = v.kind === "\\" ? v.body : highlightCodeLine(v.body);
144
+ // highlightCodeLine resets to default after each token; re-apply the dim
145
+ // gray base so plain text stays dim and only tokens show color.
146
+ const body = truncateStyled(raw.replace(/\x1b\[0m/g, "\x1b[0m\x1b[90m"), bodyMax);
144
147
  pendingDiffOutput.push(`\x1b[90m${numStr} ${body}\x1b[0m`);
145
148
  }
146
149
  }
package/dist/ui.js CHANGED
@@ -512,6 +512,7 @@ export function showCursor() {
512
512
  }
513
513
  }
514
514
  const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
515
+ const DOT_FRAMES = [". ", ".. ", "..."];
515
516
  export class Spinner {
516
517
  timer = null;
517
518
  frame = 0;
@@ -520,6 +521,8 @@ export class Spinner {
520
521
  enabled;
521
522
  lastRendered = "";
522
523
  lastLen = 0;
524
+ dotsText = "";
525
+ dotsFrame = 0;
523
526
  constructor(enabled = true) {
524
527
  this.enabled = enabled;
525
528
  }
@@ -547,6 +550,53 @@ export class Spinner {
547
550
  }, 80);
548
551
  this.draw();
549
552
  }
553
+ /** Dots-mode label: renders `Running` + a fixed 3-cell animated dots field.
554
+ * Each tick rewrites ONLY the dots cells via a cursor jump, so the rest of
555
+ * the line never flickers. Swap in the final label with replaceWith(). */
556
+ startDots(text) {
557
+ if (!this.enabled)
558
+ return;
559
+ if (this.timer)
560
+ clearInterval(this.timer);
561
+ this.dotsText = text;
562
+ this.dotsFrame = 0;
563
+ this.rows = 1;
564
+ this.lastRendered = "";
565
+ this.lastLen = text.length + 3;
566
+ this.writeDots(true);
567
+ this.timer = setInterval(() => {
568
+ this.dotsFrame = (this.dotsFrame + 1) % DOT_FRAMES.length;
569
+ this.writeDots(false);
570
+ }, 300);
571
+ }
572
+ writeDots(initial) {
573
+ const label = `\x1b[1;36m${this.dotsText}\x1b[0m`;
574
+ const dots = `\x1b[1;36m${DOT_FRAMES[this.dotsFrame]}\x1b[0m`;
575
+ if (initial) {
576
+ process.stdout.write("\r" + label + dots + "\r");
577
+ }
578
+ else {
579
+ // Jump to the dots column (1-based) and rewrite only the 3-cell field.
580
+ process.stdout.write(`\x1b[${this.dotsText.length + 1}G` + dots + "\r");
581
+ }
582
+ }
583
+ /** Overwrite the dots line in place with `text` and advance to the next
584
+ * line, without erasing (so there is no flash). */
585
+ replaceWith(text) {
586
+ if (this.timer) {
587
+ clearInterval(this.timer);
588
+ this.timer = null;
589
+ }
590
+ if (!this.enabled) {
591
+ process.stdout.write(`${text}\n`);
592
+ return;
593
+ }
594
+ const plain = stripAnsi(text);
595
+ const pad = Math.max(0, this.lastLen - plain.length);
596
+ process.stdout.write("\r" + text + " ".repeat(pad) + "\r\n");
597
+ this.rows = 0;
598
+ this.lastLen = plain.length;
599
+ }
550
600
  update(text) {
551
601
  if (this.timer) {
552
602
  this.render = () => text;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxecli/oxe",
3
- "version": "1.0.77",
3
+ "version": "1.0.78",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },