@simon_he/vue-tui 1.0.1 → 1.0.2

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/dist/cli.cjs CHANGED
@@ -3674,7 +3674,7 @@ function clamp(n, min, max) {
3674
3674
  function setFingerprintFn(buffer, fn) {
3675
3675
  buffer.fingerprintFn = fn;
3676
3676
  const len = buffer.rows * buffer.cols;
3677
- if (!len) {
3677
+ if (!fn || !len) {
3678
3678
  buffer.soaFingerprints = null;
3679
3679
  return;
3680
3680
  }
@@ -3692,7 +3692,12 @@ function setFingerprintFn(buffer, fn) {
3692
3692
  function getRowFingerprints(buffer, y) {
3693
3693
  if (!buffer.soaFingerprints)
3694
3694
  return null;
3695
- const physY = physicalRowIndex(buffer, y);
3695
+ if (buffer.rows <= 0 || buffer.cols <= 0)
3696
+ return null;
3697
+ const yy = Math.floor(y);
3698
+ if (!Number.isFinite(yy) || yy < 0 || yy >= buffer.rows)
3699
+ return null;
3700
+ const physY = physicalRowIndex(buffer, yy);
3696
3701
  const base = physY * buffer.cols;
3697
3702
  return buffer.soaFingerprints.subarray(base, base + buffer.cols);
3698
3703
  }
@@ -4988,11 +4993,17 @@ function createTerminal(opts) {
4988
4993
  },
4989
4994
  setFingerprintFn(fn) {
4990
4995
  assertNotDisposed();
4991
- base.setFingerprintFn(fn);
4996
+ const state = getPlaneState(plane);
4997
+ setFingerprintFn(state.buffer, fn);
4992
4998
  },
4993
4999
  getRowFingerprints(y) {
4994
5000
  assertNotDisposed();
4995
- return base.getRowFingerprints(y);
5001
+ const yy = Math.floor(y);
5002
+ const state = getPlaneState(plane);
5003
+ if (!Number.isFinite(yy) || yy < 0 || yy >= state.buffer.rows) {
5004
+ throw new RangeError("Row out of bounds");
5005
+ }
5006
+ return getRowFingerprints(state.buffer, yy);
4996
5007
  }
4997
5008
  };
4998
5009
  planeTerminals.set(plane, api);
@@ -5187,7 +5198,11 @@ function createTerminal(opts) {
5187
5198
  },
5188
5199
  getRowFingerprints(y) {
5189
5200
  assertNotDisposed();
5190
- return getRowFingerprints(compositeBuffer, y);
5201
+ const yy = Math.floor(y);
5202
+ if (!Number.isFinite(yy) || yy < 0 || yy >= compositeBuffer.rows) {
5203
+ throw new RangeError("Row out of bounds");
5204
+ }
5205
+ return getRowFingerprints(compositeBuffer, yy);
5191
5206
  }
5192
5207
  };
5193
5208
  base[TERMINAL_PLANE_INTERNALS] = {
@@ -9327,6 +9342,7 @@ var SYNC_START = "\x1B[?2026h";
9327
9342
  var SYNC_END = "\x1B[?2026l";
9328
9343
  var OSC8_OPEN = (href) => `\x1B]8;;${href}\x07`;
9329
9344
  var OSC8_CLOSE = "\x1B]8;;\x07";
9345
+ var stdoutFingerprintOwners = /* @__PURE__ */ new WeakMap();
9330
9346
  function createStdoutRenderer(terminal, options) {
9331
9347
  var _a, _b;
9332
9348
  const env = ((_a = import_node_process6.default) == null ? void 0 : _a.env) ?? {};
@@ -9336,9 +9352,17 @@ function createStdoutRenderer(terminal, options) {
9336
9352
  if (!output)
9337
9353
  throw new Error("createStdoutRenderer requires a Node stdout-like output");
9338
9354
  const out = output;
9355
+ const outputFd = Number(out.fd);
9356
+ const canUseSyncStdout = (options == null ? void 0 : options.output) == null && Number.isInteger(outputFd) && outputFd === 1;
9357
+ const fingerprintOwner = Symbol("stdout-renderer");
9358
+ let ownsFingerprintFn = false;
9359
+ const fingerprintTerminal = terminal;
9360
+ const canInstallTerminalFingerprintFn = () => typeof fingerprintTerminal.setFingerprintFn === "function" && typeof fingerprintTerminal.getRowFingerprints === "function";
9361
+ const outputIsTTY = Boolean(out.isTTY);
9362
+ const outputAllowsOsc8Links = out.isTTY !== false;
9339
9363
  const clear = (options == null ? void 0 : options.clear) ?? true;
9340
9364
  const hideCursor = (options == null ? void 0 : options.hideCursor) ?? true;
9341
- const altScreen = (options == null ? void 0 : options.altScreen) ?? Boolean(out.isTTY);
9365
+ const altScreen = (options == null ? void 0 : options.altScreen) ?? outputIsTTY;
9342
9366
  let defaultBg = (options == null ? void 0 : options.defaultBg) == null || (options == null ? void 0 : options.defaultBg) === "transparent" ? void 0 : (options == null ? void 0 : options.defaultBg) ?? "black";
9343
9367
  let palette = (options == null ? void 0 : options.palette) ?? null;
9344
9368
  const trackResize = (options == null ? void 0 : options.trackResize) ?? true;
@@ -9353,11 +9377,11 @@ function createStdoutRenderer(terminal, options) {
9353
9377
  function resolveUseSyncOutput() {
9354
9378
  if ((options == null ? void 0 : options.useSyncOutput) != null)
9355
9379
  return options.useSyncOutput;
9356
- const term = String(env.TERM ?? "").toLowerCase();
9380
+ const term2 = String(env.TERM ?? "").toLowerCase();
9357
9381
  const termProgram2 = String(env.TERM_PROGRAM ?? "").toLowerCase();
9358
9382
  const termProgramVersion = env.TERM_PROGRAM_VERSION;
9359
9383
  const isGhostty2 = "GHOSTTY_RESOURCES_DIR" in env;
9360
- const isProblematicTerminal = isGhostty2 || termProgram2.includes("apple_terminal") || term.includes("screen") && !term.includes("tmux");
9384
+ const isProblematicTerminal = isGhostty2 || termProgram2.includes("apple_terminal") || term2.includes("screen") && !term2.includes("tmux");
9361
9385
  if (isProblematicTerminal) {
9362
9386
  return false;
9363
9387
  }
@@ -9386,10 +9410,134 @@ function createStdoutRenderer(terminal, options) {
9386
9410
  const chunkThresholdBytes = 64 * 1024;
9387
9411
  const syncMaxBytes = 128 * 1024;
9388
9412
  const dirtyFullThreshold = 0.6;
9413
+ const DIRTY_SPAN_MERGE_GAP_CELLS = 2;
9414
+ const DIRTY_SPAN_MAX_PER_ROW = 12;
9415
+ const DIRTY_SPAN_FULL_ROW_THRESHOLD = 0.7;
9416
+ const EST_STYLE_SWITCH_BYTES = 8;
9417
+ const EST_CLEAR_TO_EOL_BYTES = 3;
9389
9418
  const disableCursorPos = false;
9390
9419
  const termProgram = String(env.TERM_PROGRAM ?? "").trim().toLowerCase();
9420
+ const term = String(env.TERM ?? "").trim().toLowerCase();
9421
+ const isKitty = "KITTY_WINDOW_ID" in env || termProgram.includes("kitty") || term.includes("kitty");
9422
+ const isAlacritty = "ALACRITTY_WINDOW_ID" in env || "ALACRITTY_LOG" in env || termProgram.includes("alacritty") || term.includes("alacritty");
9423
+ const isWezTerm = "WEZTERM_PANE" in env || "WEZTERM_EXECUTABLE" in env || termProgram.includes("wezterm");
9391
9424
  const isVscodeTerminal = termProgram === "vscode" || "VSCODE_PID" in env || "VSCODE_IPC_HOOK_CLI" in env;
9392
- const enableOsc8Links = out.isTTY !== false && !isVscodeTerminal;
9425
+ const useConservativeDirtyRows = Boolean(
9426
+ outputIsTTY && (isGhostty || isKitty || isAlacritty || isWezTerm)
9427
+ );
9428
+ const normalizeDirtyRowPatchMode = (value) => {
9429
+ const normalized = typeof value === "string" ? value.trim().toLowerCase() : value;
9430
+ return normalized === "auto" || normalized === "row" || normalized === "span" ? normalized : null;
9431
+ };
9432
+ const normalizeColumnDiffMode = (value) => {
9433
+ if (value === true)
9434
+ return "span";
9435
+ if (value === false)
9436
+ return "row";
9437
+ if (typeof value === "string" && value.trim().toLowerCase() === "auto")
9438
+ return "auto";
9439
+ return null;
9440
+ };
9441
+ const firstValidDirtyRowPatchMode = (...values) => {
9442
+ for (const value of values) {
9443
+ const mode = normalizeDirtyRowPatchMode(value);
9444
+ if (mode)
9445
+ return mode;
9446
+ }
9447
+ return null;
9448
+ };
9449
+ function resolveDirtyRowPatchMode() {
9450
+ const explicitMode = options == null ? void 0 : options.dirtyRowPatchMode;
9451
+ if (explicitMode != null) {
9452
+ const optionMode = normalizeDirtyRowPatchMode(explicitMode);
9453
+ if (!optionMode) {
9454
+ throw new Error(
9455
+ `Invalid dirtyRowPatchMode=${JSON.stringify(
9456
+ explicitMode
9457
+ )}; expected "auto", "row", or "span".`
9458
+ );
9459
+ }
9460
+ return optionMode;
9461
+ }
9462
+ const legacy = options == null ? void 0 : options.columnDiff;
9463
+ if (legacy !== void 0) {
9464
+ const legacyMode = normalizeColumnDiffMode(legacy);
9465
+ if (!legacyMode) {
9466
+ throw new Error(
9467
+ `Invalid columnDiff=${JSON.stringify(legacy)}; expected boolean or "auto".`
9468
+ );
9469
+ }
9470
+ return legacyMode;
9471
+ }
9472
+ const envMode = firstValidDirtyRowPatchMode(
9473
+ env.VUE_TUI_DIRTY_ROW_PATCH_MODE,
9474
+ env.DIMCODE_TUI_DIRTY_ROW_RENDER_MODE,
9475
+ env.DIMCODE_TUI_DIRTY_ROW_PATCH_MODE
9476
+ );
9477
+ if (envMode)
9478
+ return envMode;
9479
+ return "auto";
9480
+ }
9481
+ const dirtyRowPatchMode = resolveDirtyRowPatchMode();
9482
+ const dirtySpanConservativeMaxCells = (() => {
9483
+ if ((options == null ? void 0 : options.dirtySpanConservativeMaxCells) !== void 0) {
9484
+ const raw2 = options.dirtySpanConservativeMaxCells;
9485
+ const value2 = typeof raw2 === "number" ? raw2 : Number(raw2);
9486
+ if (!Number.isFinite(value2) || value2 <= 0) {
9487
+ throw new Error(
9488
+ `Invalid dirtySpanConservativeMaxCells=${JSON.stringify(
9489
+ raw2
9490
+ )}; expected a positive finite number.`
9491
+ );
9492
+ }
9493
+ return Math.floor(value2);
9494
+ }
9495
+ const raw = firstNonEmptyEnv(
9496
+ env,
9497
+ "VUE_TUI_DIRTY_SPAN_MAX_CELLS",
9498
+ "DIMCODE_TUI_DIRTY_SPAN_MAX_CELLS"
9499
+ );
9500
+ const value = Number(raw);
9501
+ return Number.isFinite(value) && value > 0 ? Math.floor(value) : 32;
9502
+ })();
9503
+ function resolveInternalColumnDiffMode() {
9504
+ const value = options == null ? void 0 : options.__columnDiffMode;
9505
+ if (value == null)
9506
+ return "auto";
9507
+ if (value === "auto" || value === "single-span" || value === "multi-span" || value === "full-row") {
9508
+ return value;
9509
+ }
9510
+ throw new Error(
9511
+ `Invalid __columnDiffMode=${JSON.stringify(
9512
+ value
9513
+ )}; expected "auto", "single-span", "multi-span", or "full-row".`
9514
+ );
9515
+ }
9516
+ const columnDiffMode = resolveInternalColumnDiffMode();
9517
+ function resolveEffectiveDirtyRowPatchMode() {
9518
+ switch (columnDiffMode) {
9519
+ case "full-row":
9520
+ return "row";
9521
+ case "single-span":
9522
+ return "single-span";
9523
+ case "multi-span":
9524
+ return "multi-span";
9525
+ case "auto":
9526
+ break;
9527
+ }
9528
+ switch (dirtyRowPatchMode) {
9529
+ case "row":
9530
+ return "row";
9531
+ case "span":
9532
+ case "auto":
9533
+ return "multi-span";
9534
+ }
9535
+ }
9536
+ const effectiveDirtyRowPatchMode = resolveEffectiveDirtyRowPatchMode();
9537
+ const shouldUseDirtySpans = () => effectiveDirtyRowPatchMode !== "row";
9538
+ const shouldUseMultiDirtySpans = () => effectiveDirtyRowPatchMode === "multi-span";
9539
+ const enableOsc8Links = outputAllowsOsc8Links && !isVscodeTerminal;
9540
+ const renderedHref = (value) => enableOsc8Links ? normalizeHref(value) : null;
9393
9541
  let disposed = false;
9394
9542
  let lastFrameTime = 0;
9395
9543
  let pendingRender = false;
@@ -9402,8 +9550,9 @@ function createStdoutRenderer(terminal, options) {
9402
9550
  let accumulatedDirtyCount = 0;
9403
9551
  let accumulatedDirtyMin = Number.POSITIVE_INFINITY;
9404
9552
  let accumulatedDirtyMax = -1;
9553
+ let accumulatedDirtyRowsRequireRepaint = false;
9405
9554
  let accumulatedScrollOperations = null;
9406
- const MIN_FRAME_MS = !out.isTTY ? 0 : 16;
9555
+ const MIN_FRAME_MS = outputIsTTY ? 16 : 0;
9407
9556
  function clampCellToViewport(cell, size) {
9408
9557
  const maxX = Math.max(0, Math.floor(size.cols) - 1);
9409
9558
  const maxY = Math.max(0, Math.floor(size.rows) - 1);
@@ -9421,60 +9570,74 @@ function createStdoutRenderer(terminal, options) {
9421
9570
  const env2 = ((_a2 = import_node_process6.default) == null ? void 0 : _a2.env) ?? {};
9422
9571
  return detectTerminalColorCapability({
9423
9572
  env: env2,
9424
- isTTY: Boolean(out.isTTY),
9573
+ isTTY: outputIsTTY,
9425
9574
  platform: String(((_b2 = import_node_process6.default) == null ? void 0 : _b2.platform) ?? "")
9426
9575
  }).mode;
9427
9576
  }
9428
9577
  const colorMode = resolveColorMode();
9429
9578
  const enableDim = colorMode === "truecolor";
9430
- const COLOR_INDEX = {
9431
- black: 1,
9432
- red: 2,
9433
- green: 3,
9434
- yellow: 4,
9435
- blue: 5,
9436
- magenta: 6,
9437
- cyan: 7,
9438
- white: 8,
9439
- blackBright: 9,
9440
- redBright: 10,
9441
- greenBright: 11,
9442
- yellowBright: 12,
9443
- blueBright: 13,
9444
- magentaBright: 14,
9445
- cyanBright: 15,
9446
- whiteBright: 16
9447
- };
9448
- const BUILTIN_COLOR_INDEX = { ...COLOR_INDEX };
9449
- let nextColorIdx = 17;
9450
- const MAX_COLOR_INDEX = 255;
9451
- function colorIndex(color) {
9452
- if (!color)
9453
- return 0;
9454
- let idx = COLOR_INDEX[color];
9455
- if (idx !== void 0)
9456
- return idx;
9457
- if (nextColorIdx > MAX_COLOR_INDEX)
9458
- return MAX_COLOR_INDEX;
9459
- idx = nextColorIdx++;
9460
- COLOR_INDEX[color] = idx;
9461
- return idx;
9462
- }
9463
9579
  let styleKeyCache = /* @__PURE__ */ new WeakMap();
9580
+ const styleKeyIndex = /* @__PURE__ */ new Map();
9581
+ let nextStyleKey = 1;
9582
+ const MAX_STYLE_KEYS = 131072;
9583
+ const MAX_UINT32_INTERN_ID = 4294967295;
9584
+ const STYLE_KEY_SEP = "";
9464
9585
  const normalizedHrefCache = /* @__PURE__ */ new Map();
9465
9586
  const MAX_HREF_CACHE = 2048;
9466
- const HREF_STYLE_FLAG = 1 << 21;
9587
+ let fingerprintInternResetRequiresBaseline = false;
9588
+ let fingerprintBaselineGeneration = 0;
9589
+ let activeRenderBaselineGeneration = 0;
9590
+ let terminalFingerprintFnDirty = false;
9591
+ let fingerprintInstallBlockedByExternalProvider = false;
9592
+ function invalidateFingerprintBaseline() {
9593
+ fpPrevValid = false;
9594
+ fingerprintInternResetRequiresBaseline = true;
9595
+ fingerprintBaselineGeneration++;
9596
+ }
9597
+ const activeRenderBaselineChanged = () => activeRenderBaselineGeneration !== fingerprintBaselineGeneration;
9598
+ function resetStyleKeyIndex(resetIds = false) {
9599
+ styleKeyCache = /* @__PURE__ */ new WeakMap();
9600
+ styleKeyIndex.clear();
9601
+ if (resetIds)
9602
+ nextStyleKey = 1;
9603
+ terminalFingerprintFnDirty = true;
9604
+ invalidateFingerprintBaseline();
9605
+ }
9606
+ function internStyleKey(signature) {
9607
+ const cached = styleKeyIndex.get(signature);
9608
+ if (cached !== void 0)
9609
+ return cached;
9610
+ if (styleKeyIndex.size >= MAX_STYLE_KEYS) {
9611
+ resetStyleKeyIndex();
9612
+ }
9613
+ if (nextStyleKey >= MAX_UINT32_INTERN_ID)
9614
+ resetStyleKeyIndex(true);
9615
+ const key = nextStyleKey++;
9616
+ styleKeyIndex.set(signature, key);
9617
+ return key;
9618
+ }
9619
+ function styleSignature(style) {
9620
+ return [
9621
+ style.fg ?? "",
9622
+ style.bg ?? defaultBg ?? "",
9623
+ style.bold ? "1" : "0",
9624
+ enableDim && style.dim ? "1" : "0",
9625
+ style.italic ? "1" : "0",
9626
+ style.underline ? "1" : "0",
9627
+ style.inverse ? "1" : "0",
9628
+ renderedHref(style.href) ? "1" : "0"
9629
+ ].join(STYLE_KEY_SEP);
9630
+ }
9467
9631
  function styleKey(style) {
9468
9632
  const cached = styleKeyCache.get(style);
9469
9633
  if (cached !== void 0)
9470
9634
  return cached;
9471
- const href = normalizeHref(style.href);
9472
- const key = colorIndex(style.fg) | colorIndex(style.bg ?? defaultBg) << 8 | (style.bold ? 1 << 16 : 0) | (enableDim && style.dim ? 1 << 17 : 0) | (style.italic ? 1 << 18 : 0) | (style.underline ? 1 << 19 : 0) | (style.inverse ? 1 << 20 : 0) | (href ? HREF_STYLE_FLAG : 0);
9635
+ const key = internStyleKey(styleSignature(style));
9473
9636
  styleKeyCache.set(style, key);
9474
9637
  return key;
9475
9638
  }
9476
9639
  function styleKeyFromParts(style) {
9477
- return colorIndex(style.fg) | colorIndex(style.bg ?? defaultBg) << 8 | (style.bold ? 1 << 16 : 0) | (enableDim && style.dim ? 1 << 17 : 0) | (style.italic ? 1 << 18 : 0) | (style.underline ? 1 << 19 : 0) | (style.inverse ? 1 << 20 : 0) | (normalizeHref(style.href) ? HREF_STYLE_FLAG : 0);
9640
+ return internStyleKey(styleSignature(style));
9478
9641
  }
9479
9642
  function normalizeHref(value) {
9480
9643
  if (typeof value !== "string")
@@ -9577,17 +9740,26 @@ function createStdoutRenderer(terminal, options) {
9577
9740
  let prevFP = new Uint32Array(0);
9578
9741
  let currentHrefIds = new Uint32Array(0);
9579
9742
  let prevHrefIds = new Uint32Array(0);
9743
+ let currentTextIds = new Uint32Array(0);
9744
+ let prevTextIds = new Uint32Array(0);
9580
9745
  let fpPrevValid = false;
9746
+ let nextCellTextId = 1;
9747
+ let blankTextId = 0;
9748
+ let emptyTextId = 0;
9749
+ let continuationTextId = 0;
9750
+ const cellTextIdCache = /* @__PURE__ */ new Map();
9751
+ const MAX_CELL_TEXT_IDS = 131072;
9581
9752
  let prevOverlayBlockedRows = [];
9582
9753
  let prevOverlayPartialRows = [];
9583
9754
  const hrefIndex = /* @__PURE__ */ new Map();
9584
9755
  let nextHrefId = 1;
9585
9756
  const MAX_HREF_IDS = 8192;
9586
9757
  let hrefIndexResetRequiresBaseline = false;
9587
- function resetHrefIndex() {
9758
+ function resetHrefIndex(resetIds = false) {
9588
9759
  hrefIndex.clear();
9589
- nextHrefId = 1;
9590
- fpPrevValid = false;
9760
+ if (resetIds)
9761
+ nextHrefId = 1;
9762
+ invalidateFingerprintBaseline();
9591
9763
  hrefIndexResetRequiresBaseline = true;
9592
9764
  }
9593
9765
  function ensureFingerprints(cols, rows) {
@@ -9600,52 +9772,457 @@ function createStdoutRenderer(terminal, options) {
9600
9772
  prevFP = new Uint32Array(len);
9601
9773
  currentHrefIds = new Uint32Array(len);
9602
9774
  prevHrefIds = new Uint32Array(len);
9775
+ currentTextIds = new Uint32Array(len);
9776
+ prevTextIds = new Uint32Array(len);
9603
9777
  fpPrevValid = false;
9604
9778
  prevOverlayBlockedRows = [];
9605
9779
  prevOverlayPartialRows = [];
9606
9780
  }
9607
- function charHash10(ch) {
9608
- if (ch.length <= 1)
9609
- return (ch.charCodeAt(0) || 0) & 1023;
9610
- let h2 = 33052;
9611
- for (let i = 0; i < ch.length; i++) {
9612
- h2 ^= ch.charCodeAt(i);
9613
- h2 = h2 * 257 & 65535;
9781
+ function cellFingerprintFromStyleKey(_ch, key) {
9782
+ return key >>> 0;
9783
+ }
9784
+ function cellFingerprint(_ch, style) {
9785
+ return styleKey(style) >>> 0;
9786
+ }
9787
+ function allocateCellTextId(text) {
9788
+ const id = nextCellTextId++;
9789
+ cellTextIdCache.set(text, id);
9790
+ return id;
9791
+ }
9792
+ function resetCellTextIdCache(resetIds = false) {
9793
+ cellTextIdCache.clear();
9794
+ if (resetIds)
9795
+ nextCellTextId = 1;
9796
+ blankTextId = allocateCellTextId(" ");
9797
+ emptyTextId = allocateCellTextId("");
9798
+ continuationTextId = nextCellTextId++;
9799
+ invalidateFingerprintBaseline();
9800
+ }
9801
+ function cellIdentityTextId(cell) {
9802
+ if (cell.continuation)
9803
+ return continuationTextId;
9804
+ if (cell.ch === "")
9805
+ return emptyTextId;
9806
+ return cellTextId(cell.ch);
9807
+ }
9808
+ function cellTextId(text) {
9809
+ let cached = cellTextIdCache.get(text);
9810
+ if (cached !== void 0)
9811
+ return cached;
9812
+ const mustResetTextIds = nextCellTextId >= MAX_UINT32_INTERN_ID - 2;
9813
+ if (cellTextIdCache.size >= MAX_CELL_TEXT_IDS || mustResetTextIds) {
9814
+ resetCellTextIdCache(mustResetTextIds);
9815
+ cached = cellTextIdCache.get(text);
9816
+ if (cached !== void 0)
9817
+ return cached;
9614
9818
  }
9615
- return h2 & 1023;
9819
+ return allocateCellTextId(text);
9616
9820
  }
9821
+ resetCellTextIdCache(true);
9617
9822
  function hrefId(href) {
9618
9823
  if (!href)
9619
9824
  return 0;
9620
9825
  const cached = hrefIndex.get(href);
9621
9826
  if (cached != null)
9622
9827
  return cached;
9623
- if (hrefIndex.size >= MAX_HREF_IDS || nextHrefId >= 4294967295) {
9624
- resetHrefIndex();
9828
+ const mustResetHrefIds = nextHrefId >= MAX_UINT32_INTERN_ID;
9829
+ if (hrefIndex.size >= MAX_HREF_IDS || mustResetHrefIds) {
9830
+ resetHrefIndex(mustResetHrefIds);
9625
9831
  }
9626
9832
  const id = nextHrefId++;
9627
9833
  hrefIndex.set(href, id);
9628
9834
  return id;
9629
9835
  }
9630
- function cellFingerprint(ch, style) {
9631
- return styleKey(style) << 10 | charHash10(ch);
9836
+ const BLANK_STYLE = Object.freeze({});
9837
+ const BLANK_CELL = Object.freeze({
9838
+ ch: " ",
9839
+ width: 1,
9840
+ style: BLANK_STYLE
9841
+ });
9842
+ const cellAt = (row, x) => row[x] ?? BLANK_CELL;
9843
+ function rowFingerprintsForThisRenderer(y) {
9844
+ var _a2;
9845
+ if (terminalFingerprintFnDirty)
9846
+ return null;
9847
+ if (!ownsFingerprintFn || stdoutFingerprintOwners.get(terminal) !== fingerprintOwner)
9848
+ return null;
9849
+ return ((_a2 = fingerprintTerminal.getRowFingerprints) == null ? void 0 : _a2.call(fingerprintTerminal, y)) ?? null;
9850
+ }
9851
+ function hasActiveExternalFingerprintProvider() {
9852
+ if (!canInstallTerminalFingerprintFn())
9853
+ return false;
9854
+ if (stdoutFingerprintOwners.has(terminal))
9855
+ return false;
9856
+ try {
9857
+ const rows = terminal.size().rows;
9858
+ if (rows <= 0)
9859
+ return false;
9860
+ return fingerprintTerminal.getRowFingerprints(0) != null;
9861
+ } catch {
9862
+ return false;
9863
+ }
9864
+ }
9865
+ function shouldAttemptFingerprintInstall() {
9866
+ return !fingerprintInstallBlockedByExternalProvider || !hasActiveExternalFingerprintProvider();
9632
9867
  }
9633
9868
  function fingerprintRow(row, y, cols) {
9634
- const rowFP = terminal.getRowFingerprints(y);
9869
+ const rowFP = rowFingerprintsForThisRenderer(y);
9635
9870
  const base = y * fpCols;
9636
- if (rowFP && rowFP.length >= cols) {
9871
+ if (rowFP && rowFP.length >= cols && row.length >= cols) {
9637
9872
  currentFP.set(rowFP.subarray(0, cols), base);
9638
9873
  } else {
9639
9874
  for (let x = 0; x < cols; x++) {
9640
- const cell = row[x];
9875
+ const cell = cellAt(row, x);
9641
9876
  currentFP[base + x] = cellFingerprint(cell.ch, cell.style);
9642
9877
  }
9643
9878
  }
9644
9879
  for (let x = 0; x < cols; x++) {
9645
- const cell = row[x];
9646
- currentHrefIds[base + x] = hrefId(normalizeHref(cell.style.href));
9880
+ const cell = cellAt(row, x);
9881
+ currentHrefIds[base + x] = hrefId(renderedHref(cell.style.href));
9882
+ currentTextIds[base + x] = cellIdentityTextId(cell);
9883
+ }
9884
+ }
9885
+ function snapshotRowFingerprints(row, y, cols) {
9886
+ const fp = new Uint32Array(cols);
9887
+ const hrefIds = new Uint32Array(cols);
9888
+ const textIds = new Uint32Array(cols);
9889
+ const rowFP = rowFingerprintsForThisRenderer(y);
9890
+ if (rowFP && rowFP.length >= cols && row.length >= cols) {
9891
+ fp.set(rowFP.subarray(0, cols));
9892
+ } else {
9893
+ for (let x = 0; x < cols; x++) {
9894
+ const cell = cellAt(row, x);
9895
+ fp[x] = cellFingerprint(cell.ch, cell.style);
9896
+ }
9647
9897
  }
9898
+ for (let x = 0; x < cols; x++) {
9899
+ const cell = cellAt(row, x);
9900
+ hrefIds[x] = hrefId(renderedHref(cell.style.href));
9901
+ textIds[x] = cellIdentityTextId(cell);
9902
+ }
9903
+ return { fp, hrefIds, textIds };
9648
9904
  }
9905
+ const currentCellMatchesPrevious = (base, x) => {
9906
+ return currentFP[base + x] === prevFP[base + x] && currentTextIds[base + x] === prevTextIds[base + x] && currentHrefIds[base + x] === prevHrefIds[base + x];
9907
+ };
9908
+ const cellMatchesPreviousAt = (currentBase, previousBase, x) => {
9909
+ return currentFP[currentBase + x] === prevFP[previousBase + x] && currentTextIds[currentBase + x] === prevTextIds[previousBase + x] && currentHrefIds[currentBase + x] === prevHrefIds[previousBase + x];
9910
+ };
9911
+ const referenceCellMatchesCurrent = (row, rowFP, base, x, cols, referenceFP, referenceHrefIds, referenceTextIds) => {
9912
+ const cell = cellAt(row, x);
9913
+ const fingerprint = rowFP && rowFP.length >= cols && row.length >= cols ? rowFP[x] : cellFingerprint(cell.ch, cell.style);
9914
+ return fingerprint === referenceFP[base + x] && cellIdentityTextId(cell) === referenceTextIds[base + x] && hrefId(renderedHref(cell.style.href)) === referenceHrefIds[base + x];
9915
+ };
9916
+ const fullRowDirtySpan = (cols) => {
9917
+ return cols > 0 ? [{ startX: 0, endXExclusive: cols }] : [];
9918
+ };
9919
+ const dirtySpanWidth = (span) => Math.max(0, span.endXExclusive - span.startX);
9920
+ const dirtySpanCoverage = (spans) => {
9921
+ let total = 0;
9922
+ for (const span of spans)
9923
+ total += dirtySpanWidth(span);
9924
+ return total;
9925
+ };
9926
+ const estimatedCursorMoveBytes = (y, x) => {
9927
+ return 3 + String(y + 1).length + 1 + String(x + 1).length + 1;
9928
+ };
9929
+ const estimatedRenderableTextBytes = (row, startX, endXExclusive, cols) => {
9930
+ const start = Math.max(0, Math.min(cols, Math.floor(startX)));
9931
+ const end = Math.max(start, Math.min(cols, Math.floor(endXExclusive)));
9932
+ let bytes = 0;
9933
+ for (let x = start; x < end; x++) {
9934
+ const cell = cellAt(row, x);
9935
+ if (cell.continuation)
9936
+ continue;
9937
+ bytes += import_node_buffer3.Buffer.byteLength(cell.ch || " ", "utf8");
9938
+ }
9939
+ return bytes;
9940
+ };
9941
+ const estimatedSpanPatchBytes = (row, spans, y, cols, clearToEol) => {
9942
+ let bytes = 0;
9943
+ for (const span of spans) {
9944
+ bytes += estimatedCursorMoveBytes(y, span.startX);
9945
+ bytes += estimatedRenderableTextBytes(row, span.startX, span.endXExclusive, cols);
9946
+ bytes += EST_STYLE_SWITCH_BYTES;
9947
+ }
9948
+ if (clearToEol)
9949
+ bytes += EST_CLEAR_TO_EOL_BYTES;
9950
+ return bytes;
9951
+ };
9952
+ const estimatedContiguousPatchBytes = (row, spans, y, cols, clearToEol) => {
9953
+ if (!spans.length)
9954
+ return 0;
9955
+ const first = spans[0];
9956
+ const last = spans[spans.length - 1];
9957
+ return estimatedCursorMoveBytes(y, first.startX) + estimatedRenderableTextBytes(row, first.startX, last.endXExclusive, cols) + EST_STYLE_SWITCH_BYTES + (clearToEol ? EST_CLEAR_TO_EOL_BYTES : 0);
9958
+ };
9959
+ const estimatedFullRowPatchBytes = (row, y, cols, clearToEol) => {
9960
+ return estimatedCursorMoveBytes(y, 0) + EST_STYLE_SWITCH_BYTES + estimatedRenderableTextBytes(row, 0, cols, cols) + (clearToEol ? EST_CLEAR_TO_EOL_BYTES : 0);
9961
+ };
9962
+ const resolveDirtySpanRenderMode = (row, spans, y, cols, clearToEol) => {
9963
+ if (spans.length <= 0)
9964
+ return "spans";
9965
+ const forceSpanPatch = dirtyRowPatchMode === "span" || columnDiffMode === "single-span" || columnDiffMode === "multi-span";
9966
+ const coverage = dirtySpanCoverage(spans);
9967
+ const fullRowCoverageLimit = Math.max(1, Math.floor(cols * DIRTY_SPAN_FULL_ROW_THRESHOLD));
9968
+ if (!forceSpanPatch && dirtyRowPatchMode === "auto" && useConservativeDirtyRows && coverage > dirtySpanConservativeMaxCells) {
9969
+ return "row";
9970
+ }
9971
+ if (!forceSpanPatch && coverage >= fullRowCoverageLimit) {
9972
+ return "row";
9973
+ }
9974
+ const fullRowCost = estimatedFullRowPatchBytes(row, y, cols, clearToEol);
9975
+ if (spans.length === 1) {
9976
+ const spanCost = estimatedSpanPatchBytes(row, spans, y, cols, clearToEol);
9977
+ return forceSpanPatch || spanCost < fullRowCost ? "spans" : "row";
9978
+ }
9979
+ const multiCost = estimatedSpanPatchBytes(row, spans, y, cols, clearToEol);
9980
+ const first = spans[0];
9981
+ const last = spans[spans.length - 1];
9982
+ const contiguousWidth = Math.max(0, last.endXExclusive - first.startX);
9983
+ const canUseContiguous = forceSpanPatch || contiguousWidth < fullRowCoverageLimit;
9984
+ const contiguousCost = canUseContiguous ? estimatedContiguousPatchBytes(row, spans, y, cols, clearToEol) : Number.POSITIVE_INFINITY;
9985
+ const tooManySpans = !forceSpanPatch && spans.length > DIRTY_SPAN_MAX_PER_ROW;
9986
+ const effectiveMultiCost = tooManySpans ? Number.POSITIVE_INFINITY : multiCost;
9987
+ if (forceSpanPatch) {
9988
+ return contiguousCost < multiCost ? "contiguous" : "spans";
9989
+ }
9990
+ if (contiguousCost < effectiveMultiCost && contiguousCost < fullRowCost) {
9991
+ return "contiguous";
9992
+ }
9993
+ if (!tooManySpans && multiCost < fullRowCost) {
9994
+ return "spans";
9995
+ }
9996
+ return "row";
9997
+ };
9998
+ const expandSpanStart = (row, startX, cols) => {
9999
+ var _a2;
10000
+ let x = Math.max(0, Math.min(cols, Math.floor(startX)));
10001
+ while (x > 0 && ((_a2 = row[x]) == null ? void 0 : _a2.continuation))
10002
+ x--;
10003
+ const prev = row[x - 1];
10004
+ if (prev && prev.width === 2 && !prev.continuation)
10005
+ x--;
10006
+ return Math.max(0, x);
10007
+ };
10008
+ const expandSpanEnd = (row, endXExclusive, cols) => {
10009
+ var _a2;
10010
+ let x = Math.max(0, Math.min(cols, Math.floor(endXExclusive)));
10011
+ const last = row[x - 1];
10012
+ if (last && last.width === 2 && !last.continuation)
10013
+ x++;
10014
+ while (x < cols && ((_a2 = row[x]) == null ? void 0 : _a2.continuation))
10015
+ x++;
10016
+ return Math.min(cols, x);
10017
+ };
10018
+ const previousCellLooksLikeWideContinuation = (referenceTextIds, base, x) => {
10019
+ if (x <= 0 || x >= fpCols)
10020
+ return false;
10021
+ return referenceTextIds[base + x] === continuationTextId;
10022
+ };
10023
+ const expandSpanForReferenceWideGlyphs = (span, referenceTextIds, base, cols) => {
10024
+ let startX = span.startX;
10025
+ let endXExclusive = span.endXExclusive;
10026
+ const limit = Math.max(0, Math.min(cols, fpCols));
10027
+ if (startX > 0) {
10028
+ if (previousCellLooksLikeWideContinuation(referenceTextIds, base, startX)) {
10029
+ startX--;
10030
+ } else if (previousCellLooksLikeWideContinuation(referenceTextIds, base, startX - 1)) {
10031
+ startX = Math.max(0, startX - 2);
10032
+ }
10033
+ }
10034
+ while (endXExclusive < limit) {
10035
+ if (!previousCellLooksLikeWideContinuation(referenceTextIds, base, endXExclusive)) {
10036
+ break;
10037
+ }
10038
+ endXExclusive++;
10039
+ }
10040
+ return { startX, endXExclusive };
10041
+ };
10042
+ const widenSpanToCurrentGlyphBoundaries = (row, span, cols) => {
10043
+ return {
10044
+ startX: expandSpanStart(row, span.startX, cols),
10045
+ endXExclusive: expandSpanEnd(row, span.endXExclusive, cols)
10046
+ };
10047
+ };
10048
+ const expandSpansForReferenceWideGlyphs = (spans, row, y, cols, referenceTextIds) => {
10049
+ if (!spans.length || !fpCols || y < 0 || y >= fpRows)
10050
+ return spans;
10051
+ const base = y * fpCols;
10052
+ const expanded = [];
10053
+ for (const span of spans) {
10054
+ const next = widenSpanToCurrentGlyphBoundaries(
10055
+ row,
10056
+ expandSpanForReferenceWideGlyphs(span, referenceTextIds, base, cols),
10057
+ cols
10058
+ );
10059
+ const prev = expanded[expanded.length - 1];
10060
+ if (prev && next.startX <= prev.endXExclusive + DIRTY_SPAN_MERGE_GAP_CELLS) {
10061
+ expanded[expanded.length - 1] = {
10062
+ startX: prev.startX,
10063
+ endXExclusive: Math.max(prev.endXExclusive, next.endXExclusive)
10064
+ };
10065
+ continue;
10066
+ }
10067
+ expanded.push(next);
10068
+ }
10069
+ return expanded;
10070
+ };
10071
+ const pushDirtySpan = (spans, row, startX, endXExclusive, cols) => {
10072
+ let expandedStart = Math.max(0, Math.min(cols, startX));
10073
+ let expandedEnd = Math.max(expandedStart, Math.min(cols, endXExclusive));
10074
+ if (expandedEnd <= expandedStart)
10075
+ return;
10076
+ expandedStart = expandSpanStart(row, expandedStart, cols);
10077
+ expandedEnd = expandSpanEnd(row, expandedEnd, cols);
10078
+ expandedStart = Math.max(0, Math.min(cols, expandedStart));
10079
+ expandedEnd = Math.max(expandedStart, Math.min(cols, expandedEnd));
10080
+ if (expandedEnd <= expandedStart)
10081
+ return;
10082
+ const prev = spans[spans.length - 1];
10083
+ if (prev && expandedStart <= prev.endXExclusive + DIRTY_SPAN_MERGE_GAP_CELLS) {
10084
+ spans[spans.length - 1] = {
10085
+ startX: prev.startX,
10086
+ endXExclusive: Math.max(prev.endXExclusive, expandedEnd)
10087
+ };
10088
+ return;
10089
+ }
10090
+ spans.push({
10091
+ startX: expandedStart,
10092
+ endXExclusive: expandedEnd
10093
+ });
10094
+ };
10095
+ const collectChangedSpans = (row, cols, changedAt) => {
10096
+ const limit = Math.max(0, cols);
10097
+ if (limit <= 0)
10098
+ return [];
10099
+ const spans = [];
10100
+ let startX = -1;
10101
+ for (let x = 0; x < limit; x++) {
10102
+ if (changedAt(x)) {
10103
+ if (startX < 0)
10104
+ startX = x;
10105
+ continue;
10106
+ }
10107
+ if (startX >= 0) {
10108
+ pushDirtySpan(spans, row, startX, x, cols);
10109
+ startX = -1;
10110
+ }
10111
+ }
10112
+ if (startX >= 0) {
10113
+ pushDirtySpan(spans, row, startX, limit, cols);
10114
+ }
10115
+ return spans;
10116
+ };
10117
+ const resolveChangedSpans = (row, y, cols, nextSnapshot) => {
10118
+ if (!fpCols || cols !== fpCols || y >= fpRows || !fpPrevValid) {
10119
+ return fullRowDirtySpan(cols);
10120
+ }
10121
+ const base = y * fpCols;
10122
+ let spans;
10123
+ if (!nextSnapshot) {
10124
+ spans = expandSpansForReferenceWideGlyphs(
10125
+ collectChangedSpans(row, cols, (x) => !currentCellMatchesPrevious(base, x)),
10126
+ row,
10127
+ y,
10128
+ cols,
10129
+ prevTextIds
10130
+ );
10131
+ } else {
10132
+ spans = expandSpansForReferenceWideGlyphs(
10133
+ collectChangedSpans(row, cols, (x) => {
10134
+ return nextSnapshot.fp[x] !== prevFP[base + x] || nextSnapshot.textIds[x] !== prevTextIds[base + x] || nextSnapshot.hrefIds[x] !== prevHrefIds[base + x];
10135
+ }),
10136
+ row,
10137
+ y,
10138
+ cols,
10139
+ prevTextIds
10140
+ );
10141
+ }
10142
+ return activeRenderBaselineChanged() ? fullRowDirtySpan(cols) : spans;
10143
+ };
10144
+ const resolveChangedSpansAgainstFill = (row, y, cols, fillFingerprint, fillTextId = blankTextId, fillHrefId = 0) => {
10145
+ if (!fpCols || cols !== fpCols || y >= fpRows) {
10146
+ return fullRowDirtySpan(cols);
10147
+ }
10148
+ const base = y * fpCols;
10149
+ const spans = collectChangedSpans(row, cols, (x) => {
10150
+ return currentFP[base + x] !== fillFingerprint || currentTextIds[base + x] !== fillTextId || currentHrefIds[base + x] !== fillHrefId;
10151
+ });
10152
+ return activeRenderBaselineChanged() ? fullRowDirtySpan(cols) : spans;
10153
+ };
10154
+ const resolveChangedSpansAgainstReference = (row, y, cols, referenceFP, referenceHrefIds, referenceTextIds) => {
10155
+ if (!fpCols || cols !== fpCols || y >= fpRows) {
10156
+ return fullRowDirtySpan(cols);
10157
+ }
10158
+ const rowFP = rowFingerprintsForThisRenderer(y);
10159
+ const base = y * fpCols;
10160
+ const spans = expandSpansForReferenceWideGlyphs(
10161
+ collectChangedSpans(row, cols, (x) => {
10162
+ return !referenceCellMatchesCurrent(
10163
+ row,
10164
+ rowFP,
10165
+ base,
10166
+ x,
10167
+ cols,
10168
+ referenceFP,
10169
+ referenceHrefIds,
10170
+ referenceTextIds
10171
+ );
10172
+ }),
10173
+ row,
10174
+ y,
10175
+ cols,
10176
+ referenceTextIds
10177
+ );
10178
+ return activeRenderBaselineChanged() ? fullRowDirtySpan(cols) : spans;
10179
+ };
10180
+ const isEolClearEquivalentBlankCell = (cell, defaultBlankStyleKey) => {
10181
+ if (!cell)
10182
+ return true;
10183
+ if (cell.continuation)
10184
+ return false;
10185
+ const ch = cell.ch || " ";
10186
+ if (ch !== " ")
10187
+ return false;
10188
+ if (cell.width !== 1)
10189
+ return false;
10190
+ if (renderedHref(cell.style.href))
10191
+ return false;
10192
+ return styleKey(cell.style) === defaultBlankStyleKey;
10193
+ };
10194
+ const lastExplicitPaintColumnInRow = (row, cols, defaultBlankStyleKey) => {
10195
+ const limit = Math.max(0, cols);
10196
+ for (let x = limit - 1; x >= 0; x--) {
10197
+ const cell = cellAt(row, x);
10198
+ if (!isEolClearEquivalentBlankCell(cell, defaultBlankStyleKey)) {
10199
+ if (cell && !cell.continuation && cell.width === 2 && x + 1 < limit) {
10200
+ return x + 1;
10201
+ }
10202
+ return x;
10203
+ }
10204
+ }
10205
+ return -1;
10206
+ };
10207
+ const shouldRewriteRowTail = (row, y, cols, referenceFP, referenceHrefIds, referenceTextIds, defaultBlankStyleKey = styleKeyFromParts({ bg: defaultBg })) => {
10208
+ if (!fpCols || y >= fpRows)
10209
+ return true;
10210
+ const currentTailStart = Math.max(
10211
+ 0,
10212
+ Math.min(cols, lastExplicitPaintColumnInRow(row, cols, defaultBlankStyleKey) + 1)
10213
+ );
10214
+ if (currentTailStart >= cols)
10215
+ return false;
10216
+ const blankFP = cellFingerprintFromStyleKey(" ", defaultBlankStyleKey);
10217
+ const base = y * fpCols;
10218
+ const limit = Math.min(cols, fpCols);
10219
+ for (let x = currentTailStart; x < limit; x++) {
10220
+ if (referenceFP[base + x] !== blankFP || referenceTextIds[base + x] !== blankTextId || referenceHrefIds[base + x] !== 0) {
10221
+ return true;
10222
+ }
10223
+ }
10224
+ return false;
10225
+ };
9649
10226
  const rowCursorToCol1 = [];
9650
10227
  const rowClearToEol = [];
9651
10228
  const rowTextPartsScratch = [];
@@ -9721,15 +10298,16 @@ function createStdoutRenderer(terminal, options) {
9721
10298
  rowClearToEol[y] = `\x1B[${y + 1};1H\x1B[K`;
9722
10299
  }
9723
10300
  };
9724
- const rowHasNonDefaultBlankCell = (source, y, cols) => {
10301
+ const rowHasNonDefaultBlankCell = (sourceFP, sourceTextIds, sourceHrefIds, y, cols) => {
9725
10302
  if (!fpCols || y < 0 || y >= fpRows)
9726
10303
  return false;
9727
- const blankFP = styleKeyFromParts({ bg: defaultBg }) << 10 | charHash10(" ");
10304
+ const blankFP = cellFingerprintFromStyleKey(" ", styleKeyFromParts({ bg: defaultBg }));
9728
10305
  const base = y * fpCols;
9729
10306
  const limit = Math.min(cols, fpCols);
9730
10307
  for (let x = 0; x < limit; x++) {
9731
- if (source[base + x] !== blankFP)
10308
+ if (sourceFP[base + x] !== blankFP || sourceTextIds[base + x] !== blankTextId || sourceHrefIds[base + x] !== 0) {
9732
10309
  return true;
10310
+ }
9733
10311
  }
9734
10312
  return false;
9735
10313
  };
@@ -9754,7 +10332,7 @@ function createStdoutRenderer(terminal, options) {
9754
10332
  return false;
9755
10333
  if (isGhostty || isVscodeTerminal)
9756
10334
  return false;
9757
- return out.isTTY !== false;
10335
+ return outputIsTTY;
9758
10336
  })();
9759
10337
  function prepareExplicitScrollOperations(operations, blockedRows) {
9760
10338
  if (!operations.length || !(blockedRows == null ? void 0 : blockedRows.size)) {
@@ -9880,7 +10458,7 @@ function createStdoutRenderer(terminal, options) {
9880
10458
  const srcY = y + delta;
9881
10459
  if (srcY < band.start || srcY >= band.end)
9882
10460
  continue;
9883
- const informative = rowHasNonDefaultBlankCell(currentFP, y, cols) || rowHasNonDefaultBlankCell(prevFP, srcY, cols);
10461
+ const informative = rowHasNonDefaultBlankCell(currentFP, currentTextIds, currentHrefIds, y, cols) || rowHasNonDefaultBlankCell(prevFP, prevTextIds, prevHrefIds, srcY, cols);
9884
10462
  if (!informative)
9885
10463
  continue;
9886
10464
  checked++;
@@ -9888,7 +10466,7 @@ function createStdoutRenderer(terminal, options) {
9888
10466
  const prevBase = srcY * fpCols;
9889
10467
  let rowMatch = true;
9890
10468
  for (let x = 0; x < cols; x++) {
9891
- if (currentFP[curBase + x] !== prevFP[prevBase + x] || currentHrefIds[curBase + x] !== prevHrefIds[prevBase + x]) {
10469
+ if (!cellMatchesPreviousAt(curBase, prevBase, x)) {
9892
10470
  rowMatch = false;
9893
10471
  break;
9894
10472
  }
@@ -9919,7 +10497,7 @@ function createStdoutRenderer(terminal, options) {
9919
10497
  const prevBase = srcY * fpCols;
9920
10498
  let rowMatch = true;
9921
10499
  for (let x = 0; x < cols; x++) {
9922
- if (currentFP[curBase + x] !== prevFP[prevBase + x] || currentHrefIds[curBase + x] !== prevHrefIds[prevBase + x]) {
10500
+ if (!cellMatchesPreviousAt(curBase, prevBase, x)) {
9923
10501
  rowMatch = false;
9924
10502
  break;
9925
10503
  }
@@ -9939,43 +10517,96 @@ function createStdoutRenderer(terminal, options) {
9939
10517
  }
9940
10518
  return null;
9941
10519
  }
9942
- function writeChunked(data) {
9943
- if (!isGhostty) {
9944
- if (data.length <= chunkSize) {
9945
- out.write(data);
9946
- return;
10520
+ const isHighSurrogate = (code) => code >= 55296 && code <= 56319;
10521
+ const isLowSurrogate = (code) => code >= 56320 && code <= 57343;
10522
+ const codePointEnd = (data, index) => {
10523
+ const code = data.charCodeAt(index);
10524
+ if (isHighSurrogate(code) && index + 1 < data.length) {
10525
+ const next = data.charCodeAt(index + 1);
10526
+ if (isLowSurrogate(next))
10527
+ return index + 2;
10528
+ }
10529
+ return index + 1;
10530
+ };
10531
+ const codePointBefore = (data, index) => {
10532
+ if (index <= 0)
10533
+ return null;
10534
+ const prev = data.charCodeAt(index - 1);
10535
+ if (isLowSurrogate(prev) && index - 2 >= 0) {
10536
+ const high = data.charCodeAt(index - 2);
10537
+ if (isHighSurrogate(high))
10538
+ return data.codePointAt(index - 2) ?? null;
10539
+ }
10540
+ return data.codePointAt(index - 1) ?? null;
10541
+ };
10542
+ const isGraphemeContinuationCodePoint = (codePoint) => codePoint >= 768 && codePoint <= 879 || codePoint >= 6832 && codePoint <= 6911 || codePoint >= 7616 && codePoint <= 7679 || codePoint >= 8400 && codePoint <= 8447 || codePoint >= 65056 && codePoint <= 65071 || codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999 || codePoint >= 127995 && codePoint <= 127999 || codePoint >= 917536 && codePoint <= 917631;
10543
+ const shouldAttachToPreviousGrapheme = (data, index) => {
10544
+ const codePoint = data.codePointAt(index);
10545
+ if (codePoint == null)
10546
+ return false;
10547
+ if (codePoint === 8205)
10548
+ return true;
10549
+ if (codePointBefore(data, index) === 8205)
10550
+ return true;
10551
+ return isGraphemeContinuationCodePoint(codePoint);
10552
+ };
10553
+ const safeChunkEnd = (data, start, maxBytes) => {
10554
+ let end = start;
10555
+ let bytes = 0;
10556
+ const limit = Math.max(1, Math.floor(maxBytes));
10557
+ while (end < data.length) {
10558
+ const next = codePointEnd(data, end);
10559
+ const partBytes = import_node_buffer3.Buffer.byteLength(data.slice(end, next), "utf8");
10560
+ if (end > start && bytes + partBytes > limit && !shouldAttachToPreviousGrapheme(data, end)) {
10561
+ break;
10562
+ }
10563
+ bytes += partBytes;
10564
+ end = next;
10565
+ while (end < data.length && shouldAttachToPreviousGrapheme(data, end)) {
10566
+ const attachedEnd = codePointEnd(data, end);
10567
+ bytes += import_node_buffer3.Buffer.byteLength(data.slice(end, attachedEnd), "utf8");
10568
+ end = attachedEnd;
9947
10569
  }
9948
- for (let i = 0; i < data.length; i += chunkSize) {
9949
- const chunk = data.slice(i, i + chunkSize);
9950
- out.write(chunk);
10570
+ if (bytes >= limit && (end >= data.length || !shouldAttachToPreviousGrapheme(data, end))) {
10571
+ break;
9951
10572
  }
10573
+ }
10574
+ return end > start ? end : codePointEnd(data, start);
10575
+ };
10576
+ function writeChunked(data) {
10577
+ if (import_node_buffer3.Buffer.byteLength(data, "utf8") <= chunkSize) {
10578
+ out.write(data);
9952
10579
  return;
9953
10580
  }
9954
10581
  if (isDebugEnabled()) {
9955
- getDebugLog().render(`writeChunked: sync chunked write of ${data.length} bytes`);
10582
+ getDebugLog().render(
10583
+ `writeChunked: sync chunked write of ${import_node_buffer3.Buffer.byteLength(data, "utf8")} bytes`
10584
+ );
9956
10585
  }
9957
10586
  try {
9958
- if (data.length <= chunkSize) {
9959
- out.write(data);
9960
- } else {
9961
- for (let i = 0; i < data.length; i += chunkSize) {
9962
- const chunk = data.slice(i, i + chunkSize);
9963
- out.write(chunk);
9964
- }
10587
+ for (let start = 0; start < data.length; ) {
10588
+ const end = safeChunkEnd(data, start, chunkSize);
10589
+ out.write(data.slice(start, end));
10590
+ start = end;
9965
10591
  }
9966
10592
  if (isDebugEnabled())
9967
10593
  getDebugLog().render(`writeChunked: chunked write completed`);
9968
10594
  } catch (e) {
9969
10595
  if (isDebugEnabled())
9970
10596
  getDebugLog().error(`writeChunked: write error`, e);
10597
+ throw e;
9971
10598
  }
9972
10599
  }
9973
- function doRender(dirtyRows, scrollOperations) {
10600
+ function doRender(dirtyRows, scrollOperations, forceDirtyRowsRepaint = false) {
9974
10601
  if (isDebugEnabled()) {
9975
10602
  getDebugLog().render(`doRender() START: dirtyRows=${(dirtyRows == null ? void 0 : dirtyRows.length) ?? "null"}`);
9976
10603
  }
9977
10604
  if (disposed)
9978
10605
  return;
10606
+ const currentFingerprintOwner = stdoutFingerprintOwners.get(terminal);
10607
+ if ((!ownsFingerprintFn || terminalFingerprintFnDirty) && (!currentFingerprintOwner || currentFingerprintOwner === fingerprintOwner) && canInstallTerminalFingerprintFn() && shouldAttemptFingerprintInstall()) {
10608
+ installFingerprintFn(terminalFingerprintFnDirty);
10609
+ }
9979
10610
  cliLatency == null ? void 0 : cliLatency.recordStdoutRenderStart();
9980
10611
  pendingRender = false;
9981
10612
  accumulatedAllRows = false;
@@ -9983,6 +10614,7 @@ function createStdoutRenderer(terminal, options) {
9983
10614
  accumulatedDirtyCount = 0;
9984
10615
  accumulatedDirtyMin = Number.POSITIVE_INFINITY;
9985
10616
  accumulatedDirtyMax = -1;
10617
+ accumulatedDirtyRowsRequireRepaint = false;
9986
10618
  accumulatedScrollOperations = null;
9987
10619
  lastFrameTime = Date.now();
9988
10620
  const renderStart = performance.now();
@@ -9992,11 +10624,15 @@ function createStdoutRenderer(terminal, options) {
9992
10624
  const bgSeq = openBg(defaultBg);
9993
10625
  const bgOnlyStyle = { bg: defaultBg };
9994
10626
  const bgKey = styleKeyFromParts({ bg: defaultBg });
9995
- const blankFP = bgKey << 10 | charHash10(" ");
10627
+ const blankFP = cellFingerprintFromStyleKey(" ", bgKey);
9996
10628
  let dirtySorted = true;
9997
- const forceFullRender = hrefIndexResetRequiresBaseline;
9998
- if (forceFullRender)
10629
+ const forceFullRender = hrefIndexResetRequiresBaseline || fingerprintInternResetRequiresBaseline;
10630
+ if (forceFullRender) {
9999
10631
  hrefIndexResetRequiresBaseline = false;
10632
+ fingerprintInternResetRequiresBaseline = false;
10633
+ fpPrevValid = false;
10634
+ }
10635
+ activeRenderBaselineGeneration = fingerprintBaselineGeneration;
10000
10636
  const normalizedScrollOperations = (() => {
10001
10637
  if (!(scrollOperations == null ? void 0 : scrollOperations.length))
10002
10638
  return null;
@@ -10014,10 +10650,12 @@ function createStdoutRenderer(terminal, options) {
10014
10650
  return outOps;
10015
10651
  })();
10016
10652
  let rowsToRender = (() => {
10017
- if (forceFullRender)
10653
+ if (forceFullRender || !fpPrevValid)
10018
10654
  return null;
10019
- if (!dirtyRows || dirtyRows.length === 0)
10655
+ if (!dirtyRows)
10020
10656
  return null;
10657
+ if (dirtyRows.length === 0)
10658
+ return [];
10021
10659
  const outRows = [];
10022
10660
  outRows.length = dirtyRows.length;
10023
10661
  let outLen = 0;
@@ -10025,7 +10663,7 @@ function createStdoutRenderer(terminal, options) {
10025
10663
  let prev = -1;
10026
10664
  for (let i = 0; i < dirtyRows.length; i++) {
10027
10665
  const y = Math.floor(dirtyRows[i] ?? -1);
10028
- if (y < 0 || y >= size.rows)
10666
+ if (!Number.isFinite(y) || y < 0 || y >= size.rows)
10029
10667
  continue;
10030
10668
  if (y <= prev)
10031
10669
  sorted = false;
@@ -10034,15 +10672,32 @@ function createStdoutRenderer(terminal, options) {
10034
10672
  }
10035
10673
  outRows.length = outLen;
10036
10674
  if (!outRows.length)
10037
- return null;
10675
+ return [];
10038
10676
  dirtySorted = sorted;
10039
10677
  if (!sorted)
10040
10678
  outRows.sort((a, b) => a - b);
10041
10679
  return outRows;
10042
10680
  })();
10681
+ if ((rowsToRender == null ? void 0 : rowsToRender.length) === 0 && !normalizedScrollOperations && !getImeAnchor) {
10682
+ if (isDebugEnabled())
10683
+ getDebugLog().render(" No valid dirty rows; frame skipped");
10684
+ cliLatency == null ? void 0 : cliLatency.recordStdoutNoOutput();
10685
+ return;
10686
+ }
10687
+ const dirtyRowSnapshots = fpPrevValid && rowsToRender ? /* @__PURE__ */ new Map() : null;
10688
+ const snapshotDirtyRowFingerprints = () => {
10689
+ if (!dirtyRowSnapshots || !rowsToRender)
10690
+ return;
10691
+ for (const y of rowsToRender) {
10692
+ const row = terminal.getRow(y);
10693
+ dirtyRowSnapshots.set(y, snapshotRowFingerprints(row, y, size.cols));
10694
+ }
10695
+ };
10696
+ snapshotDirtyRowFingerprints();
10043
10697
  if (fpPrevValid && rowsToRender) {
10044
10698
  currentFP.set(prevFP);
10045
10699
  currentHrefIds.set(prevHrefIds);
10700
+ currentTextIds.set(prevTextIds);
10046
10701
  }
10047
10702
  const frameParts = [];
10048
10703
  frameParts.push(!isGhostty && useSyncOutput ? SYNC_START : "");
@@ -10062,6 +10717,15 @@ function createStdoutRenderer(terminal, options) {
10062
10717
  inverse: false,
10063
10718
  href: null
10064
10719
  };
10720
+ const closeActiveHrefBeforeCursorMove = () => {
10721
+ if (!enableOsc8Links || !activeStyle.href)
10722
+ return;
10723
+ frameParts.push(OSC8_CLOSE);
10724
+ activeStyle = {
10725
+ ...activeStyle,
10726
+ href: null
10727
+ };
10728
+ };
10065
10729
  const normalizeStyle2 = (style) => {
10066
10730
  return {
10067
10731
  fg: style.fg ?? null,
@@ -10071,7 +10735,7 @@ function createStdoutRenderer(terminal, options) {
10071
10735
  italic: Boolean(style.italic),
10072
10736
  underline: Boolean(style.underline),
10073
10737
  inverse: Boolean(style.inverse),
10074
- href: normalizeHref(style.href)
10738
+ href: renderedHref(style.href)
10075
10739
  };
10076
10740
  };
10077
10741
  const emitStyle = (nextStyle, nextKey) => {
@@ -10108,15 +10772,23 @@ function createStdoutRenderer(terminal, options) {
10108
10772
  activeStyleKey = nextKey;
10109
10773
  activeStyle = next;
10110
10774
  };
10111
- const shouldEmitStyle = (style, key) => activeStyleKey !== key || activeStyle.href !== normalizeHref(style.href);
10112
- const renderRow = (y, row, startX = 0, endXExclusive = row.length, clearToEol = true) => {
10113
- const spanStart = Math.max(0, Math.min(row.length, Math.floor(startX)));
10114
- const spanEnd = Math.max(spanStart, Math.min(row.length, Math.floor(endXExclusive)));
10115
- if (spanStart >= spanEnd && !clearToEol) {
10775
+ const shouldEmitStyle = (style, key) => activeStyleKey !== key || activeStyle.href !== renderedHref(style.href);
10776
+ const renderRow = (y, row, startX = 0, endXExclusive = size.cols, clearToEol = true) => {
10777
+ const spanStart = Math.max(0, Math.min(size.cols, Math.floor(startX)));
10778
+ const spanEnd = Math.max(spanStart, Math.min(size.cols, Math.floor(endXExclusive)));
10779
+ const shouldClearToEol = clearToEol && spanEnd < size.cols;
10780
+ if (spanStart >= spanEnd && !shouldClearToEol) {
10116
10781
  return;
10117
10782
  }
10118
10783
  hasFrameOutput = true;
10784
+ const pushCursorFixAfterCell = (parts, cellX, cell) => {
10785
+ const afterX = cellX + (cell.width ?? 1);
10786
+ if (afterX >= size.cols)
10787
+ return;
10788
+ parts.push(`\x1B[${y + 1};${afterX + 1}H`);
10789
+ };
10119
10790
  if (!disableCursorPos) {
10791
+ closeActiveHrefBeforeCursorMove();
10120
10792
  if (spanStart === 0 && lastRenderWasFullRow && y === lastRenderedY + 1) {
10121
10793
  frameParts.push("\r\n");
10122
10794
  } else {
@@ -10130,7 +10802,7 @@ function createStdoutRenderer(terminal, options) {
10130
10802
  const currentTextParts = rowTextPartsScratch;
10131
10803
  currentTextParts.length = 0;
10132
10804
  for (let x = spanStart; x < spanEnd; x++) {
10133
- const cell = row[x];
10805
+ const cell = cellAt(row, x);
10134
10806
  if (cell.continuation)
10135
10807
  continue;
10136
10808
  const ch = cell.ch || " ";
@@ -10141,14 +10813,14 @@ function createStdoutRenderer(terminal, options) {
10141
10813
  currentStyle = nextStyle;
10142
10814
  currentTextParts.push(ch);
10143
10815
  if (needsCursorFix(cell, ch)) {
10144
- currentTextParts.push(`\x1B[${y + 1};${x + 1 + (cell.width ?? 1)}H`);
10816
+ pushCursorFixAfterCell(currentTextParts, x, cell);
10145
10817
  }
10146
10818
  continue;
10147
10819
  }
10148
- if (key === currentKey && normalizeHref(nextStyle.href) === normalizeHref(currentStyle == null ? void 0 : currentStyle.href)) {
10820
+ if (key === currentKey && renderedHref(nextStyle.href) === renderedHref(currentStyle == null ? void 0 : currentStyle.href)) {
10149
10821
  currentTextParts.push(ch);
10150
10822
  if (needsCursorFix(cell, ch)) {
10151
- currentTextParts.push(`\x1B[${y + 1};${x + 1 + (cell.width ?? 1)}H`);
10823
+ pushCursorFixAfterCell(currentTextParts, x, cell);
10152
10824
  }
10153
10825
  continue;
10154
10826
  }
@@ -10161,7 +10833,7 @@ function createStdoutRenderer(terminal, options) {
10161
10833
  currentTextParts.length = 0;
10162
10834
  currentTextParts.push(ch);
10163
10835
  if (needsCursorFix(cell, ch))
10164
- currentTextParts.push(`\x1B[${y + 1};${x + 1 + (cell.width ?? 1)}H`);
10836
+ pushCursorFixAfterCell(currentTextParts, x, cell);
10165
10837
  }
10166
10838
  if (currentKey != null) {
10167
10839
  if (shouldEmitStyle(currentStyle, currentKey)) {
@@ -10169,14 +10841,103 @@ function createStdoutRenderer(terminal, options) {
10169
10841
  }
10170
10842
  frameParts.push(currentTextParts.join(""));
10171
10843
  }
10172
- if (clearToEol) {
10844
+ if (shouldClearToEol) {
10173
10845
  if (activeStyleKey !== bgKey) {
10174
10846
  emitStyle(bgOnlyStyle, bgKey);
10175
10847
  }
10176
10848
  frameParts.push("\x1B[K");
10177
10849
  }
10178
10850
  lastRenderedY = y;
10179
- lastRenderWasFullRow = spanStart === 0 && clearToEol;
10851
+ lastRenderWasFullRow = spanStart === 0 && (spanEnd >= size.cols || shouldClearToEol);
10852
+ };
10853
+ const lastRenderableColumnInCurrentRow = (rowY, cols, blankFingerprint) => {
10854
+ if (!fpCols || rowY < 0 || rowY >= fpRows)
10855
+ return -1;
10856
+ const base = rowY * fpCols;
10857
+ const limit = Math.min(cols, fpCols);
10858
+ for (let x = limit - 1; x >= 0; x--) {
10859
+ if (currentFP[base + x] !== blankFingerprint || currentTextIds[base + x] !== blankTextId || currentHrefIds[base + x] !== 0) {
10860
+ return x;
10861
+ }
10862
+ }
10863
+ return -1;
10864
+ };
10865
+ const resolveTailClearStartX = (rowY, cols, shouldRewriteTail, blankFingerprint) => {
10866
+ if (!shouldRewriteTail)
10867
+ return null;
10868
+ const lastRenderableX = lastRenderableColumnInCurrentRow(rowY, cols, blankFingerprint);
10869
+ const clearStartX = Math.max(0, Math.min(cols, lastRenderableX + 1));
10870
+ return clearStartX < cols ? clearStartX : null;
10871
+ };
10872
+ const renderDirtySpans = (rowY, row, rawSpans, rewriteTail) => {
10873
+ if (!rawSpans.length && !rewriteTail)
10874
+ return;
10875
+ const rowCols = size.cols;
10876
+ const clearStartX = resolveTailClearStartX(rowY, rowCols, rewriteTail, blankFP);
10877
+ const paintTailInsteadOfEscK = clearStartX != null && dirtyRowPatchMode === "auto" && useConservativeDirtyRows;
10878
+ const spansToPaint = [];
10879
+ for (const span of rawSpans) {
10880
+ const startX = Math.max(0, Math.min(rowCols, span.startX));
10881
+ const endXExclusive = Math.max(startX, Math.min(rowCols, span.endXExclusive));
10882
+ if (endXExclusive <= startX)
10883
+ continue;
10884
+ if (clearStartX != null && !paintTailInsteadOfEscK) {
10885
+ if (startX >= clearStartX)
10886
+ continue;
10887
+ const clippedEnd = Math.min(endXExclusive, clearStartX);
10888
+ if (clippedEnd > startX) {
10889
+ spansToPaint.push({
10890
+ startX,
10891
+ endXExclusive: clippedEnd
10892
+ });
10893
+ }
10894
+ continue;
10895
+ }
10896
+ spansToPaint.push({
10897
+ startX,
10898
+ endXExclusive
10899
+ });
10900
+ }
10901
+ if (paintTailInsteadOfEscK && clearStartX != null && clearStartX < rowCols) {
10902
+ const prev = spansToPaint[spansToPaint.length - 1];
10903
+ if (prev && clearStartX <= prev.endXExclusive + DIRTY_SPAN_MERGE_GAP_CELLS) {
10904
+ spansToPaint[spansToPaint.length - 1] = {
10905
+ startX: prev.startX,
10906
+ endXExclusive: rowCols
10907
+ };
10908
+ } else {
10909
+ spansToPaint.push({
10910
+ startX: clearStartX,
10911
+ endXExclusive: rowCols
10912
+ });
10913
+ }
10914
+ }
10915
+ const clearToEolForCost = clearStartX != null && clearStartX < rowCols && !paintTailInsteadOfEscK;
10916
+ const renderMode = !shouldUseMultiDirtySpans() && spansToPaint.length > 0 ? "contiguous" : resolveDirtySpanRenderMode(row, spansToPaint, rowY, rowCols, clearToEolForCost);
10917
+ if (renderMode === "row") {
10918
+ if (clearStartX != null && !paintTailInsteadOfEscK) {
10919
+ renderRow(rowY, row, 0, clearStartX, true);
10920
+ } else {
10921
+ renderRow(rowY, row, 0, rowCols, false);
10922
+ }
10923
+ return;
10924
+ } else if (renderMode === "contiguous") {
10925
+ const first = spansToPaint[0];
10926
+ const last = spansToPaint[spansToPaint.length - 1];
10927
+ if (first && last) {
10928
+ const rewriteEnd = clearStartX == null || paintTailInsteadOfEscK ? last.endXExclusive : Math.min(last.endXExclusive, clearStartX);
10929
+ if (rewriteEnd > first.startX) {
10930
+ renderRow(rowY, row, first.startX, rewriteEnd, false);
10931
+ }
10932
+ }
10933
+ } else {
10934
+ for (const span of spansToPaint) {
10935
+ renderRow(rowY, row, span.startX, span.endXExclusive, false);
10936
+ }
10937
+ }
10938
+ if (clearStartX != null && !paintTailInsteadOfEscK) {
10939
+ renderRow(rowY, row, clearStartX, clearStartX, true);
10940
+ }
10180
10941
  };
10181
10942
  const overlayCoverage = overlayPlaneCoverageRows(size.rows);
10182
10943
  const overlayRows = Array.from(
@@ -10213,12 +10974,12 @@ function createStdoutRenderer(terminal, options) {
10213
10974
  }
10214
10975
  } else {
10215
10976
  allowInferredScrollRegions = false;
10216
- const expandedRows = new Set(rowsToRender ?? []);
10977
+ const expandedRows2 = new Set(rowsToRender ?? []);
10217
10978
  for (const op of explicitScrollOperations) {
10218
10979
  for (let y = op.startY; y < op.endY; y++)
10219
- expandedRows.add(y);
10980
+ expandedRows2.add(y);
10220
10981
  }
10221
- rowsToRender = Array.from(expandedRows).sort((a, b) => a - b);
10982
+ rowsToRender = Array.from(expandedRows2).sort((a, b) => a - b);
10222
10983
  dirtySorted = true;
10223
10984
  explicitScrollOperations = null;
10224
10985
  hiddenExplicitDirtyRows = null;
@@ -10230,15 +10991,20 @@ function createStdoutRenderer(terminal, options) {
10230
10991
  }
10231
10992
  }
10232
10993
  const scrollRowsCandidate = rowsToRender;
10233
- if (explicitScrollOperations && rowsToRender && (!enableScrollRegions || !fpPrevValid)) {
10234
- const expandedRows = new Set(rowsToRender);
10235
- for (const op of explicitScrollOperations) {
10994
+ const expandedRows = new Set(rowsToRender ?? []);
10995
+ const includeRowsForPendingScrollOperations = (operations) => {
10996
+ if (!(operations == null ? void 0 : operations.length))
10997
+ return;
10998
+ for (const op of operations) {
10236
10999
  for (let y = op.startY; y < op.endY; y++) {
10237
11000
  if (hiddenExplicitDirtyRows == null ? void 0 : hiddenExplicitDirtyRows.has(y))
10238
11001
  continue;
10239
11002
  expandedRows.add(y);
10240
11003
  }
10241
11004
  }
11005
+ };
11006
+ if (explicitScrollOperations && rowsToRender && (!enableScrollRegions || !fpPrevValid)) {
11007
+ includeRowsForPendingScrollOperations(explicitScrollOperations);
10242
11008
  rowsToRender = Array.from(expandedRows).sort((a, b) => a - b);
10243
11009
  dirtySorted = true;
10244
11010
  explicitScrollOperations = null;
@@ -10250,18 +11016,19 @@ function createStdoutRenderer(terminal, options) {
10250
11016
  }
10251
11017
  }
10252
11018
  const denseDirtyRows = Boolean(
10253
- rowsToRender && rowsToRender.length >= size.rows * dirtyFullThreshold
11019
+ rowsToRender && size.rows > 1 && rowsToRender.length >= size.rows * dirtyFullThreshold
10254
11020
  );
10255
11021
  if (denseDirtyRows) {
10256
11022
  rowsToRender = null;
10257
11023
  }
10258
11024
  let profiledRowCount = rowsToRender ? rowsToRender.length : size.rows;
10259
- const useScrollRegions = enableScrollRegions && fpPrevValid && scrollRowsCandidate && scrollRowsCandidate.length >= 3 && allowInferredScrollRegions;
11025
+ const useScrollRegions = enableScrollRegions && fpPrevValid && scrollRowsCandidate && scrollRowsCandidate.length >= 3 && !forceDirtyRowsRepaint && allowInferredScrollRegions;
10260
11026
  let scrollHandled = false;
10261
11027
  if (enableScrollRegions && explicitScrollOperations && scrollRowsCandidate && fpPrevValid) {
10262
11028
  if (currentFP.length === prevFP.length) {
10263
11029
  currentFP.set(prevFP);
10264
11030
  currentHrefIds.set(prevHrefIds);
11031
+ currentTextIds.set(prevTextIds);
10265
11032
  }
10266
11033
  const explicitRowsToRender = /* @__PURE__ */ new Set();
10267
11034
  for (const y of scrollRowsCandidate) {
@@ -10286,6 +11053,7 @@ function createStdoutRenderer(terminal, options) {
10286
11053
  for (let x = 0; x < size.cols; x++) {
10287
11054
  currentFP[dstBase + x] = prevFP[srcBase + x];
10288
11055
  currentHrefIds[dstBase + x] = prevHrefIds[srcBase + x];
11056
+ currentTextIds[dstBase + x] = prevTextIds[srcBase + x];
10289
11057
  }
10290
11058
  }
10291
11059
  for (let y = op.endY - op.delta; y < op.endY; y++) {
@@ -10294,6 +11062,7 @@ function createStdoutRenderer(terminal, options) {
10294
11062
  for (let x = 0; x < size.cols; x++) {
10295
11063
  currentFP[base + x] = blankFP;
10296
11064
  currentHrefIds[base + x] = 0;
11065
+ currentTextIds[base + x] = blankTextId;
10297
11066
  }
10298
11067
  }
10299
11068
  } else {
@@ -10306,6 +11075,7 @@ function createStdoutRenderer(terminal, options) {
10306
11075
  for (let x = 0; x < size.cols; x++) {
10307
11076
  currentFP[dstBase + x] = prevFP[srcBase + x];
10308
11077
  currentHrefIds[dstBase + x] = prevHrefIds[srcBase + x];
11078
+ currentTextIds[dstBase + x] = prevTextIds[srcBase + x];
10309
11079
  }
10310
11080
  }
10311
11081
  for (let y = op.startY; y < op.startY + absDelta; y++) {
@@ -10314,6 +11084,7 @@ function createStdoutRenderer(terminal, options) {
10314
11084
  for (let x = 0; x < size.cols; x++) {
10315
11085
  currentFP[base + x] = blankFP;
10316
11086
  currentHrefIds[base + x] = 0;
11087
+ currentTextIds[base + x] = blankTextId;
10317
11088
  }
10318
11089
  }
10319
11090
  }
@@ -10322,10 +11093,44 @@ function createStdoutRenderer(terminal, options) {
10322
11093
  const explicitDirtyRows = Array.from(explicitRowsToRender).sort((a, b) => a - b);
10323
11094
  const overlayDirtyRows = overlayTouchedRowSet ? explicitDirtyRows.filter((y) => overlayTouchedRowSet.has(y)) : [];
10324
11095
  const paintedExplicitRows = [];
11096
+ const insertedRows = /* @__PURE__ */ new Set();
11097
+ for (const op of explicitScrollOperations) {
11098
+ const absDelta = Math.abs(op.delta);
11099
+ if (op.delta > 0) {
11100
+ for (let y = op.endY - absDelta; y < op.endY; y++)
11101
+ insertedRows.add(y);
11102
+ } else {
11103
+ for (let y = op.startY; y < op.startY + absDelta; y++)
11104
+ insertedRows.add(y);
11105
+ }
11106
+ }
10325
11107
  for (const y of explicitDirtyRows) {
10326
11108
  const row = terminal.getRow(y);
10327
- fingerprintRow(row, y, size.cols);
10328
- renderRow(y, row);
11109
+ const overlayRow = overlayTouchedRowSet == null ? void 0 : overlayTouchedRowSet.has(y);
11110
+ if (forceDirtyRowsRepaint || !shouldUseDirtySpans() || overlayRow) {
11111
+ fingerprintRow(row, y, size.cols);
11112
+ renderRow(y, row);
11113
+ paintedExplicitRows.push(y);
11114
+ continue;
11115
+ }
11116
+ const insertedRow = insertedRows.has(y);
11117
+ if (insertedRow)
11118
+ fingerprintRow(row, y, size.cols);
11119
+ const spans = insertedRow ? resolveChangedSpansAgainstFill(row, y, size.cols, blankFP, blankTextId, 0) : resolveChangedSpansAgainstReference(
11120
+ row,
11121
+ y,
11122
+ size.cols,
11123
+ currentFP,
11124
+ currentHrefIds,
11125
+ currentTextIds
11126
+ );
11127
+ if (!spans.length) {
11128
+ continue;
11129
+ }
11130
+ const rewriteTail = !insertedRow && shouldRewriteRowTail(row, y, size.cols, currentFP, currentHrefIds, currentTextIds, bgKey);
11131
+ if (!insertedRow)
11132
+ fingerprintRow(row, y, size.cols);
11133
+ renderDirtySpans(y, row, spans, rewriteTail);
10329
11134
  paintedExplicitRows.push(y);
10330
11135
  }
10331
11136
  if (isDebugEnabled() && overlayDirtyRows.length) {
@@ -10342,6 +11147,7 @@ function createStdoutRenderer(terminal, options) {
10342
11147
  if (currentFP.length === prevFP.length) {
10343
11148
  currentFP.set(prevFP);
10344
11149
  currentHrefIds.set(prevHrefIds);
11150
+ currentTextIds.set(prevTextIds);
10345
11151
  }
10346
11152
  hasFrameOutput = true;
10347
11153
  if (isDebugEnabled()) {
@@ -10365,6 +11171,7 @@ function createStdoutRenderer(terminal, options) {
10365
11171
  for (let x = 0; x < size.cols; x++) {
10366
11172
  currentFP[dstBase + x] = prevFP[srcBase + x];
10367
11173
  currentHrefIds[dstBase + x] = prevHrefIds[srcBase + x];
11174
+ currentTextIds[dstBase + x] = prevTextIds[srcBase + x];
10368
11175
  }
10369
11176
  }
10370
11177
  for (let y = shift.regionEnd - shift.delta; y < shift.regionEnd; y++) {
@@ -10372,6 +11179,7 @@ function createStdoutRenderer(terminal, options) {
10372
11179
  for (let x = 0; x < size.cols; x++) {
10373
11180
  currentFP[base + x] = blankFP;
10374
11181
  currentHrefIds[base + x] = 0;
11182
+ currentTextIds[base + x] = blankTextId;
10375
11183
  }
10376
11184
  }
10377
11185
  } else {
@@ -10382,6 +11190,7 @@ function createStdoutRenderer(terminal, options) {
10382
11190
  for (let x = 0; x < size.cols; x++) {
10383
11191
  currentFP[dstBase + x] = prevFP[srcBase + x];
10384
11192
  currentHrefIds[dstBase + x] = prevHrefIds[srcBase + x];
11193
+ currentTextIds[dstBase + x] = prevTextIds[srcBase + x];
10385
11194
  }
10386
11195
  }
10387
11196
  for (let y = shift.regionStart; y < shift.regionStart + absDelta; y++) {
@@ -10389,6 +11198,7 @@ function createStdoutRenderer(terminal, options) {
10389
11198
  for (let x = 0; x < size.cols; x++) {
10390
11199
  currentFP[base + x] = blankFP;
10391
11200
  currentHrefIds[base + x] = 0;
11201
+ currentTextIds[base + x] = blankTextId;
10392
11202
  }
10393
11203
  }
10394
11204
  }
@@ -10402,6 +11212,32 @@ function createStdoutRenderer(terminal, options) {
10402
11212
  ) : shift.extraDirtyRows;
10403
11213
  for (const y of extraDirtyRows) {
10404
11214
  const row = terminal.getRow(y);
11215
+ if (shouldUseDirtySpans()) {
11216
+ const spans = resolveChangedSpansAgainstReference(
11217
+ row,
11218
+ y,
11219
+ size.cols,
11220
+ currentFP,
11221
+ currentHrefIds,
11222
+ currentTextIds
11223
+ );
11224
+ if (!spans.length) {
11225
+ fingerprintRow(row, y, size.cols);
11226
+ continue;
11227
+ }
11228
+ const rewriteTail = shouldRewriteRowTail(
11229
+ row,
11230
+ y,
11231
+ size.cols,
11232
+ currentFP,
11233
+ currentHrefIds,
11234
+ currentTextIds,
11235
+ bgKey
11236
+ );
11237
+ fingerprintRow(row, y, size.cols);
11238
+ renderDirtySpans(y, row, spans, rewriteTail);
11239
+ continue;
11240
+ }
10405
11241
  fingerprintRow(row, y, size.cols);
10406
11242
  renderRow(y, row);
10407
11243
  }
@@ -10439,13 +11275,30 @@ function createStdoutRenderer(terminal, options) {
10439
11275
  const overlayDirtyRows = overlayTouchedRowSet ? rowsToRender.filter((y) => overlayTouchedRowSet.has(y)) : [];
10440
11276
  if (isDebugEnabled()) {
10441
11277
  getDebugLog().render(
10442
- ` Partial render: ${rowsToRender.length} dirty rows: [${rowsToRender.join(", ")}]${overlayDirtyRows.length ? ` (overlay-full-row: [${overlayDirtyRows.join(", ")}])` : ""}`
11278
+ ` Partial render: ${rowsToRender.length} dirty rows: [${rowsToRender.join(",")}]${overlayDirtyRows.length ? ` (overlay-full-row: [${overlayDirtyRows.join(", ")}])` : ""}`
10443
11279
  );
10444
11280
  }
10445
11281
  for (const y of rowsToRender) {
10446
11282
  const row = terminal.getRow(y);
10447
11283
  fingerprintRow(row, y, size.cols);
10448
- renderRow(y, row);
11284
+ if (forceDirtyRowsRepaint || !shouldUseDirtySpans()) {
11285
+ renderRow(y, row);
11286
+ continue;
11287
+ }
11288
+ const spans = resolveChangedSpans(row, y, size.cols, (dirtyRowSnapshots == null ? void 0 : dirtyRowSnapshots.get(y)) ?? null);
11289
+ if (!spans.length) {
11290
+ continue;
11291
+ }
11292
+ const rewriteTail = shouldRewriteRowTail(
11293
+ row,
11294
+ y,
11295
+ size.cols,
11296
+ prevFP,
11297
+ prevHrefIds,
11298
+ prevTextIds,
11299
+ bgKey
11300
+ );
11301
+ renderDirtySpans(y, row, spans, rewriteTail);
10449
11302
  }
10450
11303
  }
10451
11304
  if (!rowsToRender && lastRenderedRows > size.rows) {
@@ -10459,17 +11312,22 @@ function createStdoutRenderer(terminal, options) {
10459
11312
  frameParts.push(rowClearToEol[size.rows + i]);
10460
11313
  }
10461
11314
  }
10462
- if (!rowsToRender)
10463
- lastRenderedRows = size.rows;
10464
- const tmpFP = prevFP;
10465
- prevFP = currentFP;
10466
- currentFP = tmpFP;
10467
- const tmpHrefIds = prevHrefIds;
10468
- prevHrefIds = currentHrefIds;
10469
- currentHrefIds = tmpHrefIds;
10470
- fpPrevValid = !hrefIndexResetRequiresBaseline;
10471
- prevOverlayBlockedRows = overlayCoverage.blockedRows;
10472
- prevOverlayPartialRows = overlayCoverage.partialRows;
11315
+ const nextLastRenderedRows = !rowsToRender ? size.rows : lastRenderedRows;
11316
+ const commitRenderBaseline = () => {
11317
+ lastRenderedRows = nextLastRenderedRows;
11318
+ const tmpFP = prevFP;
11319
+ prevFP = currentFP;
11320
+ currentFP = tmpFP;
11321
+ const tmpHrefIds = prevHrefIds;
11322
+ prevHrefIds = currentHrefIds;
11323
+ currentHrefIds = tmpHrefIds;
11324
+ const tmpTextIds = prevTextIds;
11325
+ prevTextIds = currentTextIds;
11326
+ currentTextIds = tmpTextIds;
11327
+ fpPrevValid = !hrefIndexResetRequiresBaseline && !fingerprintInternResetRequiresBaseline;
11328
+ prevOverlayBlockedRows = overlayCoverage.blockedRows;
11329
+ prevOverlayPartialRows = overlayCoverage.partialRows;
11330
+ };
10473
11331
  let emittedCursorPos = null;
10474
11332
  if (getImeAnchor) {
10475
11333
  const anchor = getImeAnchor();
@@ -10478,11 +11336,13 @@ function createStdoutRenderer(terminal, options) {
10478
11336
  if (hasFrameOutput || x !== lastCursorX || y !== lastCursorY) {
10479
11337
  hasFrameOutput = true;
10480
11338
  emittedCursorPos = { x, y };
11339
+ closeActiveHrefBeforeCursorMove();
10481
11340
  frameParts.push(`\x1B[${y + 1};${x + 1}H`);
10482
11341
  }
10483
11342
  }
10484
11343
  }
10485
11344
  if (!hasFrameOutput) {
11345
+ commitRenderBaseline();
10486
11346
  if (isDebugEnabled())
10487
11347
  getDebugLog().render(" No-op frame skipped");
10488
11348
  cliLatency == null ? void 0 : cliLatency.recordStdoutNoOutput();
@@ -10494,6 +11354,7 @@ function createStdoutRenderer(terminal, options) {
10494
11354
  frameParts.push("\x1B[?7h");
10495
11355
  frameParts.push(!isGhostty && useSyncOutput ? SYNC_END : "");
10496
11356
  const frame = frameParts.join("");
11357
+ const frameBytes = import_node_buffer3.Buffer.byteLength(frame, "utf8");
10497
11358
  if (profiler) {
10498
11359
  profiler.recordRender({
10499
11360
  durationMs: profiler.now() - renderStart,
@@ -10553,7 +11414,7 @@ function createStdoutRenderer(terminal, options) {
10553
11414
  }
10554
11415
  return count;
10555
11416
  };
10556
- const frameSize = frame.length;
11417
+ const frameSize = frameBytes;
10557
11418
  const cursorSeqCount = countCursorMoves(frame);
10558
11419
  const resetCount = countResets(frame);
10559
11420
  const buildTime = (performance.now() - renderStart).toFixed(2);
@@ -10568,15 +11429,14 @@ function createStdoutRenderer(terminal, options) {
10568
11429
  const resolveWriteMode = (frameSizeBytes) => {
10569
11430
  if (isGhostty)
10570
11431
  return "chunked";
10571
- const canWriteSync = out.fd === 1;
10572
11432
  const preferChunked = frameSizeBytes >= chunkThresholdBytes || writeEmaMs >= 24;
10573
11433
  if (preferChunked)
10574
11434
  return "chunked";
10575
- if (canWriteSync && frameSizeBytes <= syncMaxBytes)
11435
+ if (canUseSyncStdout && frameSizeBytes <= syncMaxBytes)
10576
11436
  return "sync";
10577
11437
  return "stream";
10578
11438
  };
10579
- const writeMode = resolveWriteMode(frame.length);
11439
+ const writeMode = resolveWriteMode(frameBytes);
10580
11440
  if (isDebugEnabled())
10581
11441
  getDebugLog().render(`Before write()`);
10582
11442
  try {
@@ -10587,11 +11447,11 @@ function createStdoutRenderer(terminal, options) {
10587
11447
  );
10588
11448
  }
10589
11449
  writeChunked(frame);
10590
- } else if (writeMode === "sync" && out.fd === 1) {
11450
+ } else if (writeMode === "sync" && canUseSyncStdout) {
10591
11451
  if (isDebugEnabled())
10592
11452
  getDebugLog().render(` Using writeSync`);
10593
11453
  try {
10594
- writeFdSync(1, frame);
11454
+ writeFdSync(outputFd, frame);
10595
11455
  } catch {
10596
11456
  if (isDebugEnabled()) {
10597
11457
  getDebugLog().render(` writeSync failed, falling back to stream write`);
@@ -10610,14 +11470,13 @@ function createStdoutRenderer(terminal, options) {
10610
11470
  } catch (writeError) {
10611
11471
  if (isDebugEnabled())
10612
11472
  getDebugLog().error(`Write ERROR:`, writeError);
10613
- if (!isGhostty && useSyncOutput) {
10614
- try {
10615
- out.write(`\x1B[?7h${SYNC_END}`);
10616
- } catch {
10617
- }
11473
+ try {
11474
+ out.write(`\x1B[?7h${!isGhostty && useSyncOutput ? SYNC_END : ""}`);
11475
+ } catch {
10618
11476
  }
10619
11477
  throw writeError;
10620
11478
  }
11479
+ commitRenderBaseline();
10621
11480
  {
10622
11481
  const writeMs = performance.now() - writeStart;
10623
11482
  writeEmaMs = writeEmaMs === 0 ? writeMs : writeEmaMs * 0.85 + writeMs * 0.15;
@@ -10630,7 +11489,7 @@ function createStdoutRenderer(terminal, options) {
10630
11489
  }
10631
11490
  recordStdoutFrame({
10632
11491
  at: Date.now(),
10633
- bytes: frame.length,
11492
+ bytes: frameBytes,
10634
11493
  writeMs,
10635
11494
  writeEmaMs,
10636
11495
  writeMode: writeMode === "sync" ? "sync" : writeMode === "chunked" ? "chunked" : "stream"
@@ -10638,13 +11497,13 @@ function createStdoutRenderer(terminal, options) {
10638
11497
  if (profiler) {
10639
11498
  profiler.recordWrite({
10640
11499
  durationMs: writeMs,
10641
- bytes: frame.length,
11500
+ bytes: frameBytes,
10642
11501
  mode: writeMode === "sync" ? "sync" : writeMode === "chunked" ? "chunked" : "stream"
10643
11502
  });
10644
11503
  }
10645
11504
  cliLatency == null ? void 0 : cliLatency.recordStdoutWrite({
10646
11505
  durationMs: writeMs,
10647
- bytes: frame.length,
11506
+ bytes: frameBytes,
10648
11507
  mode: writeMode === "sync" ? "sync" : writeMode === "chunked" ? "chunked" : "stream"
10649
11508
  });
10650
11509
  }
@@ -10769,6 +11628,7 @@ function createStdoutRenderer(terminal, options) {
10769
11628
  }
10770
11629
  function markScrollOperationDirty(op, rowCount) {
10771
11630
  markAccumulatedDirtyRange(op.startY, op.endY, rowCount);
11631
+ accumulatedDirtyRowsRequireRepaint = true;
10772
11632
  }
10773
11633
  function mergeScrollOperations(next) {
10774
11634
  if (!(next == null ? void 0 : next.length) || accumulatedAllRows)
@@ -10819,20 +11679,28 @@ function createStdoutRenderer(terminal, options) {
10819
11679
  `render() called: dirtyRows=${(dirtyRows == null ? void 0 : dirtyRows.length) ?? "null"}, pending=${pendingRender}, elapsed=${Date.now() - lastFrameTime}ms`
10820
11680
  );
10821
11681
  }
10822
- if (!dirtyRows || dirtyRows.length === 0) {
11682
+ if (dirtyRows == null) {
10823
11683
  accumulatedAllRows = true;
10824
11684
  accumulatedDirtyBits = null;
10825
11685
  accumulatedDirtyCount = 0;
10826
11686
  accumulatedDirtyMin = Number.POSITIVE_INFINITY;
10827
11687
  accumulatedDirtyMax = -1;
11688
+ accumulatedDirtyRowsRequireRepaint = false;
10828
11689
  accumulatedScrollOperations = null;
11690
+ } else if (dirtyRows.length === 0) {
11691
+ if (scrollOperations == null ? void 0 : scrollOperations.length) {
11692
+ mergeScrollOperations(scrollOperations);
11693
+ } else if (!accumulatedAllRows && accumulatedDirtyCount === 0 && !getImeAnchor) {
11694
+ cliLatency == null ? void 0 : cliLatency.recordStdoutQueued(0);
11695
+ return;
11696
+ }
10829
11697
  } else if (!accumulatedAllRows) {
10830
11698
  const rowCount = terminal.size().rows;
10831
11699
  mergeScrollOperations(scrollOperations);
10832
11700
  const bits = ensureAccumulatedDirtyBits(rowCount);
10833
11701
  for (let i = 0; i < dirtyRows.length; i++) {
10834
11702
  const y = Math.floor(dirtyRows[i] ?? -1);
10835
- if (y < 0 || y >= rowCount)
11703
+ if (!Number.isFinite(y) || y < 0 || y >= rowCount)
10836
11704
  continue;
10837
11705
  if (!bits[y]) {
10838
11706
  bits[y] = 1;
@@ -10861,15 +11729,17 @@ function createStdoutRenderer(terminal, options) {
10861
11729
  }
10862
11730
  const rows = buildAccumulatedRows(dirtyRows);
10863
11731
  const pendingScrolls = buildAccumulatedScrollOperations();
11732
+ const forceDirtyRowsRepaint = accumulatedDirtyRowsRequireRepaint;
10864
11733
  cliLatency == null ? void 0 : cliLatency.recordStdoutQueued(0);
10865
- doRender(rows, pendingScrolls);
11734
+ doRender(rows, pendingScrolls, forceDirtyRowsRepaint);
10866
11735
  return;
10867
11736
  }
10868
11737
  if (elapsed >= MIN_FRAME_MS) {
10869
11738
  const rows = buildAccumulatedRows(dirtyRows);
10870
11739
  const pendingScrolls = buildAccumulatedScrollOperations();
11740
+ const forceDirtyRowsRepaint = accumulatedDirtyRowsRequireRepaint;
10871
11741
  cliLatency == null ? void 0 : cliLatency.recordStdoutQueued(0);
10872
- doRender(rows, pendingScrolls);
11742
+ doRender(rows, pendingScrolls, forceDirtyRowsRepaint);
10873
11743
  } else {
10874
11744
  pendingRender = true;
10875
11745
  if (renderTimer)
@@ -10879,19 +11749,67 @@ function createStdoutRenderer(terminal, options) {
10879
11749
  if (!disposed) {
10880
11750
  const rows = buildAccumulatedRows(dirtyRows);
10881
11751
  const pendingScrolls = buildAccumulatedScrollOperations();
10882
- doRender(rows, pendingScrolls);
11752
+ const forceDirtyRowsRepaint = accumulatedDirtyRowsRequireRepaint;
11753
+ doRender(rows, pendingScrolls, forceDirtyRowsRepaint);
10883
11754
  }
10884
11755
  }, queuedDelayMs);
10885
11756
  cliLatency == null ? void 0 : cliLatency.recordStdoutQueued(queuedDelayMs);
10886
11757
  }
10887
11758
  }
10888
- function installFingerprintFn() {
10889
- terminal.setFingerprintFn((ch, style) => {
11759
+ function installFingerprintFn(force = false) {
11760
+ if (!canInstallTerminalFingerprintFn()) {
11761
+ if (ownsFingerprintFn && stdoutFingerprintOwners.get(terminal) === fingerprintOwner) {
11762
+ stdoutFingerprintOwners.delete(terminal);
11763
+ invalidateFingerprintBaseline();
11764
+ }
11765
+ ownsFingerprintFn = false;
11766
+ terminalFingerprintFnDirty = true;
11767
+ fingerprintInstallBlockedByExternalProvider = false;
11768
+ return;
11769
+ }
11770
+ const currentOwner = stdoutFingerprintOwners.get(terminal);
11771
+ if (!currentOwner && !ownsFingerprintFn && hasActiveExternalFingerprintProvider()) {
11772
+ fingerprintInstallBlockedByExternalProvider = true;
11773
+ ownsFingerprintFn = false;
11774
+ terminalFingerprintFnDirty = false;
11775
+ invalidateFingerprintBaseline();
11776
+ return;
11777
+ }
11778
+ fingerprintInstallBlockedByExternalProvider = false;
11779
+ if (currentOwner && currentOwner !== fingerprintOwner) {
11780
+ if (ownsFingerprintFn)
11781
+ invalidateFingerprintBaseline();
11782
+ ownsFingerprintFn = false;
11783
+ return;
11784
+ }
11785
+ if (ownsFingerprintFn && currentOwner === fingerprintOwner && !force && !terminalFingerprintFnDirty) {
11786
+ return;
11787
+ }
11788
+ stdoutFingerprintOwners.set(terminal, fingerprintOwner);
11789
+ ownsFingerprintFn = true;
11790
+ fingerprintTerminal.setFingerprintFn((ch, style) => {
10890
11791
  return cellFingerprint(ch, style);
10891
11792
  });
11793
+ terminalFingerprintFnDirty = false;
11794
+ invalidateFingerprintBaseline();
11795
+ }
11796
+ function releaseFingerprintFn() {
11797
+ var _a2;
11798
+ if (!ownsFingerprintFn)
11799
+ return;
11800
+ if (stdoutFingerprintOwners.get(terminal) !== fingerprintOwner) {
11801
+ ownsFingerprintFn = false;
11802
+ return;
11803
+ }
11804
+ stdoutFingerprintOwners.delete(terminal);
11805
+ ownsFingerprintFn = false;
11806
+ try {
11807
+ (_a2 = fingerprintTerminal.setFingerprintFn) == null ? void 0 : _a2.call(fingerprintTerminal, null);
11808
+ } catch {
11809
+ }
10892
11810
  }
10893
11811
  installFingerprintFn();
10894
- if (altScreen && out.isTTY)
11812
+ if (altScreen && outputIsTTY)
10895
11813
  out.write("\x1B[?1049h");
10896
11814
  if (hideCursor)
10897
11815
  out.write("\x1B[?25l");
@@ -10908,7 +11826,7 @@ function createStdoutRenderer(terminal, options) {
10908
11826
  });
10909
11827
  const resizeSource = (options == null ? void 0 : options.output) ?? import_node_process6.default.stdout;
10910
11828
  const canTrackResize = Boolean(
10911
- trackResize && out.isTTY && typeof (resizeSource == null ? void 0 : resizeSource.on) === "function"
11829
+ trackResize && outputIsTTY && typeof (resizeSource == null ? void 0 : resizeSource.on) === "function"
10912
11830
  );
10913
11831
  const onResize = () => {
10914
11832
  const cols = Number(resizeSource == null ? void 0 : resizeSource.columns);
@@ -10959,7 +11877,9 @@ function createStdoutRenderer(terminal, options) {
10959
11877
  accumulatedDirtyCount = 0;
10960
11878
  accumulatedDirtyMin = Number.POSITIVE_INFINITY;
10961
11879
  accumulatedDirtyMax = -1;
11880
+ accumulatedDirtyRowsRequireRepaint = false;
10962
11881
  off();
11882
+ releaseFingerprintFn();
10963
11883
  if (canTrackResize && typeof (resizeSource == null ? void 0 : resizeSource.off) === "function") {
10964
11884
  try {
10965
11885
  resizeSource.off("resize", onResize);
@@ -10973,7 +11893,7 @@ function createStdoutRenderer(terminal, options) {
10973
11893
  }
10974
11894
  if (hideCursor)
10975
11895
  out.write("\x1B[?25h");
10976
- if (altScreen && out.isTTY)
11896
+ if (altScreen && outputIsTTY)
10977
11897
  out.write("\x1B[?1049l");
10978
11898
  profiler == null ? void 0 : profiler.dispose();
10979
11899
  }
@@ -10987,13 +11907,8 @@ function createStdoutRenderer(terminal, options) {
10987
11907
  }
10988
11908
  if ("palette" in next)
10989
11909
  palette = next.palette ?? null;
10990
- styleKeyCache = /* @__PURE__ */ new WeakMap();
10991
- for (const key of Object.keys(COLOR_INDEX)) {
10992
- if (!(key in BUILTIN_COLOR_INDEX))
10993
- delete COLOR_INDEX[key];
10994
- }
10995
- nextColorIdx = 17;
10996
- installFingerprintFn();
11910
+ resetStyleKeyIndex();
11911
+ installFingerprintFn(true);
10997
11912
  fpRows = 0;
10998
11913
  fpPrevValid = false;
10999
11914
  prevOverlayBlockedRows = [];