ptywright 0.4.0 → 0.6.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.
@@ -1,3 +1,4 @@
1
+ import { a as styleKey, i as isDefaultStyle, n as extractStyle, r as findMeaningfulEndCol, t as DEFAULT_STYLE } from "./style-BtIUv5H0.mjs";
1
2
  import { Terminal } from "@xterm/headless";
2
3
  //#region src/terminal/keys.ts
3
4
  const CSI = "\x1B[";
@@ -143,159 +144,8 @@ function functionKeySpec(key) {
143
144
  };
144
145
  }
145
146
  //#endregion
146
- //#region src/terminal/style.ts
147
- const DEFAULT_STYLE = {
148
- fg: { mode: "default" },
149
- bg: { mode: "default" },
150
- bold: false,
151
- dim: false,
152
- italic: false,
153
- underline: false,
154
- inverse: false,
155
- strikethrough: false
156
- };
157
- function extractStyle(cell) {
158
- const style = {
159
- fg: extractColor(cell.isFgDefault(), cell.isFgPalette(), cell.isFgRGB(), cell.getFgColor()),
160
- bg: extractColor(cell.isBgDefault(), cell.isBgPalette(), cell.isBgRGB(), cell.getBgColor()),
161
- bold: cell.isBold() !== 0,
162
- dim: cell.isDim() !== 0,
163
- italic: cell.isItalic() !== 0,
164
- underline: cell.isUnderline() !== 0,
165
- inverse: cell.isInverse() !== 0,
166
- strikethrough: cell.isStrikethrough() !== 0
167
- };
168
- return isDefaultStyle(style) ? DEFAULT_STYLE : style;
169
- }
170
- function isDefaultStyle(style) {
171
- return style.fg.mode === "default" && style.bg.mode === "default" && !style.bold && !style.dim && !style.italic && !style.underline && !style.inverse && !style.strikethrough;
172
- }
173
- function styleKey(style) {
174
- return [
175
- style.fg.mode === "default" ? "d" : `${style.fg.mode}:${style.fg.value}`,
176
- style.bg.mode === "default" ? "d" : `${style.bg.mode}:${style.bg.value}`,
177
- style.bold ? "b" : "",
178
- style.dim ? "d" : "",
179
- style.italic ? "i" : "",
180
- style.underline ? "u" : "",
181
- style.inverse ? "r" : "",
182
- style.strikethrough ? "s" : ""
183
- ].join("|");
184
- }
185
- function findMeaningfulEndCol(line, cols, nullCell) {
186
- if (!line) return 0;
187
- for (let x = cols - 1; x >= 0; x -= 1) {
188
- const cell = line.getCell(x, nullCell);
189
- if (!cell) continue;
190
- if (cell.getWidth() === 0) continue;
191
- const chars = cell.getChars();
192
- const style = extractStyle(cell);
193
- if (chars !== "" && chars !== " " || !isDefaultStyle(style)) return x + 1;
194
- }
195
- return 0;
196
- }
197
- function extractColor(isDefault, isPalette, isRgb, value) {
198
- if (isDefault) return { mode: "default" };
199
- if (isRgb) return {
200
- mode: "rgb",
201
- value
202
- };
203
- if (isPalette) return {
204
- mode: "palette",
205
- value
206
- };
207
- return { mode: "default" };
208
- }
209
- //#endregion
210
- //#region src/terminal/ansi.ts
211
- const ESC$2 = "\x1B";
212
- const SGR_RESET = `${ESC$2}[0m`;
213
- function renderAnsiLines(terminal, options) {
214
- const scope = options?.scope ?? "visible";
215
- const trimRight = options?.trimRight ?? false;
216
- const buffer = terminal.buffer.active;
217
- const nullCell = buffer.getNullCell();
218
- let startY = 0;
219
- let count = buffer.length;
220
- if (scope === "visible") {
221
- startY = buffer.viewportY;
222
- count = terminal.rows;
223
- }
224
- const out = [];
225
- for (let i = 0; i < count; i += 1) {
226
- const y = startY + i;
227
- const line = buffer.getLine(y);
228
- out.push(renderLine(line, terminal.cols, nullCell, trimRight));
229
- }
230
- return out;
231
- }
232
- function renderLine(line, cols, nullCell, trimRight) {
233
- let endCol = cols;
234
- if (trimRight) endCol = findMeaningfulEndCol(line, cols, nullCell);
235
- let ansi = "";
236
- let plain = "";
237
- let hasStyle = false;
238
- let usedSgr = false;
239
- const defaultKey = styleKey(DEFAULT_STYLE);
240
- let currentKey = defaultKey;
241
- for (let x = 0; x < endCol; x += 1) {
242
- const cell = line?.getCell(x, nullCell);
243
- if (!cell) {
244
- plain += " ";
245
- ansi += " ";
246
- continue;
247
- }
248
- if (cell.getWidth() === 0) continue;
249
- const chars = cell.getChars() || " ";
250
- const style = extractStyle(cell);
251
- const isDefault = isDefaultStyle(style);
252
- if (!isDefault) hasStyle = true;
253
- const key = styleKey(style);
254
- if (key !== currentKey) {
255
- ansi += isDefault ? SGR_RESET : toSgr(style);
256
- usedSgr = true;
257
- currentKey = key;
258
- }
259
- ansi += chars;
260
- plain += chars;
261
- }
262
- if (usedSgr && currentKey !== defaultKey) ansi += SGR_RESET;
263
- return {
264
- ansi,
265
- plain,
266
- hasStyle
267
- };
268
- }
269
- function toSgr(style) {
270
- const codes = ["0"];
271
- if (style.bold) codes.push("1");
272
- if (style.dim) codes.push("2");
273
- if (style.italic) codes.push("3");
274
- if (style.underline) codes.push("4");
275
- if (style.inverse) codes.push("7");
276
- if (style.strikethrough) codes.push("9");
277
- if (style.fg.mode === "palette") codes.push(`38;5;${style.fg.value}`);
278
- else if (style.fg.mode === "rgb") {
279
- const [r, g, b] = rgb(style.fg.value);
280
- codes.push(`38;2;${r};${g};${b}`);
281
- }
282
- if (style.bg.mode === "palette") codes.push(`48;5;${style.bg.value}`);
283
- else if (style.bg.mode === "rgb") {
284
- const [r, g, b] = rgb(style.bg.value);
285
- codes.push(`48;2;${r};${g};${b}`);
286
- }
287
- return `${ESC$2}[${codes.join(";")}m`;
288
- }
289
- function rgb(value) {
290
- return [
291
- value >> 16 & 255,
292
- value >> 8 & 255,
293
- value & 255
294
- ];
295
- }
296
- //#endregion
297
147
  //#region src/terminal/mouse.ts
