@oxecli/oxe 1.0.62 → 1.0.64

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/dist/ui.js +53 -14
  2. package/package.json +1 -1
package/dist/ui.js CHANGED
@@ -529,6 +529,7 @@ export class Spinner {
529
529
  render = null;
530
530
  enabled;
531
531
  lastRendered = "";
532
+ lastLen = 0;
532
533
  constructor(enabled = true) {
533
534
  this.enabled = enabled;
534
535
  }
@@ -549,6 +550,7 @@ export class Spinner {
549
550
  this.frame = 0;
550
551
  this.rows = 0;
551
552
  this.lastRendered = "";
553
+ this.lastLen = 0;
552
554
  this.timer = setInterval(() => {
553
555
  this.frame++;
554
556
  this.draw();
@@ -570,21 +572,35 @@ export class Spinner {
570
572
  return;
571
573
  this.lastRendered = rendered;
572
574
  const newRows = Math.max(1, displayRows(rendered));
573
- const clearRows = Math.max(this.rows, newRows, 1);
574
- for (let i = 0; i < clearRows; i++) {
575
- process.stdout.write("\r\x1b[2K");
576
- if (i < clearRows - 1)
577
- process.stdout.write("\n");
575
+ if (newRows === 1) {
576
+ // Overwrite in place: NEVER erase the whole line (the \x1b[2K erase-to-blank
577
+ // followed by a full redraw is what caused the flicker/flash). We just
578
+ // re-emit from the start; identical glyphs stay put, only the changed
579
+ // cells (spinner frame, trailing timer) actually change on screen. Pad
580
+ // with spaces so a shorter line clears any previously longer text.
581
+ const plain = stripAnsi(rendered);
582
+ const pad = Math.max(0, this.lastLen - plain.length);
583
+ process.stdout.write("\r" + rendered + " ".repeat(pad) + "\r");
584
+ this.lastLen = plain.length;
585
+ }
586
+ else {
587
+ // Multi-row wrapped text: fall back to an erase-based redraw.
588
+ const clearRows = Math.max(this.rows, newRows, 1);
589
+ for (let i = 0; i < clearRows; i++) {
590
+ process.stdout.write("\r\x1b[2K");
591
+ if (i < clearRows - 1)
592
+ process.stdout.write("\n");
593
+ }
594
+ if (clearRows > 1)
595
+ process.stdout.write(`\x1b[${clearRows - 1}A\r`);
596
+ else
597
+ process.stdout.write("\r");
598
+ process.stdout.write(rendered);
599
+ if (newRows > 1)
600
+ process.stdout.write(`\x1b[${newRows - 1}A\r`);
601
+ else
602
+ process.stdout.write("\r");
578
603
  }
579
- if (clearRows > 1)
580
- process.stdout.write(`\x1b[${clearRows - 1}A\r`);
581
- else
582
- process.stdout.write("\r");
583
- process.stdout.write(rendered);
584
- if (newRows > 1)
585
- process.stdout.write(`\x1b[${newRows - 1}A\r`);
586
- else
587
- process.stdout.write("\r");
588
604
  this.rows = newRows;
589
605
  }
590
606
  stop() {
@@ -949,6 +965,10 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
949
965
  let bracketedPasteBuffer = "";
950
966
  let pasteBurst = false;
951
967
  let pasteBurstTimer = null;
968
+ // Accumulator for modifier-prefixed CSI sequences (e.g. Shift+Enter sent as
969
+ // `\x1b[27;2;13~`), which Node's readline splits into several keypress
970
+ // events instead of one, so they must be reassembled here.
971
+ let modSeq = "";
952
972
  readline.emitKeypressEvents(process.stdin);
953
973
  if (process.stdin.isTTY) {
954
974
  process.stdin.setRawMode(true);
@@ -1297,6 +1317,25 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
1297
1317
  }, 500);
1298
1318
  };
1299
1319
  const onKeypress = (str, key) => {
1320
+ // Reassemble a modifier-prefixed CSI sequence that readline splits up,
1321
+ // e.g. Shift+Enter arriving as `\x1b[27;2;` + "1" + "3" + "~".
1322
+ if (key?.sequence === "\x1b[27;2;") {
1323
+ modSeq = "\x1b[27;2;";
1324
+ return;
1325
+ }
1326
+ if (modSeq) {
1327
+ modSeq += str || key?.sequence || "";
1328
+ if (modSeq.endsWith("~")) {
1329
+ const full = modSeq;
1330
+ modSeq = "";
1331
+ // `\x1b[27;2;13~` is Shift+Enter (modifier 2 = shift, key 13 = Enter).
1332
+ if (full === "\x1b[27;2;13~") {
1333
+ insert("\n");
1334
+ repaint();
1335
+ }
1336
+ }
1337
+ return;
1338
+ }
1300
1339
  if (consumeBracketedPaste(str, key))
1301
1340
  return;
1302
1341
  if (pasteBurst && str && str.length === 1 && !key?.ctrl && !key?.meta) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxecli/oxe",
3
- "version": "1.0.62",
3
+ "version": "1.0.64",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },