pi2dsh 0.2.0

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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +122 -0
  3. package/README.zh.md +122 -0
  4. package/dist/cli.d.mts +2 -0
  5. package/dist/cli.mjs +128 -0
  6. package/dist/cli.mjs.map +1 -0
  7. package/dist/compat/pi-ai.d.mts +2597 -0
  8. package/dist/compat/pi-ai.d.mts.map +1 -0
  9. package/dist/compat/pi-ai.mjs +4669 -0
  10. package/dist/compat/pi-ai.mjs.map +1 -0
  11. package/dist/compat/pi-coding-agent.d.mts +745 -0
  12. package/dist/compat/pi-coding-agent.d.mts.map +1 -0
  13. package/dist/compat/pi-coding-agent.mjs +4 -0
  14. package/dist/compat/pi-tui.d.mts +3 -0
  15. package/dist/compat/pi-tui.mjs +3622 -0
  16. package/dist/compat/pi-tui.mjs.map +1 -0
  17. package/dist/host.d.mts +35 -0
  18. package/dist/host.d.mts.map +1 -0
  19. package/dist/host.mjs +197 -0
  20. package/dist/host.mjs.map +1 -0
  21. package/dist/index.d.mts +69 -0
  22. package/dist/index.d.mts.map +1 -0
  23. package/dist/index.mjs +5 -0
  24. package/dist/mcp-config-jL9w70It.mjs +1535 -0
  25. package/dist/mcp-config-jL9w70It.mjs.map +1 -0
  26. package/dist/pi-coding-agent-Dsg6_0ua.mjs +2060 -0
  27. package/dist/pi-coding-agent-Dsg6_0ua.mjs.map +1 -0
  28. package/dist/pi-config-shim-CZ1wFzqM.mjs +27 -0
  29. package/dist/pi-config-shim-CZ1wFzqM.mjs.map +1 -0
  30. package/dist/pi-tui-iHoF2tFc.d.mts +1043 -0
  31. package/dist/pi-tui-iHoF2tFc.d.mts.map +1 -0
  32. package/dist/pi-tui-utils-CcaVtm-3.mjs +895 -0
  33. package/dist/pi-tui-utils-CcaVtm-3.mjs.map +1 -0
  34. package/dist/pi-types-KazmR2O5.d.mts +62 -0
  35. package/dist/pi-types-KazmR2O5.d.mts.map +1 -0
  36. package/dist/pi-uuid-Db8ShZsK.mjs +47 -0
  37. package/dist/pi-uuid-Db8ShZsK.mjs.map +1 -0
  38. package/dist/rolldown-runtime-C2Q2p085.mjs +15 -0
  39. package/dist/runtime-D84Hv_3m.mjs +1499 -0
  40. package/dist/runtime-D84Hv_3m.mjs.map +1 -0
  41. package/dist/runtime.d.mts +31 -0
  42. package/dist/runtime.d.mts.map +1 -0
  43. package/dist/runtime.mjs +3 -0
  44. package/dist/source-D7Ir-rPT.mjs +154 -0
  45. package/dist/source-D7Ir-rPT.mjs.map +1 -0
  46. package/dist/types-7IWJPPvS.d.mts +59 -0
  47. package/dist/types-7IWJPPvS.d.mts.map +1 -0
  48. package/package.json +135 -0