298
- const ESC$1 = "\x1B";
148
+ const ESC$2 = "\x1B";
299
149
  function encodeSgrMouse(event) {
300
150
  const x = clampInt(event.x, 1, 500);
301
151
  const y = clampInt(event.y, 1, 300);
@@ -312,11 +162,11 @@ function encodeSingleSgrMouse(event, x, y) {
312
162
  const modifiers = event.modifiers;
313
163
  const modifierBits = (modifiers?.shift ? 4 : 0) + (modifiers?.alt ? 8 : 0) + (modifiers?.ctrl ? 16 : 0);
314
164
  const buttonCode = buttonToCode(event.button ?? "left");
315
- if (event.action === "down") return `${ESC$1}[<${buttonCode + modifierBits};${x};${y}M`;
316
- if (event.action === "up") return `${ESC$1}[<${buttonCode + modifierBits};${x};${y}m`;
317
- if (event.action === "scroll_up") return `${ESC$1}[<${64 + modifierBits};${x};${y}M`;
318
- if (event.action === "scroll_down") return `${ESC$1}[<${65 + modifierBits};${x};${y}M`;
319
- return `${ESC$1}[<${32 + (event.button ? buttonCode : 3) + modifierBits};${x};${y}M`;
165
+ if (event.action === "down") return `${ESC$2}[<${buttonCode + modifierBits};${x};${y}M`;
166
+ if (event.action === "up") return `${ESC$2}[<${buttonCode + modifierBits};${x};${y}m`;
167
+ if (event.action === "scroll_up") return `${ESC$2}[<${64 + modifierBits};${x};${y}M`;
168
+ if (event.action === "scroll_down") return `${ESC$2}[<${65 + modifierBits};${x};${y}M`;
169
+ return `${ESC$2}[<${32 + (event.button ? buttonCode : 3) + modifierBits};${x};${y}M`;
320
170
  }
321
171
  function buttonToCode(button) {
322
172
  if (button === "left") return 0;
@@ -331,58 +181,7 @@ function clampInt(value, min, max) {
331
181
  return int;
332
182
  }
333
183
  //#endregion
334
- //#region src/terminal/snapshot.ts
335
- function snapshotVisibleLines(terminal, trimRight) {
336
- const buffer = terminal.buffer.active;
337
- const nullCell = buffer.getNullCell();
338
- const startY = buffer.viewportY;
339
- const lines = [];
340
- for (let row = 0; row < terminal.rows; row += 1) {
341
- const line = buffer.getLine(startY + row);
342
- lines.push(snapshotPlainLine(line, terminal.cols, nullCell, trimRight));
343
- }
344
- return lines;
345
- }
346
- function snapshotVisibleStyleRuns(terminal, trimRight) {
347
- const buffer = terminal.buffer.active;
348
- const nullCell = buffer.getNullCell();
349
- const startY = buffer.viewportY;
350
- const out = [];
351
- for (let row = 0; row < terminal.rows; row += 1) {
352
- const line = buffer.getLine(startY + row);
353
- out.push(snapshotStyleRuns(line, terminal.cols, nullCell, trimRight));
354
- }
355
- return out;
356
- }
357
- function snapshotBufferLines(terminal, trimRight) {
358
- const buffer = terminal.buffer.active;
359
- const lines = [];
360
- for (let y = 0; y < buffer.length; y += 1) {
361
- const line = buffer.getLine(y);
362
- lines.push(line ? line.translateToString(trimRight, 0, terminal.cols) : "");
363
- }
364
- return lines;
365
- }
366
- function snapshotLines(terminal, options) {
367
- const trimRight = options?.trimRight ?? true;
368
- return (options?.scope ?? "visible") === "buffer" ? snapshotBufferLines(terminal, trimRight) : snapshotVisibleLines(terminal, trimRight);
369
- }
370
- function snapshotGrid(terminal, options) {
371
- const trimRight = options?.trimRight ?? true;
372
- const includeStyles = options?.includeStyles ?? false;
373
- const buffer = terminal.buffer.active;
374
- const lines = snapshotVisibleLines(terminal, trimRight);
375
- return {
376
- cols: terminal.cols,
377
- rows: terminal.rows,
378
- bufferType: buffer.type,
379
- cursorX: buffer.cursorX,
380
- cursorY: buffer.cursorY,
381
- viewportY: buffer.viewportY,
382
- lines,
383
- styleRuns: includeStyles ? snapshotVisibleStyleRuns(terminal, trimRight) : void 0
384
- };
385
- }
184
+ //#region src/terminal/snapshot_rows.ts
386
185
  function snapshotPlainLine(line, cols, nullCell, trimRight) {
387
186
  let endCol = cols;
388
187
  if (trimRight) endCol = findMeaningfulEndCol(line, cols, nullCell);
@@ -442,48 +241,57 @@ function snapshotStyleRuns(line, cols, nullCell, trimRight) {
442
241
  return out;
443
242
  }
444
243
  //#endregion
445
- //#region src/terminal/mask.ts
446
- function applyTextMaskRules(lines, rules) {
447
- if (!rules || rules.length === 0) return lines;
448
- const compiled = compileMaskRules(rules);
449
- if (compiled.length === 0) return lines;
450
- return lines.map((line) => applyCompiledMaskRules(line, compiled));
244
+ //#region src/terminal/snapshot.ts
245
+ function snapshotVisibleLines(terminal, trimRight) {
246
+ const buffer = terminal.buffer.active;
247
+ const nullCell = buffer.getNullCell();
248
+ const startY = buffer.viewportY;
249
+ const lines = [];
250
+ for (let row = 0; row < terminal.rows; row += 1) {
251
+ const line = buffer.getLine(startY + row);
252
+ lines.push(snapshotPlainLine(line, terminal.cols, nullCell, trimRight));
253
+ }
254
+ return lines;
451
255
  }
452
- function compileMaskRules(rules) {
453
- const compiled = [];
454
- for (const rule of rules) {
455
- if (!rule.regex.trim()) continue;
456
- const preserveLength = rule.preserveLength ?? false;
457
- const replacement = preserveLength ? firstChar(rule.replacement) : rule.replacement ?? "<masked>";
458
- const flags = normalizeFlags(rule.flags);
459
- let regex;
460
- try {
461
- regex = new RegExp(rule.regex, flags);
462
- } catch (error) {
463
- throw new Error(`invalid mask rule regex=${JSON.stringify(rule.regex)} flags=${JSON.stringify(rule.flags ?? "")}: ${error.message}`);
464
- }
465
- compiled.push({
466
- regex,
467
- replacement,
468
- preserveLength
469
- });
256
+ function snapshotVisibleStyleRuns(terminal, trimRight) {
257
+ const buffer = terminal.buffer.active;
258
+ const nullCell = buffer.getNullCell();
259
+ const startY = buffer.viewportY;
260
+ const out = [];
261
+ for (let row = 0; row < terminal.rows; row += 1) {
262
+ const line = buffer.getLine(startY + row);
263
+ out.push(snapshotStyleRuns(line, terminal.cols, nullCell, trimRight));
470
264
  }
471
- return compiled;
265
+ return out;
472
266
  }
473
- function normalizeFlags(flags) {
474
- const value = flags?.trim() ? flags.trim() : "g";
475
- const set = new Set(value.split(""));
476
- set.add("g");
477
- return [...set].join("");
267
+ function snapshotBufferLines(terminal, trimRight) {
268
+ const buffer = terminal.buffer.active;
269
+ const lines = [];
270
+ for (let y = 0; y < buffer.length; y += 1) {
271
+ const line = buffer.getLine(y);
272
+ lines.push(line ? line.translateToString(trimRight, 0, terminal.cols) : "");
273
+ }
274
+ return lines;
478
275
  }
479
- function firstChar(value) {
480
- const trimmed = value?.trim();
481
- return trimmed && trimmed.length > 0 ? trimmed[0] : "█";
276
+ function snapshotLines(terminal, options) {
277
+ const trimRight = options?.trimRight ?? true;
278
+ return (options?.scope ?? "visible") === "buffer" ? snapshotBufferLines(terminal, trimRight) : snapshotVisibleLines(terminal, trimRight);
482
279
  }
483
- function applyCompiledMaskRules(line, rules) {
484
- let out = line;
485
- for (const rule of rules) out = out.replace(rule.regex, (match) => rule.preserveLength ? rule.replacement.repeat(match.length) : rule.replacement);
486
- return out;
280
+ function snapshotGrid(terminal, options) {
281
+ const trimRight = options?.trimRight ?? true;
282
+ const includeStyles = options?.includeStyles ?? false;
283
+ const buffer = terminal.buffer.active;
284
+ const lines = snapshotVisibleLines(terminal, trimRight);
285
+ return {
286
+ cols: terminal.cols,
287
+ rows: terminal.rows,
288
+ bufferType: buffer.type,
289
+ cursorX: buffer.cursorX,
290
+ cursorY: buffer.cursorY,
291
+ viewportY: buffer.viewportY,
292
+ lines,
293
+ styleRuns: includeStyles ? snapshotVisibleStyleRuns(terminal, trimRight) : void 0
294
+ };
487
295
  }
488
296
  //#endregion
489
297
  //#region src/util/hash.ts
@@ -496,13 +304,6 @@ function fnv1a32(text) {
496
304
  return (hash >>> 0).toString(16).padStart(8, "0");
497
305
  }
498
306
  //#endregion
499
- //#region src/util/sleep.ts
500
- async function sleep(ms) {
501
- await new Promise((resolve) => {
502
- setTimeout(resolve, ms);
503
- });
504
- }
505
- //#endregion
506
307
  //#region src/trace/asciicast.ts
507
308
  function encodeAsciicast(header, events) {
508
309
  const lines = [JSON.stringify(header)];
@@ -589,6 +390,269 @@ var TraceRecorder = class {
589
390
  }
590
391
  };
591
392
  //#endregion
393
+ //#region src/terminal/ansi.ts
394
+ const ESC$1 = "\x1B";
395
+ const SGR_RESET = `${ESC$1}[0m`;
396
+ function renderAnsiLines(terminal, options) {
397
+ const scope = options?.scope ?? "visible";
398
+ const trimRight = options?.trimRight ?? false;
399
+ const buffer = terminal.buffer.active;
400
+ const nullCell = buffer.getNullCell();
401
+ let startY = 0;
402
+ let count = buffer.length;
403
+ if (scope === "visible") {
404
+ startY = buffer.viewportY;
405
+ count = terminal.rows;
406
+ }
407
+ const out = [];
408
+ for (let i = 0; i < count; i += 1) {
409
+ const y = startY + i;
410
+ const line = buffer.getLine(y);
411
+ out.push(renderLine(line, terminal.cols, nullCell, trimRight));
412
+ }
413
+ return out;
414
+ }
415
+ function renderLine(line, cols, nullCell, trimRight) {
416
+ let endCol = cols;
417
+ if (trimRight) endCol = findMeaningfulEndCol(line, cols, nullCell);
418
+ let ansi = "";
419
+ let plain = "";
420
+ let hasStyle = false;
421
+ let usedSgr = false;
422
+ const defaultKey = styleKey(DEFAULT_STYLE);
423
+ let currentKey = defaultKey;
424
+ for (let x = 0; x < endCol; x += 1) {
425
+ const cell = line?.getCell(x, nullCell);
426
+ if (!cell) {
427
+ plain += " ";
428
+ ansi += " ";
429
+ continue;
430
+ }
431
+ if (cell.getWidth() === 0) continue;
432
+ const chars = cell.getChars() || " ";
433
+ const style = extractStyle(cell);
434
+ const isDefault = isDefaultStyle(style);
435
+ if (!isDefault) hasStyle = true;
436
+ const key = styleKey(style);
437
+ if (key !== currentKey) {
438
+ ansi += isDefault ? SGR_RESET : toSgr(style);
439
+ usedSgr = true;
440
+ currentKey = key;
441
+ }
442
+ ansi += chars;
443
+ plain += chars;
444
+ }
445
+ if (usedSgr && currentKey !== defaultKey) ansi += SGR_RESET;
446
+ return {
447
+ ansi,
448
+ plain,
449
+ hasStyle
450
+ };
451
+ }
452
+ function toSgr(style) {
453
+ const codes = ["0"];
454
+ if (style.bold) codes.push("1");
455
+ if (style.dim) codes.push("2");
456
+ if (style.italic) codes.push("3");
457
+ if (style.underline) codes.push("4");
458
+ if (style.inverse) codes.push("7");
459
+ if (style.strikethrough) codes.push("9");
460
+ if (style.fg.mode === "palette") codes.push(`38;5;${style.fg.value}`);
461
+ else if (style.fg.mode === "rgb") {
462
+ const [r, g, b] = rgb(style.fg.value);
463
+ codes.push(`38;2;${r};${g};${b}`);
464
+ }
465
+ if (style.bg.mode === "palette") codes.push(`48;5;${style.bg.value}`);
466
+ else if (style.bg.mode === "rgb") {
467
+ const [r, g, b] = rgb(style.bg.value);
468
+ codes.push(`48;2;${r};${g};${b}`);
469
+ }
470
+ return `${ESC$1}[${codes.join(";")}m`;
471
+ }
472
+ function rgb(value) {
473
+ return [
474
+ value >> 16 & 255,
475
+ value >> 8 & 255,
476
+ value & 255
477
+ ];
478
+ }
479
+ //#endregion
480
+ //#region src/terminal/mask.ts
481
+ function applyTextMaskRules(lines, rules) {
482
+ if (!rules || rules.length === 0) return lines;
483
+ const compiled = compileMaskRules(rules);
484
+ if (compiled.length === 0) return lines;
485
+ return lines.map((line) => applyCompiledMaskRules(line, compiled));
486
+ }
487
+ function compileMaskRules(rules) {
488
+ const compiled = [];
489
+ for (const rule of rules) {
490
+ if (!rule.regex.trim()) continue;
491
+ const preserveLength = rule.preserveLength ?? false;
492
+ const replacement = preserveLength ? firstChar(rule.replacement) : rule.replacement ?? "<masked>";
493
+ const flags = normalizeFlags(rule.flags);
494
+ let regex;
495
+ try {
496
+ regex = new RegExp(rule.regex, flags);
497
+ } catch (error) {
498
+ throw new Error(`invalid mask rule regex=${JSON.stringify(rule.regex)} flags=${JSON.stringify(rule.flags ?? "")}: ${error.message}`);
499
+ }
500
+ compiled.push({
501
+ regex,
502
+ replacement,
503
+ preserveLength
504
+ });
505
+ }
506
+ return compiled;
507
+ }
508
+ function normalizeFlags(flags) {
509
+ const value = flags?.trim() ? flags.trim() : "g";
510
+ const set = new Set(value.split(""));
511
+ set.add("g");
512
+ return [...set].join("");
513
+ }
514
+ function firstChar(value) {
515
+ const trimmed = value?.trim();
516
+ return trimmed && trimmed.length > 0 ? trimmed[0] : "█";
517
+ }
518
+ function applyCompiledMaskRules(line, rules) {
519
+ let out = line;
520
+ for (const rule of rules) out = out.replace(rule.regex, (match) => rule.preserveLength ? rule.replacement.repeat(match.length) : rule.replacement);
521
+ return out;
522
+ }
523
+ //#endregion
524
+ //#region src/session/terminal_session_snapshot.ts
525
+ function snapshotSessionText(terminal, options) {
526
+ if (options?.maxLines !== void 0 && options.tailLines !== void 0) throw new Error("snapshotText: maxLines and tailLines are mutually exclusive");
527
+ let lines = snapshotLines(terminal, {
528
+ scope: options?.scope,
529
+ trimRight: options?.trimRight
530
+ });
531
+ if (options?.trimBottom ?? true) lines = trimBottomEmptyLines(lines);
532
+ if (options?.maxLines !== void 0) {
533
+ const max = Math.max(0, Math.trunc(options.maxLines));
534
+ lines = lines.slice(0, max);
535
+ }
536
+ if (options?.tailLines !== void 0) {
537
+ const tail = Math.max(0, Math.trunc(options.tailLines));
538
+ lines = lines.slice(Math.max(0, lines.length - tail));
539
+ }
540
+ lines = applyTextMaskRules(lines, options?.mask);
541
+ const text = lines.join("\n");
542
+ return {
543
+ text,
544
+ hash: fnv1a32(text)
545
+ };
546
+ }
547
+ function snapshotSessionAnsi(terminal, options) {
548
+ if (options?.maxLines !== void 0 && options.tailLines !== void 0) throw new Error("snapshotAnsi: maxLines and tailLines are mutually exclusive");
549
+ let lines = renderAnsiLines(terminal, {
550
+ scope: options?.scope,
551
+ trimRight: options?.trimRight
552
+ });
553
+ if (options?.trimBottom ?? true) lines = trimBottomEmptyAnsiLines(lines);
554
+ if (options?.maxLines !== void 0) {
555
+ const max = Math.max(0, Math.trunc(options.maxLines));
556
+ lines = lines.slice(0, max);
557
+ }
558
+ if (options?.tailLines !== void 0) {
559
+ const tail = Math.max(0, Math.trunc(options.tailLines));
560
+ lines = lines.slice(Math.max(0, lines.length - tail));
561
+ }
562
+ if (options?.mask && options.mask.length > 0) {
563
+ const maskedPlain = applyTextMaskRules(lines.map((line) => line.plain), options.mask);
564
+ const maskedAnsi = applyTextMaskRules(lines.map((line) => line.ansi), options.mask);
565
+ lines = lines.map((line, idx) => ({
566
+ ...line,
567
+ plain: maskedPlain[idx] ?? "",
568
+ ansi: maskedAnsi[idx] ?? ""
569
+ }));
570
+ }
571
+ const ansi = lines.map((l) => l.ansi).join("\n");
572
+ return {
573
+ ansi,
574
+ plain: lines.map((l) => l.plain).join("\n"),
575
+ hash: fnv1a32(ansi),
576
+ lines
577
+ };
578
+ }
579
+ function trimBottomEmptyLines(lines) {
580
+ let end = lines.length;
581
+ while (end > 0 && lines[end - 1] === "") end -= 1;
582
+ return end === lines.length ? lines : lines.slice(0, end);
583
+ }
584
+ function trimBottomEmptyAnsiLines(lines) {
585
+ let end = lines.length;
586
+ while (end > 0) {
587
+ const line = lines[end - 1];
588
+ if (!(!line?.hasStyle && (line?.plain ?? "").trim() === "")) break;
589
+ end -= 1;
590
+ }
591
+ return end === lines.length ? lines : lines.slice(0, end);
592
+ }
593
+ //#endregion
594
+ //#region src/util/sleep.ts
595
+ async function sleep(ms) {
596
+ await new Promise((resolve) => {
597
+ setTimeout(resolve, ms);
598
+ });
599
+ }
600
+ //#endregion
601
+ //#region src/session/terminal_session_wait.ts
602
+ async function waitForSessionText(host, args) {
603
+ const startedAt = Date.now();
604
+ let closedSince = null;
605
+ while (Date.now() - startedAt <= args.timeoutMs) {
606
+ const snapshot = await host.snapshotText({
607
+ captureFrame: true,
608
+ scope: args.scope
609
+ });
610
+ if (args.text && snapshot.text.includes(args.text)) return {
611
+ found: true,
612
+ ...snapshot
613
+ };
614
+ if (args.regex && args.regex.test(snapshot.text)) return {
615
+ found: true,
616
+ ...snapshot
617
+ };
618
+ if (host.isClosed()) {
619
+ closedSince ??= Date.now();
620
+ const drainMs = Math.max(500, args.intervalMs * 4);
621
+ if (Date.now() - closedSince >= drainMs) break;
622
+ }
623
+ await sleep(args.intervalMs);
624
+ }
625
+ return {
626
+ found: false,
627
+ ...await host.snapshotText({
628
+ captureFrame: true,
629
+ scope: args.scope
630
+ })
631
+ };
632
+ }
633
+ async function waitForStableSessionScreen(host, args) {
634
+ const startedAt = Date.now();
635
+ let stableSince = null;
636
+ let lastHash = null;
637
+ while (Date.now() - startedAt <= args.timeoutMs) {
638
+ const snapshot = await host.snapshotText({ captureFrame: true });
639
+ if (snapshot.hash === lastHash) stableSince ??= Date.now();
640
+ else {
641
+ stableSince = null;
642
+ lastHash = snapshot.hash;
643
+ }
644
+ if (stableSince !== null && Date.now() - stableSince >= args.quietMs) return {
645
+ stable: true,
646
+ ...snapshot
647
+ };
648
+ await sleep(args.intervalMs);
649
+ }
650
+ return {
651
+ stable: false,
652
+ ...await host.snapshotText({ captureFrame: true })
653
+ };
654
+ }
655
+ //#endregion
592
656
  //#region src/session/terminal_session.ts
593
657
  const ESC = "\x1B";
594
658
  var TerminalSession = class {
@@ -688,23 +752,7 @@ var TerminalSession = class {
688
752
  }
689
753
  async snapshotText(options) {
690
754
  await this.flush();
691
- if (options?.maxLines !== void 0 && options.tailLines !== void 0) throw new Error("snapshotText: maxLines and tailLines are mutually exclusive");
692
- let lines = snapshotLines(this.terminal, {
693
- scope: options?.scope,
694
- trimRight: options?.trimRight
695
- });
696
- if (options?.trimBottom ?? true) lines = trimBottomEmptyLines(lines);
697
- if (options?.maxLines !== void 0) {
698
- const max = Math.max(0, Math.trunc(options.maxLines));
699
- lines = lines.slice(0, max);
700
- }
701
- if (options?.tailLines !== void 0) {
702
- const tail = Math.max(0, Math.trunc(options.tailLines));
703
- lines = lines.slice(Math.max(0, lines.length - tail));
704
- }
705
- lines = applyTextMaskRules(lines, options?.mask);
706
- const text = lines.join("\n");
707
- const hash = fnv1a32(text);
755
+ const { text, hash } = snapshotSessionText(this.terminal, options);
708
756
  if (options?.captureFrame ?? true) this.captureFrame(text, hash);
709
757
  return {
710
758
  text,
@@ -713,36 +761,7 @@ var TerminalSession = class {
713
761
  }
714
762
  async snapshotAnsi(options) {
715
763
  await this.flush();
716
- if (options?.maxLines !== void 0 && options.tailLines !== void 0) throw new Error("snapshotAnsi: maxLines and tailLines are mutually exclusive");
717
- let lines = renderAnsiLines(this.terminal, {
718
- scope: options?.scope,
719
- trimRight: options?.trimRight
720
- });
721
- if (options?.trimBottom ?? true) lines = trimBottomEmptyAnsiLines(lines);
722
- if (options?.maxLines !== void 0) {
723
- const max = Math.max(0, Math.trunc(options.maxLines));
724
- lines = lines.slice(0, max);
725
- }
726
- if (options?.tailLines !== void 0) {
727
- const tail = Math.max(0, Math.trunc(options.tailLines));
728
- lines = lines.slice(Math.max(0, lines.length - tail));
729
- }
730
- if (options?.mask && options.mask.length > 0) {
731
- const maskedPlain = applyTextMaskRules(lines.map((line) => line.plain), options.mask);
732
- const maskedAnsi = applyTextMaskRules(lines.map((line) => line.ansi), options.mask);
733
- lines = lines.map((line, idx) => ({
734
- ...line,
735
- plain: maskedPlain[idx] ?? "",
736
- ansi: maskedAnsi[idx] ?? ""
737
- }));
738
- }
739
- const ansi = lines.map((l) => l.ansi).join("\n");
740
- return {
741
- ansi,
742
- plain: lines.map((l) => l.plain).join("\n"),
743
- hash: fnv1a32(ansi),
744
- lines
745
- };
764
+ return snapshotSessionAnsi(this.terminal, options);
746
765
  }
747
766
  async snapshotGrid(options) {
748
767
  await this.flush();
@@ -771,57 +790,10 @@ var TerminalSession = class {
771
790
  return [...this.rawOutputRing];
772
791
  }
773
792
  async waitForText(args) {
774
- const startedAt = Date.now();
775
- let closedSince = null;
776
- while (Date.now() - startedAt <= args.timeoutMs) {
777
- const snapshot = await this.snapshotText({
778
- captureFrame: true,
779
- scope: args.scope
780
- });
781
- if (args.text && snapshot.text.includes(args.text)) return {
782
- found: true,
783
- ...snapshot
784
- };
785
- if (args.regex && args.regex.test(snapshot.text)) return {
786
- found: true,
787
- ...snapshot
788
- };
789
- if (this.isClosed()) {
790
- closedSince ??= Date.now();
791
- const drainMs = Math.max(500, args.intervalMs * 4);
792
- if (Date.now() - closedSince >= drainMs) break;
793
- }
794
- await sleep(args.intervalMs);
795
- }
796
- return {
797
- found: false,
798
- ...await this.snapshotText({
799
- captureFrame: true,
800
- scope: args.scope
801
- })
802
- };
793
+ return waitForSessionText(this, args);
803
794
  }
804
795
  async waitForStableScreen(args) {
805
- const startedAt = Date.now();
806
- let stableSince = null;
807
- let lastHash = null;
808
- while (Date.now() - startedAt <= args.timeoutMs) {
809
- const snapshot = await this.snapshotText({ captureFrame: true });
810
- if (snapshot.hash === lastHash) stableSince ??= Date.now();
811
- else {
812
- stableSince = null;
813
- lastHash = snapshot.hash;
814
- }
815
- if (stableSince !== null && Date.now() - stableSince >= args.quietMs) return {
816
- stable: true,
817
- ...snapshot
818
- };
819
- await sleep(args.intervalMs);
820
- }
821
- return {
822
- stable: false,
823
- ...await this.snapshotText({ captureFrame: true })
824
- };
796
+ return waitForStableSessionScreen(this, args);
825
797
  }
826
798
  close() {
827
799
  if (this.closed === null) this.closed = { type: "closed_by_user" };
@@ -875,19 +847,5 @@ var TerminalSession = class {
875
847
  return true;
876
848
  }
877
849
  };
878
- function trimBottomEmptyLines(lines) {
879
- let end = lines.length;
880
- while (end > 0 && lines[end - 1] === "") end -= 1;
881
- return end === lines.length ? lines : lines.slice(0, end);
882
- }
883
- function trimBottomEmptyAnsiLines(lines) {
884
- let end = lines.length;
885
- while (end > 0) {
886
- const line = lines[end - 1];
887
- if (!(!line?.hasStyle && (line?.plain ?? "").trim() === "")) break;
888
- end -= 1;
889
- }
890
- return end === lines.length ? lines : lines.slice(0, end);
891
- }
892
850
  //#endregion
893
- export { applyTextMaskRules as a, encodeSgrMouse as c, isDefaultStyle as d, styleKey as f, fnv1a32 as i, extractStyle as l, TraceRecorder as n, snapshotGrid as o, encodeKey as p, sleep as r, snapshotLines as s, TerminalSession as t, findMeaningfulEndCol as u };
851
+ export { fnv1a32 as a, encodeSgrMouse as c, TraceRecorder as i, encodeKey as l, sleep as n, snapshotGrid as o, applyTextMaskRules as r, snapshotLines as s, TerminalSession as t };