@oh-my-pi/pi-tui 18.2.8 → 18.2.9

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/src/utils.ts CHANGED
@@ -384,6 +384,70 @@ export function visibleWidth(str: string): number {
384
384
  return width;
385
385
  }
386
386
 
387
+ /** Remove ANSI, OSC, and APC control sequences while preserving visible text. */
388
+ export function stripTerminalSequences(str: string): string {
389
+ if (!str.includes("\x1b")) return str;
390
+ let result = "";
391
+ let i = 0;
392
+ while (i < str.length) {
393
+ const ansi = extractAnsiCode(str, i);
394
+ if (ansi) {
395
+ i += ansi.length;
396
+ continue;
397
+ }
398
+ result += str[i];
399
+ i++;
400
+ }
401
+ return result;
402
+ }
403
+
404
+ /**
405
+ * Extract ANSI escape sequences from a string at the given position.
406
+ * Copied verbatim from pi-mono `packages/tui/src/utils.ts` alongside
407
+ * `stripTerminalSequences` so both behave identically to upstream pi. Kept
408
+ * module-private: it was removed from the public API in 9.6.2 and this port
409
+ * does not reintroduce that export.
410
+ */
411
+ function extractAnsiCode(str: string, pos: number): { code: string; length: number } | null {
412
+ if (pos >= str.length || str[pos] !== "\x1b") return null;
413
+
414
+ const next = str[pos + 1];
415
+
416
+ // CSI sequence: ESC [ ... m/G/K/H/J
417
+ if (next === "[") {
418
+ let j = pos + 2;
419
+ while (j < str.length && !/[mGKHJ]/.test(str[j]!)) j++;
420
+ if (j < str.length) return { code: str.substring(pos, j + 1), length: j + 1 - pos };
421
+ return null;
422
+ }
423
+
424
+ // OSC sequence: ESC ] ... BEL or ESC ] ... ST (ESC \)
425
+ // Used for hyperlinks (OSC 8), window titles, etc.
426
+ if (next === "]") {
427
+ let j = pos + 2;
428
+ while (j < str.length) {
429
+ if (str[j] === "\x07") return { code: str.substring(pos, j + 1), length: j + 1 - pos };
430
+ if (str[j] === "\x1b" && str[j + 1] === "\\") return { code: str.substring(pos, j + 2), length: j + 2 - pos };
431
+ j++;
432
+ }
433
+ return null;
434
+ }
435
+
436
+ // APC sequence: ESC _ ... BEL or ESC _ ... ST (ESC \)
437
+ // Used for cursor marker and application-specific commands
438
+ if (next === "_") {
439
+ let j = pos + 2;
440
+ while (j < str.length) {
441
+ if (str[j] === "\x07") return { code: str.substring(pos, j + 1), length: j + 1 - pos };
442
+ if (str[j] === "\x1b" && str[j + 1] === "\\") return { code: str.substring(pos, j + 2), length: j + 2 - pos };
443
+ j++;
444
+ }
445
+ return null;
446
+ }
447
+
448
+ return null;
449
+ }
450
+
387
451
  /**
388
452
  * Scaled spans must bypass wrapping/padding and, when scaled up, reserve the
389
453
  * terminal rows their multicell glyphs flow into.