@@ -0,0 +1,895 @@
1
+
2
+ import { eastAsianWidth } from "get-east-asian-width";
3
+ //#region src/compat/vendor/pi-tui-utils.ts
4
+ const graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
5
+ const wordSegmenter = new Intl.Segmenter(void 0, { granularity: "word" });
6
+ /**
7
+ * Get the shared grapheme segmenter instance.
8
+ */
9
+ function getGraphemeSegmenter() {
10
+ return graphemeSegmenter;
11
+ }
12
+ /**
13
+ * Get the shared word segmenter instance.
14
+ */
15
+ function getWordSegmenter() {
16
+ return wordSegmenter;
17
+ }
18
+ /**
19
+ * Check if a grapheme cluster (after segmentation) could possibly be an RGI emoji.
20
+ * This is a fast heuristic to avoid the expensive rgiEmojiRegex test.
21
+ * The tested Unicode blocks are deliberately broad to account for future
22
+ * Unicode additions.
23
+ */
24
+ function couldBeEmoji(segment) {
25
+ const cp = segment.codePointAt(0);
26
+ return cp >= 126976 && cp <= 130047 || cp >= 8960 && cp <= 9215 || cp >= 9728 && cp <= 10175 || cp >= 11088 && cp <= 11093 || segment.includes("️") || segment.length > 2;
27
+ }
28
+ const zeroWidthRegex = /^(?:\p{Default_Ignorable_Code_Point}|\p{Control}|\p{Mark}|\p{Surrogate})+$/v;
29
+ const leadingNonPrintingRegex = /^[\p{Default_Ignorable_Code_Point}\p{Control}\p{Format}\p{Mark}\p{Surrogate}]+/v;
30
+ const nonPrintingCharRegex = /^(?:\p{Default_Ignorable_Code_Point}|\p{Control}|\p{Format}|\p{Mark}|\p{Surrogate})$/v;
31
+ const markCharRegex = /^\p{Mark}$/v;
32
+ const terminalSpacingMarkRegex = /^(?:[\p{Spacing_Mark}--[\u1734\u302E\u302F]]|[\u065F\u0F7F\u102B\u102C\u1031\u1033-\u1035\u1038\u103A-\u103E])+$/v;
33
+ const rgiEmojiRegex = /^\p{RGI_Emoji}$/v;
34
+ const WIDTH_CACHE_SIZE = 512;
35
+ const widthCache = /* @__PURE__ */ new Map();
36
+ const cjkBreakRegex = /[\p{Script_Extensions=Han}\p{Script_Extensions=Hiragana}\p{Script_Extensions=Katakana}\p{Script_Extensions=Hangul}\p{Script_Extensions=Bopomofo}]/u;
37
+ function isPrintableAscii(str) {
38
+ for (let i = 0; i < str.length; i++) {
39
+ const code = str.charCodeAt(i);
40
+ if (code < 32 || code > 126) return false;
41
+ }
42
+ return true;
43
+ }
44
+ function truncateFragmentToWidth(text, maxWidth) {
45
+ if (maxWidth <= 0 || text.length === 0) return {
46
+ text: "",
47
+ width: 0
48
+ };
49
+ if (isPrintableAscii(text)) {
50
+ const clipped = text.slice(0, maxWidth);
51
+ return {
52
+ text: clipped,
53
+ width: clipped.length
54
+ };
55
+ }
56
+ const hasAnsi = text.includes("\x1B");
57
+ const hasTabs = text.includes(" ");
58
+ if (!hasAnsi && !hasTabs) {
59
+ let result = "";
60
+ let width = 0;
61
+ for (const { segment } of graphemeSegmenter.segment(text)) {
62
+ const w = graphemeWidth(segment);
63
+ if (width + w > maxWidth) break;
64
+ result += segment;
65
+ width += w;
66
+ }
67
+ return {
68
+ text: result,
69
+ width
70
+ };
71
+ }
72
+ let result = "";
73
+ let width = 0;
74
+ let i = 0;
75
+ let pendingAnsi = "";
76
+ while (i < text.length) {
77
+ const ansi = extractAnsiCode(text, i);
78
+ if (ansi) {
79
+ pendingAnsi += ansi.code;
80
+ i += ansi.length;
81
+ continue;
82
+ }
83
+ if (text[i] === " ") {
84
+ if (width + 3 > maxWidth) break;
85
+ if (pendingAnsi) {
86
+ result += pendingAnsi;
87
+ pendingAnsi = "";
88
+ }
89
+ result += " ";
90
+ width += 3;
91
+ i++;
92
+ continue;
93
+ }
94
+ let end = i;
95
+ while (end < text.length && text[end] !== " ") {
96
+ if (extractAnsiCode(text, end)) break;
97
+ end++;
98
+ }
99
+ for (const { segment } of graphemeSegmenter.segment(text.slice(i, end))) {
100
+ const w = graphemeWidth(segment);
101
+ if (width + w > maxWidth) return {
102
+ text: result,
103
+ width
104
+ };
105
+ if (pendingAnsi) {
106
+ result += pendingAnsi;
107
+ pendingAnsi = "";
108
+ }
109
+ result += segment;
110
+ width += w;
111
+ }
112
+ i = end;
113
+ }
114
+ return {
115
+ text: result,
116
+ width
117
+ };
118
+ }
119
+ function finalizeTruncatedResult(prefix, prefixWidth, ellipsis, ellipsisWidth, maxWidth, pad) {
120
+ const reset = "\x1B[0m";
121
+ const hyperlinkClose = getActiveOsc8Close(prefix);
122
+ const visibleWidth = prefixWidth + ellipsisWidth;
123
+ let result;
124
+ if (ellipsis.length > 0) result = `${prefix}${hyperlinkClose}${reset}${ellipsis}${reset}`;
125
+ else result = `${prefix}${hyperlinkClose}${reset}`;
126
+ return pad ? result + " ".repeat(Math.max(0, maxWidth - visibleWidth)) : result;
127
+ }
128
+ /**
129
+ * Calculate the terminal width of a single grapheme cluster.
130
+ * Based on code from the string-width library, but includes a possible-emoji
131
+ * check to avoid running the RGI_Emoji regex unnecessarily.
132
+ */
133
+ function graphemeWidth(segment) {
134
+ if (segment === " ") return 3;
135
+ if (terminalSpacingMarkRegex.test(segment)) return [...segment].length;
136
+ if (zeroWidthRegex.test(segment)) return 0;
137
+ if (couldBeEmoji(segment) && rgiEmojiRegex.test(segment)) return 2;
138
+ const base = segment.replace(leadingNonPrintingRegex, "");
139
+ const cp = base.codePointAt(0);
140
+ if (cp === void 0) return 0;
141
+ if (cp >= 127462 && cp <= 127487) return 2;
142
+ let width = eastAsianWidth(cp);
143
+ let followsMark = false;
144
+ const chars = [...base];
145
+ for (const char of chars.slice(1)) if (terminalSpacingMarkRegex.test(char)) {
146
+ width += 1;
147
+ followsMark = false;
148
+ } else if (markCharRegex.test(char)) followsMark = true;
149
+ else if (!nonPrintingCharRegex.test(char)) {
150
+ const c = char.codePointAt(0);
151
+ if (followsMark || c >= 65280 && c <= 65519) width += eastAsianWidth(c);
152
+ else if (c === 3635 || c === 3763) width += 1;
153
+ followsMark = false;
154
+ }
155
+ return width;
156
+ }
157
+ /**
158
+ * Calculate the visible width of a string in terminal columns.
159
+ */
160
+ function visibleWidth(str) {
161
+ if (str.length === 0) return 0;
162
+ if (isPrintableAscii(str)) return str.length;
163
+ const cached = widthCache.get(str);
164
+ if (cached !== void 0) return cached;
165
+ let clean = str;
166
+ if (str.includes(" ")) clean = clean.replace(/\t/g, " ");
167
+ if (clean.includes("\x1B")) {
168
+ let stripped = "";
169
+ let i = 0;
170
+ while (i < clean.length) {
171
+ const ansi = extractAnsiCode(clean, i);
172
+ if (ansi) {
173
+ i += ansi.length;
174
+ continue;
175
+ }
176
+ stripped += clean[i];
177
+ i++;
178
+ }
179
+ clean = stripped;
180
+ }
181
+ let width = 0;
182
+ for (const { segment } of graphemeSegmenter.segment(clean)) width += graphemeWidth(segment);
183
+ if (widthCache.size >= WIDTH_CACHE_SIZE) {
184
+ const firstKey = widthCache.keys().next().value;
185
+ if (firstKey !== void 0) widthCache.delete(firstKey);
186
+ }
187
+ widthCache.set(str, width);
188
+ return width;
189
+ }
190
+ /** Remove ANSI, OSC, and APC control sequences while preserving visible text. */
191
+ function stripTerminalSequences(str) {
192
+ if (!str.includes("\x1B")) return str;
193
+ let result = "";
194
+ let i = 0;
195
+ while (i < str.length) {
196
+ const ansi = extractAnsiCode(str, i);
197
+ if (ansi) {
198
+ i += ansi.length;
199
+ continue;
200
+ }
201
+ result += str[i];
202
+ i++;
203
+ }
204
+ return result;
205
+ }
206
+ /** Return the terminal-cell range occupied by the grapheme at a visible column. */
207
+ function getGraphemeCellRange(line, column) {
208
+ let currentCol = 0;
209
+ let i = 0;
210
+ while (i < line.length) {
211
+ const ansi = extractAnsiCode(line, i);
212
+ if (ansi) {
213
+ i += ansi.length;
214
+ continue;
215
+ }
216
+ let textEnd = i;
217
+ while (textEnd < line.length && !extractAnsiCode(line, textEnd)) textEnd++;
218
+ for (const { segment } of graphemeSegmenter.segment(line.slice(i, textEnd))) {
219
+ const width = graphemeWidth(segment);
220
+ if (width > 0 && column >= currentCol && column < currentCol + width) return {
221
+ start: currentCol,
222
+ end: currentCol + width
223
+ };
224
+ currentCol += width;
225
+ }
226
+ i = textEnd;
227
+ }
228
+ }
229
+ /** Return the OSC 8 hyperlink covering a visible terminal column. */
230
+ function getOsc8LinkAtColumn(line, column) {
231
+ let activeUrl;
232
+ let currentCol = 0;
233
+ let i = 0;
234
+ while (i < line.length) {
235
+ const ansi = extractAnsiCode(line, i);
236
+ if (ansi) {
237
+ const hyperlink = /^\x1b\]8;[^;]*;([^\x07\x1b]*)(?:\x07|\x1b\\)$/.exec(ansi.code);
238
+ if (hyperlink) activeUrl = hyperlink[1] || void 0;
239
+ i += ansi.length;
240
+ continue;
241
+ }
242
+ let textEnd = i;
243
+ while (textEnd < line.length && !extractAnsiCode(line, textEnd)) textEnd++;
244
+ for (const { segment } of graphemeSegmenter.segment(line.slice(i, textEnd))) {
245
+ const width = segment === " " ? 3 : graphemeWidth(segment);
246
+ if (column >= currentCol && column < currentCol + width) return activeUrl;
247
+ currentCol += width;
248
+ }
249
+ i = textEnd;
250
+ }
251
+ }
252
+ /**
253
+ * Normalize text for terminal output without changing logical editor content.
254
+ * Some terminals render precomposed Thai/Lao AM vowels inconsistently during
255
+ * differential repaint. Their compatibility decompositions have the same cell
256
+ * width but avoid stale-cell artifacts in terminal renderers. Visible tabs are
257
+ * expanded to the fixed width used by layout so terminal tab stops cannot wrap
258
+ * a logical line, while tabs inside terminal string sequences stay untouched.
259
+ */
260
+ const THAI_LAO_AM_REGEX = /[\u0e33\u0eb3]/;
261
+ const THAI_LAO_AM_GLOBAL_REGEX = /[\u0e33\u0eb3]/g;
262
+ function normalizeTerminalOutput(str) {
263
+ let normalized = str;
264
+ if (THAI_LAO_AM_REGEX.test(normalized)) normalized = normalized.replace(THAI_LAO_AM_GLOBAL_REGEX, (char) => char === "ำ" ? "ํา" : "ໍາ");
265
+ if (!normalized.includes(" ")) return normalized;
266
+ let result = "";
267
+ let i = 0;
268
+ while (i < normalized.length) {
269
+ const ansi = extractAnsiCode(normalized, i);
270
+ if (ansi) {
271
+ result += ansi.code;
272
+ i += ansi.length;
273
+ continue;
274
+ }
275
+ result += normalized[i] === " " ? " " : normalized[i];
276
+ i++;
277
+ }
278
+ return result;
279
+ }
280
+ /**
281
+ * Extract ANSI escape sequences from a string at the given position.
282
+ */
283
+ function extractAnsiCode(str, pos) {
284
+ if (pos >= str.length || str[pos] !== "\x1B") return null;
285
+ const next = str[pos + 1];
286
+ if (next === "[") {
287
+ let j = pos + 2;
288
+ while (j < str.length && !/[mGKHJ]/.test(str[j])) j++;
289
+ if (j < str.length) return {
290
+ code: str.substring(pos, j + 1),
291
+ length: j + 1 - pos
292
+ };
293
+ return null;
294
+ }
295
+ if (next === "]") {
296
+ let j = pos + 2;
297
+ while (j < str.length) {
298
+ if (str[j] === "\x07") return {
299
+ code: str.substring(pos, j + 1),
300
+ length: j + 1 - pos
301
+ };
302
+ if (str[j] === "\x1B" && str[j + 1] === "\\") return {
303
+ code: str.substring(pos, j + 2),
304
+ length: j + 2 - pos
305
+ };
306
+ j++;
307
+ }
308
+ return null;
309
+ }
310
+ if (next === "_") {
311
+ let j = pos + 2;
312
+ while (j < str.length) {
313
+ if (str[j] === "\x07") return {
314
+ code: str.substring(pos, j + 1),
315
+ length: j + 1 - pos
316
+ };
317
+ if (str[j] === "\x1B" && str[j + 1] === "\\") return {
318
+ code: str.substring(pos, j + 2),
319
+ length: j + 2 - pos
320
+ };
321
+ j++;
322
+ }
323
+ return null;
324
+ }
325
+ return null;
326
+ }
327
+ function parseOsc8Hyperlink(ansiCode) {
328
+ if (!ansiCode.startsWith("\x1B]8;")) return;
329
+ const terminator = ansiCode.endsWith("\x07") ? "\x07" : "\x1B\\";
330
+ const body = ansiCode.slice(4, terminator === "\x07" ? -1 : -2);
331
+ const separatorIndex = body.indexOf(";");
332
+ if (separatorIndex === -1) return;
333
+ const params = body.slice(0, separatorIndex);
334
+ const url = body.slice(separatorIndex + 1);
335
+ if (!url) return null;
336
+ return {
337
+ params,
338
+ url,
339
+ terminator
340
+ };
341
+ }
342
+ function formatOsc8Hyperlink(hyperlink) {
343
+ return `\x1b]8;${hyperlink.params};${hyperlink.url}${hyperlink.terminator}`;
344
+ }
345
+ function formatOsc8Close(terminator) {
346
+ return `\x1b]8;;${terminator}`;
347
+ }
348
+ function getActiveOsc8Close(prefix) {
349
+ if (!prefix.includes("\x1B]8;")) return "";
350
+ let activeHyperlink = null;
351
+ let i = 0;
352
+ while (i < prefix.length) {
353
+ const ansi = extractAnsiCode(prefix, i);
354
+ if (ansi) {
355
+ const hyperlink = parseOsc8Hyperlink(ansi.code);
356
+ if (hyperlink !== void 0) activeHyperlink = hyperlink;
357
+ i += ansi.length;
358
+ } else i++;
359
+ }
360
+ return activeHyperlink ? formatOsc8Close(activeHyperlink.terminator) : "";
361
+ }
362
+ /**
363
+ * Track active ANSI SGR codes to preserve styling across line breaks.
364
+ */
365
+ var AnsiCodeTracker = class {
366
+ bold = false;
367
+ dim = false;
368
+ italic = false;
369
+ underline = false;
370
+ blink = false;
371
+ inverse = false;
372
+ hidden = false;
373
+ strikethrough = false;
374
+ fgColor = null;
375
+ bgColor = null;
376
+ activeHyperlink = null;
377
+ process(ansiCode) {
378
+ const hyperlink = parseOsc8Hyperlink(ansiCode);
379
+ if (hyperlink !== void 0) {
380
+ this.activeHyperlink = hyperlink;
381
+ return;
382
+ }
383
+ if (!ansiCode.endsWith("m")) return;
384
+ const match = ansiCode.match(/\x1b\[([\d;]*)m/);
385
+ if (!match) return;
386
+ const params = match[1];
387
+ if (params === "" || params === "0") {
388
+ this.reset();
389
+ return;
390
+ }
391
+ const parts = params.split(";");
392
+ let i = 0;
393
+ while (i < parts.length) {
394
+ const code = Number.parseInt(parts[i], 10);
395
+ if (code === 38 || code === 48) {
396
+ if (parts[i + 1] === "5" && parts[i + 2] !== void 0) {
397
+ const colorCode = `${parts[i]};${parts[i + 1]};${parts[i + 2]}`;
398
+ if (code === 38) this.fgColor = colorCode;
399
+ else this.bgColor = colorCode;
400
+ i += 3;
401
+ continue;
402
+ } else if (parts[i + 1] === "2" && parts[i + 4] !== void 0) {
403
+ const colorCode = `${parts[i]};${parts[i + 1]};${parts[i + 2]};${parts[i + 3]};${parts[i + 4]}`;
404
+ if (code === 38) this.fgColor = colorCode;
405
+ else this.bgColor = colorCode;
406
+ i += 5;
407
+ continue;
408
+ }
409
+ }
410
+ switch (code) {
411
+ case 0:
412
+ this.reset();
413
+ break;
414
+ case 1:
415
+ this.bold = true;
416
+ break;
417
+ case 2:
418
+ this.dim = true;
419
+ break;
420
+ case 3:
421
+ this.italic = true;
422
+ break;
423
+ case 4:
424
+ this.underline = true;
425
+ break;
426
+ case 5:
427
+ this.blink = true;
428
+ break;
429
+ case 7:
430
+ this.inverse = true;
431
+ break;
432
+ case 8:
433
+ this.hidden = true;
434
+ break;
435
+ case 9:
436
+ this.strikethrough = true;
437
+ break;
438
+ case 21:
439
+ this.bold = false;
440
+ break;
441
+ case 22:
442
+ this.bold = false;
443
+ this.dim = false;
444
+ break;
445
+ case 23:
446
+ this.italic = false;
447
+ break;
448
+ case 24:
449
+ this.underline = false;
450
+ break;
451
+ case 25:
452
+ this.blink = false;
453
+ break;
454
+ case 27:
455
+ this.inverse = false;
456
+ break;
457
+ case 28:
458
+ this.hidden = false;
459
+ break;
460
+ case 29:
461
+ this.strikethrough = false;
462
+ break;
463
+ case 39:
464
+ this.fgColor = null;
465
+ break;
466
+ case 49:
467
+ this.bgColor = null;
468
+ break;
469
+ default: if (code >= 30 && code <= 37 || code >= 90 && code <= 97) this.fgColor = String(code);
470
+ else if (code >= 40 && code <= 47 || code >= 100 && code <= 107) this.bgColor = String(code);
471
+ }
472
+ i++;
473
+ }
474
+ }
475
+ reset() {
476
+ this.bold = false;
477
+ this.dim = false;
478
+ this.italic = false;
479
+ this.underline = false;
480
+ this.blink = false;
481
+ this.inverse = false;
482
+ this.hidden = false;
483
+ this.strikethrough = false;
484
+ this.fgColor = null;
485
+ this.bgColor = null;
486
+ }
487
+ /** Clear all state for reuse. */
488
+ clear() {
489
+ this.reset();
490
+ this.activeHyperlink = null;
491
+ }
492
+ getActiveCodes() {
493
+ const codes = [];
494
+ if (this.bold) codes.push("1");
495
+ if (this.dim) codes.push("2");
496
+ if (this.italic) codes.push("3");
497
+ if (this.underline) codes.push("4");
498
+ if (this.blink) codes.push("5");
499
+ if (this.inverse) codes.push("7");
500
+ if (this.hidden) codes.push("8");
501
+ if (this.strikethrough) codes.push("9");
502
+ if (this.fgColor) codes.push(this.fgColor);
503
+ if (this.bgColor) codes.push(this.bgColor);
504
+ let result = codes.length > 0 ? `\x1b[${codes.join(";")}m` : "";
505
+ if (this.activeHyperlink) result += formatOsc8Hyperlink(this.activeHyperlink);
506
+ return result;
507
+ }
508
+ hasActiveCodes() {
509
+ return this.bold || this.dim || this.italic || this.underline || this.blink || this.inverse || this.hidden || this.strikethrough || this.fgColor !== null || this.bgColor !== null || this.activeHyperlink !== null;
510
+ }
511
+ /**
512
+ * Get reset codes for attributes that need to be turned off at line end.
513
+ * Underline must be closed to prevent bleeding into padding.
514
+ * Active OSC 8 hyperlinks must be closed and re-opened on the next line.
515
+ * Returns empty string if no attributes need closing.
516
+ */
517
+ getLineEndReset() {
518
+ let result = "";
519
+ if (this.underline) result += "\x1B[24m";
520
+ if (this.activeHyperlink) result += formatOsc8Close(this.activeHyperlink.terminator);
521
+ return result;
522
+ }
523
+ };
524
+ function updateTrackerFromText(text, tracker) {
525
+ let i = 0;
526
+ while (i < text.length) {
527
+ const ansiResult = extractAnsiCode(text, i);
528
+ if (ansiResult) {
529
+ tracker.process(ansiResult.code);
530
+ i += ansiResult.length;
531
+ } else i++;
532
+ }
533
+ }
534
+ /**
535
+ * Split text into words while keeping ANSI codes attached.
536
+ */
537
+ function splitIntoTokensWithAnsi(text) {
538
+ const tokens = [];
539
+ let current = "";
540
+ let pendingAnsi = "";
541
+ let currentKind = null;
542
+ let i = 0;
543
+ const flushCurrent = () => {
544
+ if (!current) return;
545
+ tokens.push(current);
546
+ current = "";
547
+ currentKind = null;
548
+ };
549
+ while (i < text.length) {
550
+ const ansiResult = extractAnsiCode(text, i);
551
+ if (ansiResult) {
552
+ pendingAnsi += ansiResult.code;
553
+ i += ansiResult.length;
554
+ continue;
555
+ }
556
+ let end = i;
557
+ while (end < text.length && !extractAnsiCode(text, end)) end++;
558
+ for (const { segment } of graphemeSegmenter.segment(text.slice(i, end))) {
559
+ const segmentIsSpace = segment === " ";
560
+ if (!segmentIsSpace && cjkBreakRegex.test(segment)) {
561
+ flushCurrent();
562
+ const token = pendingAnsi + segment;
563
+ pendingAnsi = "";
564
+ tokens.push(token);
565
+ continue;
566
+ }
567
+ const segmentKind = segmentIsSpace ? "space" : "word";
568
+ if (current && currentKind !== segmentKind) flushCurrent();
569
+ if (pendingAnsi) {
570
+ current += pendingAnsi;
571
+ pendingAnsi = "";
572
+ }
573
+ currentKind = segmentKind;
574
+ current += segment;
575
+ }
576
+ i = end;
577
+ }
578
+ if (pendingAnsi) {
579
+ if (current) current += pendingAnsi;
580
+ else if (tokens.length > 0) tokens[tokens.length - 1] += pendingAnsi;
581
+ else current = pendingAnsi;
582
+ }
583
+ if (current) tokens.push(current);
584
+ return tokens;
585
+ }
586
+ /**
587
+ * Wrap text with ANSI codes preserved.
588
+ *
589
+ * ONLY does word wrapping - NO padding, NO background colors.
590
+ * Returns lines where each line is <= width visible chars.
591
+ * Active ANSI codes are preserved across line breaks.
592
+ *
593
+ * @param text - Text to wrap (may contain ANSI codes and newlines)
594
+ * @param width - Maximum visible width per line
595
+ * @returns Array of wrapped lines (NOT padded to width)
596
+ */
597
+ function wrapTextWithAnsi(text, width) {
598
+ if (!text) return [""];
599
+ const inputLines = text.split(/\r\n|\r|\n/);
600
+ const result = [];
601
+ const tracker = new AnsiCodeTracker();
602
+ for (const inputLine of inputLines) {
603
+ const wrappedLines = wrapSingleLine((result.length > 0 ? tracker.getActiveCodes() : "") + inputLine, width);
604
+ for (const wrappedLine of wrappedLines) result.push(wrappedLine);
605
+ updateTrackerFromText(inputLine, tracker);
606
+ }
607
+ return result.length > 0 ? result : [""];
608
+ }
609
+ function wrapSingleLine(line, width) {
610
+ if (!line) return [""];
611
+ if (visibleWidth(line) <= width) return [line];
612
+ const wrapped = [];
613
+ const tracker = new AnsiCodeTracker();
614
+ const tokens = splitIntoTokensWithAnsi(line);
615
+ let currentLine = "";
616
+ let currentVisibleLength = 0;
617
+ for (const token of tokens) {
618
+ const tokenVisibleLength = visibleWidth(token);
619
+ const isWhitespace = token.trim() === "";
620
+ if (tokenVisibleLength > width && !isWhitespace) {
621
+ if (currentLine) {
622
+ const lineEndReset = tracker.getLineEndReset();
623
+ if (lineEndReset) currentLine += lineEndReset;
624
+ wrapped.push(currentLine);
625
+ currentLine = "";
626
+ currentVisibleLength = 0;
627
+ }
628
+ const broken = breakLongWord(token, width, tracker);
629
+ for (let i = 0; i < broken.length - 1; i++) wrapped.push(broken[i]);
630
+ currentLine = broken[broken.length - 1];
631
+ currentVisibleLength = visibleWidth(currentLine);
632
+ continue;
633
+ }
634
+ if (currentVisibleLength + tokenVisibleLength > width && currentVisibleLength > 0) {
635
+ let lineToWrap = currentLine.trimEnd();
636
+ const lineEndReset = tracker.getLineEndReset();
637
+ if (lineEndReset) lineToWrap += lineEndReset;
638
+ wrapped.push(lineToWrap);
639
+ if (isWhitespace) {
640
+ currentLine = tracker.getActiveCodes();
641
+ currentVisibleLength = 0;
642
+ } else {
643
+ currentLine = tracker.getActiveCodes() + token;
644
+ currentVisibleLength = tokenVisibleLength;
645
+ }
646
+ } else {
647
+ currentLine += token;
648
+ currentVisibleLength += tokenVisibleLength;
649
+ }
650
+ updateTrackerFromText(token, tracker);
651
+ }
652
+ if (currentLine) wrapped.push(currentLine);
653
+ return wrapped.length > 0 ? wrapped.map((line) => line.trimEnd()) : [""];
654
+ }
655
+ const PUNCTUATION_REGEX = /[(){}[\]<>.,;:'"!?+\-=*/\\|&%^$#@~`]/;
656
+ /**
657
+ * Check if a character is whitespace.
658
+ */
659
+ function isWhitespaceChar(char) {
660
+ return /\s/.test(char);
661
+ }
662
+ /**
663
+ * Check if a character is punctuation.
664
+ */
665
+ function isPunctuationChar(char) {
666
+ return PUNCTUATION_REGEX.test(char);
667
+ }
668
+ function breakLongWord(word, width, tracker) {
669
+ const lines = [];
670
+ let currentLine = tracker.getActiveCodes();
671
+ let currentWidth = 0;
672
+ let i = 0;
673
+ const segments = [];
674
+ while (i < word.length) {
675
+ const ansiResult = extractAnsiCode(word, i);
676
+ if (ansiResult) {
677
+ segments.push({
678
+ type: "ansi",
679
+ value: ansiResult.code
680
+ });
681
+ i += ansiResult.length;
682
+ } else {
683
+ let end = i;
684
+ while (end < word.length) {
685
+ if (extractAnsiCode(word, end)) break;
686
+ end++;
687
+ }
688
+ const textPortion = word.slice(i, end);
689
+ for (const seg of graphemeSegmenter.segment(textPortion)) segments.push({
690
+ type: "grapheme",
691
+ value: seg.segment
692
+ });
693
+ i = end;
694
+ }
695
+ }
696
+ for (const seg of segments) {
697
+ if (seg.type === "ansi") {
698
+ currentLine += seg.value;
699
+ tracker.process(seg.value);
700
+ continue;
701
+ }
702
+ const grapheme = seg.value;
703
+ if (!grapheme) continue;
704
+ const graphemeWidth = visibleWidth(grapheme);
705
+ if (currentWidth + graphemeWidth > width) {
706
+ const lineEndReset = tracker.getLineEndReset();
707
+ if (lineEndReset) currentLine += lineEndReset;
708
+ lines.push(currentLine);
709
+ currentLine = tracker.getActiveCodes();
710
+ currentWidth = 0;
711
+ }
712
+ currentLine += grapheme;
713
+ currentWidth += graphemeWidth;
714
+ }
715
+ if (currentLine) lines.push(currentLine);
716
+ return lines.length > 0 ? lines : [""];
717
+ }
718
+ /**
719
+ * Apply background color to a line, padding to full width.
720
+ *
721
+ * @param line - Line of text (may contain ANSI codes)
722
+ * @param width - Total width to pad to
723
+ * @param bgFn - Background color function
724
+ * @returns Line with background applied and padded to width
725
+ */
726
+ function applyBackgroundToLine(line, width, bgFn) {
727
+ const visibleLen = visibleWidth(line);
728
+ const paddingNeeded = Math.max(0, width - visibleLen);
729
+ return bgFn(line + " ".repeat(paddingNeeded));
730
+ }
731
+ /**
732
+ * Truncate text to fit within a maximum visible width, adding ellipsis if needed.
733
+ * Optionally pad with spaces to reach exactly maxWidth.
734
+ * Properly handles ANSI escape codes (they don't count toward width).
735
+ *
736
+ * @param text - Text to truncate (may contain ANSI codes)
737
+ * @param maxWidth - Maximum visible width
738
+ * @param ellipsis - Ellipsis string to append when truncating (default: "...")
739
+ * @param pad - If true, pad result with spaces to exactly maxWidth (default: false)
740
+ * @returns Truncated text, optionally padded to exactly maxWidth
741
+ */
742
+ function truncateToWidth(text, maxWidth, ellipsis = "...", pad = false) {
743
+ if (maxWidth <= 0) return "";
744
+ if (text.length === 0) return pad ? " ".repeat(maxWidth) : "";
745
+ const ellipsisWidth = visibleWidth(ellipsis);
746
+ if (ellipsisWidth >= maxWidth) {
747
+ const textWidth = visibleWidth(text);
748
+ if (textWidth <= maxWidth) return pad ? text + " ".repeat(maxWidth - textWidth) : text;
749
+ const clippedEllipsis = truncateFragmentToWidth(ellipsis, maxWidth);
750
+ if (clippedEllipsis.width === 0) return pad ? " ".repeat(maxWidth) : "";
751
+ return finalizeTruncatedResult("", 0, clippedEllipsis.text, clippedEllipsis.width, maxWidth, pad);
752
+ }
753
+ if (isPrintableAscii(text)) {
754
+ if (text.length <= maxWidth) return pad ? text + " ".repeat(maxWidth - text.length) : text;
755
+ const targetWidth = maxWidth - ellipsisWidth;
756
+ return finalizeTruncatedResult(text.slice(0, targetWidth), targetWidth, ellipsis, ellipsisWidth, maxWidth, pad);
757
+ }
758
+ const targetWidth = maxWidth - ellipsisWidth;
759
+ let result = "";
760
+ let pendingAnsi = "";
761
+ let visibleSoFar = 0;
762
+ let keptWidth = 0;
763
+ let keepContiguousPrefix = true;
764
+ let overflowed = false;
765
+ let exhaustedInput = false;
766
+ const hasAnsi = text.includes("\x1B");
767
+ const hasTabs = text.includes(" ");
768
+ if (!hasAnsi && !hasTabs) {
769
+ for (const { segment } of graphemeSegmenter.segment(text)) {
770
+ const width = graphemeWidth(segment);
771
+ if (keepContiguousPrefix && keptWidth + width <= targetWidth) {
772
+ result += segment;
773
+ keptWidth += width;
774
+ } else keepContiguousPrefix = false;
775
+ visibleSoFar += width;
776
+ if (visibleSoFar > maxWidth) {
777
+ overflowed = true;
778
+ break;
779
+ }
780
+ }
781
+ exhaustedInput = !overflowed;
782
+ } else {
783
+ let i = 0;
784
+ while (i < text.length) {
785
+ const ansi = extractAnsiCode(text, i);
786
+ if (ansi) {
787
+ pendingAnsi += ansi.code;
788
+ i += ansi.length;
789
+ continue;
790
+ }
791
+ if (text[i] === " ") {
792
+ if (keepContiguousPrefix && keptWidth + 3 <= targetWidth) {
793
+ if (pendingAnsi) {
794
+ result += pendingAnsi;
795
+ pendingAnsi = "";
796
+ }
797
+ result += " ";
798
+ keptWidth += 3;
799
+ } else {
800
+ keepContiguousPrefix = false;
801
+ pendingAnsi = "";
802
+ }
803
+ visibleSoFar += 3;
804
+ if (visibleSoFar > maxWidth) {
805
+ overflowed = true;
806
+ break;
807
+ }
808
+ i++;
809
+ continue;
810
+ }
811
+ let end = i;
812
+ while (end < text.length && text[end] !== " ") {
813
+ if (extractAnsiCode(text, end)) break;
814
+ end++;
815
+ }
816
+ for (const { segment } of graphemeSegmenter.segment(text.slice(i, end))) {
817
+ const width = graphemeWidth(segment);
818
+ if (keepContiguousPrefix && keptWidth + width <= targetWidth) {
819
+ if (pendingAnsi) {
820
+ result += pendingAnsi;
821
+ pendingAnsi = "";
822
+ }
823
+ result += segment;
824
+ keptWidth += width;
825
+ } else {
826
+ keepContiguousPrefix = false;
827
+ pendingAnsi = "";
828
+ }
829
+ visibleSoFar += width;
830
+ if (visibleSoFar > maxWidth) {
831
+ overflowed = true;
832
+ break;
833
+ }
834
+ }
835
+ if (overflowed) break;
836
+ i = end;
837
+ }
838
+ exhaustedInput = i >= text.length;
839
+ }
840
+ if (!overflowed && exhaustedInput) return pad ? text + " ".repeat(Math.max(0, maxWidth - visibleSoFar)) : text;
841
+ return finalizeTruncatedResult(result, keptWidth, ellipsis, ellipsisWidth, maxWidth, pad);
842
+ }
843
+ /**
844
+ * Extract a range of visible columns from a line. Handles ANSI codes and wide chars.
845
+ * @param strict - If true, exclude wide chars at boundary that would extend past the range
846
+ */
847
+ function sliceByColumn(line, startCol, length, strict = false) {
848
+ return sliceWithWidth(line, startCol, length, strict).text;
849
+ }
850
+ /** Like sliceByColumn but also returns the actual visible width of the result. */
851
+ function sliceWithWidth(line, startCol, length, strict = false) {
852
+ if (length <= 0) return {
853
+ text: "",
854
+ width: 0
855
+ };
856
+ const endCol = startCol + length;
857
+ let result = "", resultWidth = 0, currentCol = 0, i = 0, pendingAnsi = "";
858
+ while (i < line.length) {
859
+ const ansi = extractAnsiCode(line, i);
860
+ if (ansi) {
861
+ if (currentCol >= startCol && currentCol < endCol) result += ansi.code;
862
+ else if (currentCol < startCol) pendingAnsi += ansi.code;
863
+ i += ansi.length;
864
+ continue;
865
+ }
866
+ let textEnd = i;
867
+ while (textEnd < line.length && !extractAnsiCode(line, textEnd)) textEnd++;
868
+ for (const { segment } of graphemeSegmenter.segment(line.slice(i, textEnd))) {
869
+ const w = graphemeWidth(segment);
870
+ const inRange = currentCol >= startCol && currentCol < endCol;
871
+ const fits = !strict || currentCol + w <= endCol;
872
+ if (inRange && fits) {
873
+ if (pendingAnsi) {
874
+ result += pendingAnsi;
875
+ pendingAnsi = "";
876
+ }
877
+ result += segment;
878
+ resultWidth += w;
879
+ }
880
+ currentCol += w;
881
+ if (currentCol >= endCol) break;
882
+ }
883
+ i = textEnd;
884
+ if (currentCol >= endCol) break;
885
+ }
886
+ return {
887
+ text: result,
888
+ width: resultWidth
889
+ };
890
+ }
891
+ new AnsiCodeTracker();
892
+ //#endregion
893
+ export { wrapTextWithAnsi as _, getGraphemeCellRange as a, getWordSegmenter as c, normalizeTerminalOutput as d, sliceByColumn as f, visibleWidth as g, truncateToWidth as h, extractAnsiCode as i, isPunctuationChar as l, stripTerminalSequences as m, applyBackgroundToLine as n, getGraphemeSegmenter as o, sliceWithWidth as p, cjkBreakRegex as r, getOsc8LinkAtColumn as s, PUNCTUATION_REGEX as t, isWhitespaceChar as u };
894
+
895
+ //# sourceMappingURL=pi-tui-utils-CcaVtm-3.mjs.map