@f5xc-salesdemos/pi-tui 19.31.0 → 19.32.1

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5xc-salesdemos/pi-tui",
4
- "version": "19.31.0",
4
+ "version": "19.32.1",
5
5
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
6
6
  "homepage": "https://github.com/f5xc-salesdemos/xcsh",
7
7
  "author": "Can Boluk",
@@ -37,8 +37,8 @@
37
37
  "fmt": "biome format --write ."
38
38
  },
39
39
  "dependencies": {
40
- "@f5xc-salesdemos/pi-natives": "19.31.0",
41
- "@f5xc-salesdemos/pi-utils": "19.31.0",
40
+ "@f5xc-salesdemos/pi-natives": "19.32.1",
41
+ "@f5xc-salesdemos/pi-utils": "19.32.1",
42
42
  "marked": "^17.0"
43
43
  },
44
44
  "devDependencies": {
@@ -398,9 +398,12 @@ export class StdinBuffer extends EventEmitter<StdinBufferEventMap> {
398
398
  }
399
399
 
400
400
  // Remember a flushed incomplete escape so a continuation arriving in
401
- // the next read can be reassembled (see process()).
401
+ // the next read can be reassembled (see process()). Only a genuinely
402
+ // incomplete trailing fragment qualifies — a complete sequence (e.g.
403
+ // one peeled out of a de-blobbed probe response) is not a keypress
404
+ // awaiting its tail and must not glue onto the next read.
402
405
  const last = flushed[flushed.length - 1];
403
- if (last !== undefined && last.startsWith(ESC)) {
406
+ if (last !== undefined && last.startsWith(ESC) && isCompleteSequence(last) === "incomplete") {
404
407
  this.#pendingEscape = last;
405
408
  this.#pendingEscapeAt = Date.now();
406
409
  }
@@ -418,9 +421,42 @@ export class StdinBuffer extends EventEmitter<StdinBufferEventMap> {
418
421
  return [];
419
422
  }
420
423
 
421
- const sequences = [this.#buffer];
424
+ // Re-split the abandoned buffer into individual complete sequences instead
425
+ // of emitting it whole. Under render load a terminal *response* can arrive
426
+ // before its terminator (notably an OSC 11 reply missing its ST); the
427
+ // tokenizer then treats it as incomplete and, while scanning for the
428
+ // terminator, absorbs any following replies (DA1, Kitty) into one fragment.
429
+ // Emitting that as a single event defeats the terminal's anchored
430
+ // single-sequence matchers, so the whole blob leaks into the editor as text
431
+ // (#1446). Split an unterminated string sequence (OSC/DCS/APC) at an
432
+ // embedded ESC that introduces a new sequence so each reply is recognized
433
+ // and swallowed on its own.
434
+ const out: string[] = [];
435
+ let work = this.#buffer;
422
436
  this.#buffer = "";
423
- return sequences;
437
+
438
+ while (work.length > 0) {
439
+ const { sequences, remainder } = extractCompleteSequences(work);
440
+ out.push(...sequences);
441
+ if (remainder.length === 0) {
442
+ break;
443
+ }
444
+ // The remainder is an incomplete ESC-led fragment. If it absorbed a
445
+ // later sequence (an embedded ESC beyond the introducer), peel off the
446
+ // abandoned prefix and re-extract from the embedded boundary; otherwise
447
+ // it is a genuine trailing fragment (e.g. a lone Escape keypress or a
448
+ // half-arrived CSI) and is emitted as-is.
449
+ const embeddedEsc = remainder.indexOf(ESC, 1);
450
+ if (embeddedEsc > 0) {
451
+ out.push(remainder.slice(0, embeddedEsc));
452
+ work = remainder.slice(embeddedEsc);
453
+ } else {
454
+ out.push(remainder);
455
+ break;
456
+ }
457
+ }
458
+
459
+ return out;
424
460
  }
425
461
 
426
462
  clear(): void {