@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/README.md +55 -20
- package/dist/{TSelect-HzILuti9.js → TSelect-DR0FomSu.js} +1 -1
- package/dist/{TTree-ChOot2Bh.js → TTree-kzhlB5P0.js} +3 -3
- package/dist/agent.js +2 -2
- package/dist/cli.cjs +1081 -166
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +846 -164
- package/dist/core/buffer/buffer.d.ts +2 -2
- package/dist/core/types.d.ts +0 -4
- package/dist/core.cjs +20 -5
- package/dist/core.js +1 -1
- package/dist/{create-terminal-C2oiWFJU.js → create-terminal-DBFNYr8f.js} +15 -6
- package/dist/{dom-renderer-BqGijQID.js → dom-renderer-BniJSQyX.js} +1 -1
- package/dist/index.cjs +20 -5
- package/dist/index.js +4 -4
- package/dist/renderer/cli/index.d.ts +1 -1
- package/dist/renderer/cli/stdout-renderer.d.ts +50 -7
- package/dist/renderer-dom.js +2 -2
- package/dist/vue.cjs +20 -5
- package/dist/vue.js +4 -4
- package/package.json +28 -10
package/dist/cli.js
CHANGED
|
@@ -3278,14 +3278,14 @@ function clamp(n, min, max) {
|
|
|
3278
3278
|
return Math.max(min, Math.min(max, n));
|
|
3279
3279
|
}
|
|
3280
3280
|
/**
|
|
3281
|
-
* Register a fingerprint function on the buffer. This enables SoA fingerprint
|
|
3281
|
+
* Register or clear a fingerprint function on the buffer. This enables SoA fingerprint
|
|
3282
3282
|
* pre-computation: cell writes will update fingerprints inline, and the renderer
|
|
3283
3283
|
* can read them via getRowFingerprints() instead of per-cell hash computation.
|
|
3284
3284
|
*/
|
|
3285
3285
|
function setFingerprintFn(buffer, fn) {
|
|
3286
3286
|
buffer.fingerprintFn = fn;
|
|
3287
3287
|
const len = buffer.rows * buffer.cols;
|
|
3288
|
-
if (!len) {
|
|
3288
|
+
if (!fn || !len) {
|
|
3289
3289
|
buffer.soaFingerprints = null;
|
|
3290
3290
|
return;
|
|
3291
3291
|
}
|
|
@@ -3306,7 +3306,10 @@ function setFingerprintFn(buffer, fn) {
|
|
|
3306
3306
|
*/
|
|
3307
3307
|
function getRowFingerprints(buffer, y) {
|
|
3308
3308
|
if (!buffer.soaFingerprints) return null;
|
|
3309
|
-
|
|
3309
|
+
if (buffer.rows <= 0 || buffer.cols <= 0) return null;
|
|
3310
|
+
const yy = Math.floor(y);
|
|
3311
|
+
if (!Number.isFinite(yy) || yy < 0 || yy >= buffer.rows) return null;
|
|
3312
|
+
const physY = physicalRowIndex(buffer, yy);
|
|
3310
3313
|
const base = physY * buffer.cols;
|
|
3311
3314
|
return buffer.soaFingerprints.subarray(base, base + buffer.cols);
|
|
3312
3315
|
}
|
|
@@ -4405,11 +4408,15 @@ function createTerminal(opts) {
|
|
|
4405
4408
|
},
|
|
4406
4409
|
setFingerprintFn(fn) {
|
|
4407
4410
|
assertNotDisposed();
|
|
4408
|
-
|
|
4411
|
+
const state = getPlaneState(plane);
|
|
4412
|
+
setFingerprintFn(state.buffer, fn);
|
|
4409
4413
|
},
|
|
4410
4414
|
getRowFingerprints(y) {
|
|
4411
4415
|
assertNotDisposed();
|
|
4412
|
-
|
|
4416
|
+
const yy = Math.floor(y);
|
|
4417
|
+
const state = getPlaneState(plane);
|
|
4418
|
+
if (!Number.isFinite(yy) || yy < 0 || yy >= state.buffer.rows) throw new RangeError("Row out of bounds");
|
|
4419
|
+
return getRowFingerprints(state.buffer, yy);
|
|
4413
4420
|
}
|
|
4414
4421
|
};
|
|
4415
4422
|
planeTerminals.set(plane, api);
|
|
@@ -4585,7 +4592,9 @@ function createTerminal(opts) {
|
|
|
4585
4592
|
},
|
|
4586
4593
|
getRowFingerprints(y) {
|
|
4587
4594
|
assertNotDisposed();
|
|
4588
|
-
|
|
4595
|
+
const yy = Math.floor(y);
|
|
4596
|
+
if (!Number.isFinite(yy) || yy < 0 || yy >= compositeBuffer.rows) throw new RangeError("Row out of bounds");
|
|
4597
|
+
return getRowFingerprints(compositeBuffer, yy);
|
|
4589
4598
|
}
|
|
4590
4599
|
};
|
|
4591
4600
|
base[TERMINAL_PLANE_INTERNALS] = {
|
|
@@ -8422,15 +8431,24 @@ const SYNC_START = "\x1B[?2026h";
|
|
|
8422
8431
|
const SYNC_END = "\x1B[?2026l";
|
|
8423
8432
|
const OSC8_OPEN = (href) => `\u001B]8;;${href}\u0007`;
|
|
8424
8433
|
const OSC8_CLOSE = "\x1B]8;;\x07";
|
|
8434
|
+
const stdoutFingerprintOwners = new WeakMap();
|
|
8425
8435
|
function createStdoutRenderer(terminal, options) {
|
|
8426
8436
|
const env = (process === null || process === void 0 ? void 0 : process.env) ?? {};
|
|
8427
8437
|
if (shouldInstallFileWriters(env)) installNodeFileWriters();
|
|
8428
8438
|
const output = (options === null || options === void 0 ? void 0 : options.output) ?? process.stdout;
|
|
8429
8439
|
if (!output) throw new Error("createStdoutRenderer requires a Node stdout-like output");
|
|
8430
8440
|
const out = output;
|
|
8441
|
+
const outputFd = Number(out.fd);
|
|
8442
|
+
const canUseSyncStdout = (options === null || options === void 0 ? void 0 : options.output) == null && Number.isInteger(outputFd) && outputFd === 1;
|
|
8443
|
+
const fingerprintOwner = Symbol("stdout-renderer");
|
|
8444
|
+
let ownsFingerprintFn = false;
|
|
8445
|
+
const fingerprintTerminal = terminal;
|
|
8446
|
+
const canInstallTerminalFingerprintFn = () => typeof fingerprintTerminal.setFingerprintFn === "function" && typeof fingerprintTerminal.getRowFingerprints === "function";
|
|
8447
|
+
const outputIsTTY = Boolean(out.isTTY);
|
|
8448
|
+
const outputAllowsOsc8Links = out.isTTY !== false;
|
|
8431
8449
|
const clear = (options === null || options === void 0 ? void 0 : options.clear) ?? true;
|
|
8432
8450
|
const hideCursor = (options === null || options === void 0 ? void 0 : options.hideCursor) ?? true;
|
|
8433
|
-
const altScreen = (options === null || options === void 0 ? void 0 : options.altScreen) ??
|
|
8451
|
+
const altScreen = (options === null || options === void 0 ? void 0 : options.altScreen) ?? outputIsTTY;
|
|
8434
8452
|
let defaultBg = (options === null || options === void 0 ? void 0 : options.defaultBg) == null || (options === null || options === void 0 ? void 0 : options.defaultBg) === "transparent" ? void 0 : (options === null || options === void 0 ? void 0 : options.defaultBg) ?? "black";
|
|
8435
8453
|
let palette = (options === null || options === void 0 ? void 0 : options.palette) ?? null;
|
|
8436
8454
|
const trackResize = (options === null || options === void 0 ? void 0 : options.trackResize) ?? true;
|
|
@@ -8444,11 +8462,11 @@ function createStdoutRenderer(terminal, options) {
|
|
|
8444
8462
|
});
|
|
8445
8463
|
function resolveUseSyncOutput() {
|
|
8446
8464
|
if ((options === null || options === void 0 ? void 0 : options.useSyncOutput) != null) return options.useSyncOutput;
|
|
8447
|
-
const term = String(env.TERM ?? "").toLowerCase();
|
|
8465
|
+
const term$1 = String(env.TERM ?? "").toLowerCase();
|
|
8448
8466
|
const termProgram$1 = String(env.TERM_PROGRAM ?? "").toLowerCase();
|
|
8449
8467
|
const termProgramVersion = env.TERM_PROGRAM_VERSION;
|
|
8450
8468
|
const isGhostty$1 = "GHOSTTY_RESOURCES_DIR" in env;
|
|
8451
|
-
const isProblematicTerminal = isGhostty$1 || termProgram$1.includes("apple_terminal") || term.includes("screen") && !term.includes("tmux");
|
|
8469
|
+
const isProblematicTerminal = isGhostty$1 || termProgram$1.includes("apple_terminal") || term$1.includes("screen") && !term$1.includes("tmux");
|
|
8452
8470
|
if (isProblematicTerminal) return false;
|
|
8453
8471
|
const isGoodTerminal = termProgram$1.includes("iterm") && termProgramVersion && compareVersion(String(termProgramVersion), "3.5.0") >= 0;
|
|
8454
8472
|
if (isGoodTerminal) return true;
|
|
@@ -8475,10 +8493,90 @@ function createStdoutRenderer(terminal, options) {
|
|
|
8475
8493
|
const chunkThresholdBytes = 64 * 1024;
|
|
8476
8494
|
const syncMaxBytes = 128 * 1024;
|
|
8477
8495
|
const dirtyFullThreshold = .6;
|
|
8496
|
+
const DIRTY_SPAN_MERGE_GAP_CELLS = 2;
|
|
8497
|
+
const DIRTY_SPAN_MAX_PER_ROW = 12;
|
|
8498
|
+
const DIRTY_SPAN_FULL_ROW_THRESHOLD = .7;
|
|
8499
|
+
const EST_STYLE_SWITCH_BYTES = 8;
|
|
8500
|
+
const EST_CLEAR_TO_EOL_BYTES = 3;
|
|
8478
8501
|
const disableCursorPos = false;
|
|
8479
8502
|
const termProgram = String(env.TERM_PROGRAM ?? "").trim().toLowerCase();
|
|
8503
|
+
const term = String(env.TERM ?? "").trim().toLowerCase();
|
|
8504
|
+
const isKitty = "KITTY_WINDOW_ID" in env || termProgram.includes("kitty") || term.includes("kitty");
|
|
8505
|
+
const isAlacritty = "ALACRITTY_WINDOW_ID" in env || "ALACRITTY_LOG" in env || termProgram.includes("alacritty") || term.includes("alacritty");
|
|
8506
|
+
const isWezTerm = "WEZTERM_PANE" in env || "WEZTERM_EXECUTABLE" in env || termProgram.includes("wezterm");
|
|
8480
8507
|
const isVscodeTerminal = termProgram === "vscode" || "VSCODE_PID" in env || "VSCODE_IPC_HOOK_CLI" in env;
|
|
8481
|
-
const
|
|
8508
|
+
const useConservativeDirtyRows = Boolean(outputIsTTY && (isGhostty || isKitty || isAlacritty || isWezTerm));
|
|
8509
|
+
const normalizeDirtyRowPatchMode = (value) => {
|
|
8510
|
+
const normalized = typeof value === "string" ? value.trim().toLowerCase() : value;
|
|
8511
|
+
return normalized === "auto" || normalized === "row" || normalized === "span" ? normalized : null;
|
|
8512
|
+
};
|
|
8513
|
+
const normalizeColumnDiffMode = (value) => {
|
|
8514
|
+
if (value === true) return "span";
|
|
8515
|
+
if (value === false) return "row";
|
|
8516
|
+
if (typeof value === "string" && value.trim().toLowerCase() === "auto") return "auto";
|
|
8517
|
+
return null;
|
|
8518
|
+
};
|
|
8519
|
+
const firstValidDirtyRowPatchMode = (...values) => {
|
|
8520
|
+
for (const value of values) {
|
|
8521
|
+
const mode = normalizeDirtyRowPatchMode(value);
|
|
8522
|
+
if (mode) return mode;
|
|
8523
|
+
}
|
|
8524
|
+
return null;
|
|
8525
|
+
};
|
|
8526
|
+
function resolveDirtyRowPatchMode() {
|
|
8527
|
+
const explicitMode = options === null || options === void 0 ? void 0 : options.dirtyRowPatchMode;
|
|
8528
|
+
if (explicitMode != null) {
|
|
8529
|
+
const optionMode = normalizeDirtyRowPatchMode(explicitMode);
|
|
8530
|
+
if (!optionMode) throw new Error(`Invalid dirtyRowPatchMode=${JSON.stringify(explicitMode)}; expected "auto", "row", or "span".`);
|
|
8531
|
+
return optionMode;
|
|
8532
|
+
}
|
|
8533
|
+
const legacy = options === null || options === void 0 ? void 0 : options.columnDiff;
|
|
8534
|
+
if (legacy !== void 0) {
|
|
8535
|
+
const legacyMode = normalizeColumnDiffMode(legacy);
|
|
8536
|
+
if (!legacyMode) throw new Error(`Invalid columnDiff=${JSON.stringify(legacy)}; expected boolean or "auto".`);
|
|
8537
|
+
return legacyMode;
|
|
8538
|
+
}
|
|
8539
|
+
const envMode = firstValidDirtyRowPatchMode(env.VUE_TUI_DIRTY_ROW_PATCH_MODE, env.DIMCODE_TUI_DIRTY_ROW_RENDER_MODE, env.DIMCODE_TUI_DIRTY_ROW_PATCH_MODE);
|
|
8540
|
+
if (envMode) return envMode;
|
|
8541
|
+
return "auto";
|
|
8542
|
+
}
|
|
8543
|
+
const dirtyRowPatchMode = resolveDirtyRowPatchMode();
|
|
8544
|
+
const dirtySpanConservativeMaxCells = (() => {
|
|
8545
|
+
if ((options === null || options === void 0 ? void 0 : options.dirtySpanConservativeMaxCells) !== void 0) {
|
|
8546
|
+
const raw$1 = options.dirtySpanConservativeMaxCells;
|
|
8547
|
+
const value$1 = typeof raw$1 === "number" ? raw$1 : Number(raw$1);
|
|
8548
|
+
if (!Number.isFinite(value$1) || value$1 <= 0) throw new Error(`Invalid dirtySpanConservativeMaxCells=${JSON.stringify(raw$1)}; expected a positive finite number.`);
|
|
8549
|
+
return Math.floor(value$1);
|
|
8550
|
+
}
|
|
8551
|
+
const raw = firstNonEmptyEnv(env, "VUE_TUI_DIRTY_SPAN_MAX_CELLS", "DIMCODE_TUI_DIRTY_SPAN_MAX_CELLS");
|
|
8552
|
+
const value = Number(raw);
|
|
8553
|
+
return Number.isFinite(value) && value > 0 ? Math.floor(value) : 32;
|
|
8554
|
+
})();
|
|
8555
|
+
function resolveInternalColumnDiffMode() {
|
|
8556
|
+
const value = options === null || options === void 0 ? void 0 : options.__columnDiffMode;
|
|
8557
|
+
if (value == null) return "auto";
|
|
8558
|
+
if (value === "auto" || value === "single-span" || value === "multi-span" || value === "full-row") return value;
|
|
8559
|
+
throw new Error(`Invalid __columnDiffMode=${JSON.stringify(value)}; expected "auto", "single-span", "multi-span", or "full-row".`);
|
|
8560
|
+
}
|
|
8561
|
+
const columnDiffMode = resolveInternalColumnDiffMode();
|
|
8562
|
+
function resolveEffectiveDirtyRowPatchMode() {
|
|
8563
|
+
switch (columnDiffMode) {
|
|
8564
|
+
case "full-row": return "row";
|
|
8565
|
+
case "single-span": return "single-span";
|
|
8566
|
+
case "multi-span": return "multi-span";
|
|
8567
|
+
case "auto": break;
|
|
8568
|
+
}
|
|
8569
|
+
switch (dirtyRowPatchMode) {
|
|
8570
|
+
case "row": return "row";
|
|
8571
|
+
case "span":
|
|
8572
|
+
case "auto": return "multi-span";
|
|
8573
|
+
}
|
|
8574
|
+
}
|
|
8575
|
+
const effectiveDirtyRowPatchMode = resolveEffectiveDirtyRowPatchMode();
|
|
8576
|
+
const shouldUseDirtySpans = () => effectiveDirtyRowPatchMode !== "row";
|
|
8577
|
+
const shouldUseMultiDirtySpans = () => effectiveDirtyRowPatchMode === "multi-span";
|
|
8578
|
+
const enableOsc8Links = outputAllowsOsc8Links && !isVscodeTerminal;
|
|
8579
|
+
const renderedHref = (value) => enableOsc8Links ? normalizeHref(value) : null;
|
|
8482
8580
|
let disposed = false;
|
|
8483
8581
|
let lastFrameTime = 0;
|
|
8484
8582
|
let pendingRender = false;
|
|
@@ -8491,8 +8589,9 @@ function createStdoutRenderer(terminal, options) {
|
|
|
8491
8589
|
let accumulatedDirtyCount = 0;
|
|
8492
8590
|
let accumulatedDirtyMin = Number.POSITIVE_INFINITY;
|
|
8493
8591
|
let accumulatedDirtyMax = -1;
|
|
8592
|
+
let accumulatedDirtyRowsRequireRepaint = false;
|
|
8494
8593
|
let accumulatedScrollOperations = null;
|
|
8495
|
-
const MIN_FRAME_MS =
|
|
8594
|
+
const MIN_FRAME_MS = outputIsTTY ? 16 : 0;
|
|
8496
8595
|
function clampCellToViewport(cell, size) {
|
|
8497
8596
|
const maxX = Math.max(0, Math.floor(size.cols) - 1);
|
|
8498
8597
|
const maxY = Math.max(0, Math.floor(size.rows) - 1);
|
|
@@ -8511,56 +8610,68 @@ function createStdoutRenderer(terminal, options) {
|
|
|
8511
8610
|
const env$1 = (process === null || process === void 0 ? void 0 : process.env) ?? {};
|
|
8512
8611
|
return detectTerminalColorCapability({
|
|
8513
8612
|
env: env$1,
|
|
8514
|
-
isTTY:
|
|
8613
|
+
isTTY: outputIsTTY,
|
|
8515
8614
|
platform: String((process === null || process === void 0 ? void 0 : process.platform) ?? "")
|
|
8516
8615
|
}).mode;
|
|
8517
8616
|
}
|
|
8518
8617
|
const colorMode = resolveColorMode();
|
|
8519
8618
|
const enableDim = colorMode === "truecolor";
|
|
8520
|
-
const COLOR_INDEX = {
|
|
8521
|
-
black: 1,
|
|
8522
|
-
red: 2,
|
|
8523
|
-
green: 3,
|
|
8524
|
-
yellow: 4,
|
|
8525
|
-
blue: 5,
|
|
8526
|
-
magenta: 6,
|
|
8527
|
-
cyan: 7,
|
|
8528
|
-
white: 8,
|
|
8529
|
-
blackBright: 9,
|
|
8530
|
-
redBright: 10,
|
|
8531
|
-
greenBright: 11,
|
|
8532
|
-
yellowBright: 12,
|
|
8533
|
-
blueBright: 13,
|
|
8534
|
-
magentaBright: 14,
|
|
8535
|
-
cyanBright: 15,
|
|
8536
|
-
whiteBright: 16
|
|
8537
|
-
};
|
|
8538
|
-
const BUILTIN_COLOR_INDEX = { ...COLOR_INDEX };
|
|
8539
|
-
let nextColorIdx = 17;
|
|
8540
|
-
const MAX_COLOR_INDEX = 255;
|
|
8541
|
-
function colorIndex(color) {
|
|
8542
|
-
if (!color) return 0;
|
|
8543
|
-
let idx = COLOR_INDEX[color];
|
|
8544
|
-
if (idx !== void 0) return idx;
|
|
8545
|
-
if (nextColorIdx > MAX_COLOR_INDEX) return MAX_COLOR_INDEX;
|
|
8546
|
-
idx = nextColorIdx++;
|
|
8547
|
-
COLOR_INDEX[color] = idx;
|
|
8548
|
-
return idx;
|
|
8549
|
-
}
|
|
8550
8619
|
let styleKeyCache = new WeakMap();
|
|
8620
|
+
const styleKeyIndex = new Map();
|
|
8621
|
+
let nextStyleKey = 1;
|
|
8622
|
+
const MAX_STYLE_KEYS = 131072;
|
|
8623
|
+
const MAX_UINT32_INTERN_ID = 4294967295;
|
|
8624
|
+
const STYLE_KEY_SEP = "";
|
|
8551
8625
|
const normalizedHrefCache = new Map();
|
|
8552
8626
|
const MAX_HREF_CACHE = 2048;
|
|
8553
|
-
|
|
8627
|
+
let fingerprintInternResetRequiresBaseline = false;
|
|
8628
|
+
let fingerprintBaselineGeneration = 0;
|
|
8629
|
+
let activeRenderBaselineGeneration = 0;
|
|
8630
|
+
let terminalFingerprintFnDirty = false;
|
|
8631
|
+
let fingerprintInstallBlockedByExternalProvider = false;
|
|
8632
|
+
function invalidateFingerprintBaseline() {
|
|
8633
|
+
fpPrevValid = false;
|
|
8634
|
+
fingerprintInternResetRequiresBaseline = true;
|
|
8635
|
+
fingerprintBaselineGeneration++;
|
|
8636
|
+
}
|
|
8637
|
+
const activeRenderBaselineChanged = () => activeRenderBaselineGeneration !== fingerprintBaselineGeneration;
|
|
8638
|
+
function resetStyleKeyIndex(resetIds = false) {
|
|
8639
|
+
styleKeyCache = new WeakMap();
|
|
8640
|
+
styleKeyIndex.clear();
|
|
8641
|
+
if (resetIds) nextStyleKey = 1;
|
|
8642
|
+
terminalFingerprintFnDirty = true;
|
|
8643
|
+
invalidateFingerprintBaseline();
|
|
8644
|
+
}
|
|
8645
|
+
function internStyleKey(signature) {
|
|
8646
|
+
const cached = styleKeyIndex.get(signature);
|
|
8647
|
+
if (cached !== void 0) return cached;
|
|
8648
|
+
if (styleKeyIndex.size >= MAX_STYLE_KEYS) resetStyleKeyIndex();
|
|
8649
|
+
if (nextStyleKey >= MAX_UINT32_INTERN_ID) resetStyleKeyIndex(true);
|
|
8650
|
+
const key = nextStyleKey++;
|
|
8651
|
+
styleKeyIndex.set(signature, key);
|
|
8652
|
+
return key;
|
|
8653
|
+
}
|
|
8654
|
+
function styleSignature(style) {
|
|
8655
|
+
return [
|
|
8656
|
+
style.fg ?? "",
|
|
8657
|
+
style.bg ?? defaultBg ?? "",
|
|
8658
|
+
style.bold ? "1" : "0",
|
|
8659
|
+
enableDim && style.dim ? "1" : "0",
|
|
8660
|
+
style.italic ? "1" : "0",
|
|
8661
|
+
style.underline ? "1" : "0",
|
|
8662
|
+
style.inverse ? "1" : "0",
|
|
8663
|
+
renderedHref(style.href) ? "1" : "0"
|
|
8664
|
+
].join(STYLE_KEY_SEP);
|
|
8665
|
+
}
|
|
8554
8666
|
function styleKey(style) {
|
|
8555
8667
|
const cached = styleKeyCache.get(style);
|
|
8556
8668
|
if (cached !== void 0) return cached;
|
|
8557
|
-
const
|
|
8558
|
-
const key = colorIndex(style.fg) | colorIndex(style.bg ?? defaultBg) << 8 | (style.bold ? 65536 : 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);
|
|
8669
|
+
const key = internStyleKey(styleSignature(style));
|
|
8559
8670
|
styleKeyCache.set(style, key);
|
|
8560
8671
|
return key;
|
|
8561
8672
|
}
|
|
8562
8673
|
function styleKeyFromParts(style) {
|
|
8563
|
-
return
|
|
8674
|
+
return internStyleKey(styleSignature(style));
|
|
8564
8675
|
}
|
|
8565
8676
|
function normalizeHref(value) {
|
|
8566
8677
|
if (typeof value !== "string") return null;
|
|
@@ -8633,17 +8744,25 @@ function createStdoutRenderer(terminal, options) {
|
|
|
8633
8744
|
let prevFP = new Uint32Array(0);
|
|
8634
8745
|
let currentHrefIds = new Uint32Array(0);
|
|
8635
8746
|
let prevHrefIds = new Uint32Array(0);
|
|
8747
|
+
let currentTextIds = new Uint32Array(0);
|
|
8748
|
+
let prevTextIds = new Uint32Array(0);
|
|
8636
8749
|
let fpPrevValid = false;
|
|
8750
|
+
let nextCellTextId = 1;
|
|
8751
|
+
let blankTextId = 0;
|
|
8752
|
+
let emptyTextId = 0;
|
|
8753
|
+
let continuationTextId = 0;
|
|
8754
|
+
const cellTextIdCache = new Map();
|
|
8755
|
+
const MAX_CELL_TEXT_IDS = 131072;
|
|
8637
8756
|
let prevOverlayBlockedRows = [];
|
|
8638
8757
|
let prevOverlayPartialRows = [];
|
|
8639
8758
|
const hrefIndex = new Map();
|
|
8640
8759
|
let nextHrefId = 1;
|
|
8641
8760
|
const MAX_HREF_IDS = 8192;
|
|
8642
8761
|
let hrefIndexResetRequiresBaseline = false;
|
|
8643
|
-
function resetHrefIndex() {
|
|
8762
|
+
function resetHrefIndex(resetIds = false) {
|
|
8644
8763
|
hrefIndex.clear();
|
|
8645
|
-
nextHrefId = 1;
|
|
8646
|
-
|
|
8764
|
+
if (resetIds) nextHrefId = 1;
|
|
8765
|
+
invalidateFingerprintBaseline();
|
|
8647
8766
|
hrefIndexResetRequiresBaseline = true;
|
|
8648
8767
|
}
|
|
8649
8768
|
function ensureFingerprints(cols, rows) {
|
|
@@ -8655,44 +8774,359 @@ function createStdoutRenderer(terminal, options) {
|
|
|
8655
8774
|
prevFP = new Uint32Array(len);
|
|
8656
8775
|
currentHrefIds = new Uint32Array(len);
|
|
8657
8776
|
prevHrefIds = new Uint32Array(len);
|
|
8777
|
+
currentTextIds = new Uint32Array(len);
|
|
8778
|
+
prevTextIds = new Uint32Array(len);
|
|
8658
8779
|
fpPrevValid = false;
|
|
8659
8780
|
prevOverlayBlockedRows = [];
|
|
8660
8781
|
prevOverlayPartialRows = [];
|
|
8661
8782
|
}
|
|
8662
|
-
function
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
|
|
8667
|
-
|
|
8783
|
+
function cellFingerprintFromStyleKey(_ch, key) {
|
|
8784
|
+
return key >>> 0;
|
|
8785
|
+
}
|
|
8786
|
+
function cellFingerprint(_ch, style) {
|
|
8787
|
+
return styleKey(style) >>> 0;
|
|
8788
|
+
}
|
|
8789
|
+
function allocateCellTextId(text) {
|
|
8790
|
+
const id = nextCellTextId++;
|
|
8791
|
+
cellTextIdCache.set(text, id);
|
|
8792
|
+
return id;
|
|
8793
|
+
}
|
|
8794
|
+
function resetCellTextIdCache(resetIds = false) {
|
|
8795
|
+
cellTextIdCache.clear();
|
|
8796
|
+
if (resetIds) nextCellTextId = 1;
|
|
8797
|
+
blankTextId = allocateCellTextId(" ");
|
|
8798
|
+
emptyTextId = allocateCellTextId("");
|
|
8799
|
+
continuationTextId = nextCellTextId++;
|
|
8800
|
+
invalidateFingerprintBaseline();
|
|
8801
|
+
}
|
|
8802
|
+
function cellIdentityTextId(cell) {
|
|
8803
|
+
if (cell.continuation) return continuationTextId;
|
|
8804
|
+
if (cell.ch === "") return emptyTextId;
|
|
8805
|
+
return cellTextId(cell.ch);
|
|
8806
|
+
}
|
|
8807
|
+
function cellTextId(text) {
|
|
8808
|
+
let cached = cellTextIdCache.get(text);
|
|
8809
|
+
if (cached !== void 0) return cached;
|
|
8810
|
+
const mustResetTextIds = nextCellTextId >= MAX_UINT32_INTERN_ID - 2;
|
|
8811
|
+
if (cellTextIdCache.size >= MAX_CELL_TEXT_IDS || mustResetTextIds) {
|
|
8812
|
+
resetCellTextIdCache(mustResetTextIds);
|
|
8813
|
+
cached = cellTextIdCache.get(text);
|
|
8814
|
+
if (cached !== void 0) return cached;
|
|
8668
8815
|
}
|
|
8669
|
-
return
|
|
8816
|
+
return allocateCellTextId(text);
|
|
8670
8817
|
}
|
|
8818
|
+
resetCellTextIdCache(true);
|
|
8671
8819
|
function hrefId(href) {
|
|
8672
8820
|
if (!href) return 0;
|
|
8673
8821
|
const cached = hrefIndex.get(href);
|
|
8674
8822
|
if (cached != null) return cached;
|
|
8675
|
-
|
|
8823
|
+
const mustResetHrefIds = nextHrefId >= MAX_UINT32_INTERN_ID;
|
|
8824
|
+
if (hrefIndex.size >= MAX_HREF_IDS || mustResetHrefIds) resetHrefIndex(mustResetHrefIds);
|
|
8676
8825
|
const id = nextHrefId++;
|
|
8677
8826
|
hrefIndex.set(href, id);
|
|
8678
8827
|
return id;
|
|
8679
8828
|
}
|
|
8680
|
-
|
|
8681
|
-
|
|
8829
|
+
const BLANK_STYLE = Object.freeze({});
|
|
8830
|
+
const BLANK_CELL = Object.freeze({
|
|
8831
|
+
ch: " ",
|
|
8832
|
+
width: 1,
|
|
8833
|
+
style: BLANK_STYLE
|
|
8834
|
+
});
|
|
8835
|
+
const cellAt = (row, x) => row[x] ?? BLANK_CELL;
|
|
8836
|
+
function rowFingerprintsForThisRenderer(y) {
|
|
8837
|
+
var _fingerprintTerminal$;
|
|
8838
|
+
if (terminalFingerprintFnDirty) return null;
|
|
8839
|
+
if (!ownsFingerprintFn || stdoutFingerprintOwners.get(terminal) !== fingerprintOwner) return null;
|
|
8840
|
+
return ((_fingerprintTerminal$ = fingerprintTerminal.getRowFingerprints) === null || _fingerprintTerminal$ === void 0 ? void 0 : _fingerprintTerminal$.call(fingerprintTerminal, y)) ?? null;
|
|
8841
|
+
}
|
|
8842
|
+
function hasActiveExternalFingerprintProvider() {
|
|
8843
|
+
if (!canInstallTerminalFingerprintFn()) return false;
|
|
8844
|
+
if (stdoutFingerprintOwners.has(terminal)) return false;
|
|
8845
|
+
try {
|
|
8846
|
+
const rows = terminal.size().rows;
|
|
8847
|
+
if (rows <= 0) return false;
|
|
8848
|
+
return fingerprintTerminal.getRowFingerprints(0) != null;
|
|
8849
|
+
} catch {
|
|
8850
|
+
return false;
|
|
8851
|
+
}
|
|
8852
|
+
}
|
|
8853
|
+
function shouldAttemptFingerprintInstall() {
|
|
8854
|
+
return !fingerprintInstallBlockedByExternalProvider || !hasActiveExternalFingerprintProvider();
|
|
8682
8855
|
}
|
|
8683
8856
|
function fingerprintRow(row, y, cols) {
|
|
8684
|
-
const rowFP =
|
|
8857
|
+
const rowFP = rowFingerprintsForThisRenderer(y);
|
|
8685
8858
|
const base = y * fpCols;
|
|
8686
|
-
if (rowFP && rowFP.length >= cols) currentFP.set(rowFP.subarray(0, cols), base);
|
|
8859
|
+
if (rowFP && rowFP.length >= cols && row.length >= cols) currentFP.set(rowFP.subarray(0, cols), base);
|
|
8687
8860
|
else for (let x = 0; x < cols; x++) {
|
|
8688
|
-
const cell = row
|
|
8861
|
+
const cell = cellAt(row, x);
|
|
8689
8862
|
currentFP[base + x] = cellFingerprint(cell.ch, cell.style);
|
|
8690
8863
|
}
|
|
8691
8864
|
for (let x = 0; x < cols; x++) {
|
|
8692
|
-
const cell = row
|
|
8693
|
-
currentHrefIds[base + x] = hrefId(
|
|
8865
|
+
const cell = cellAt(row, x);
|
|
8866
|
+
currentHrefIds[base + x] = hrefId(renderedHref(cell.style.href));
|
|
8867
|
+
currentTextIds[base + x] = cellIdentityTextId(cell);
|
|
8868
|
+
}
|
|
8869
|
+
}
|
|
8870
|
+
function snapshotRowFingerprints(row, y, cols) {
|
|
8871
|
+
const fp = new Uint32Array(cols);
|
|
8872
|
+
const hrefIds = new Uint32Array(cols);
|
|
8873
|
+
const textIds = new Uint32Array(cols);
|
|
8874
|
+
const rowFP = rowFingerprintsForThisRenderer(y);
|
|
8875
|
+
if (rowFP && rowFP.length >= cols && row.length >= cols) fp.set(rowFP.subarray(0, cols));
|
|
8876
|
+
else for (let x = 0; x < cols; x++) {
|
|
8877
|
+
const cell = cellAt(row, x);
|
|
8878
|
+
fp[x] = cellFingerprint(cell.ch, cell.style);
|
|
8879
|
+
}
|
|
8880
|
+
for (let x = 0; x < cols; x++) {
|
|
8881
|
+
const cell = cellAt(row, x);
|
|
8882
|
+
hrefIds[x] = hrefId(renderedHref(cell.style.href));
|
|
8883
|
+
textIds[x] = cellIdentityTextId(cell);
|
|
8694
8884
|
}
|
|
8885
|
+
return {
|
|
8886
|
+
fp,
|
|
8887
|
+
hrefIds,
|
|
8888
|
+
textIds
|
|
8889
|
+
};
|
|
8695
8890
|
}
|
|
8891
|
+
const currentCellMatchesPrevious = (base, x) => {
|
|
8892
|
+
return currentFP[base + x] === prevFP[base + x] && currentTextIds[base + x] === prevTextIds[base + x] && currentHrefIds[base + x] === prevHrefIds[base + x];
|
|
8893
|
+
};
|
|
8894
|
+
const cellMatchesPreviousAt = (currentBase, previousBase, x) => {
|
|
8895
|
+
return currentFP[currentBase + x] === prevFP[previousBase + x] && currentTextIds[currentBase + x] === prevTextIds[previousBase + x] && currentHrefIds[currentBase + x] === prevHrefIds[previousBase + x];
|
|
8896
|
+
};
|
|
8897
|
+
const referenceCellMatchesCurrent = (row, rowFP, base, x, cols, referenceFP, referenceHrefIds, referenceTextIds) => {
|
|
8898
|
+
const cell = cellAt(row, x);
|
|
8899
|
+
const fingerprint = rowFP && rowFP.length >= cols && row.length >= cols ? rowFP[x] : cellFingerprint(cell.ch, cell.style);
|
|
8900
|
+
return fingerprint === referenceFP[base + x] && cellIdentityTextId(cell) === referenceTextIds[base + x] && hrefId(renderedHref(cell.style.href)) === referenceHrefIds[base + x];
|
|
8901
|
+
};
|
|
8902
|
+
const fullRowDirtySpan = (cols) => {
|
|
8903
|
+
return cols > 0 ? [{
|
|
8904
|
+
startX: 0,
|
|
8905
|
+
endXExclusive: cols
|
|
8906
|
+
}] : [];
|
|
8907
|
+
};
|
|
8908
|
+
const dirtySpanWidth = (span) => Math.max(0, span.endXExclusive - span.startX);
|
|
8909
|
+
const dirtySpanCoverage = (spans) => {
|
|
8910
|
+
let total = 0;
|
|
8911
|
+
for (const span of spans) total += dirtySpanWidth(span);
|
|
8912
|
+
return total;
|
|
8913
|
+
};
|
|
8914
|
+
const estimatedCursorMoveBytes = (y, x) => {
|
|
8915
|
+
return 3 + String(y + 1).length + 1 + String(x + 1).length + 1;
|
|
8916
|
+
};
|
|
8917
|
+
const estimatedRenderableTextBytes = (row, startX, endXExclusive, cols) => {
|
|
8918
|
+
const start = Math.max(0, Math.min(cols, Math.floor(startX)));
|
|
8919
|
+
const end = Math.max(start, Math.min(cols, Math.floor(endXExclusive)));
|
|
8920
|
+
let bytes = 0;
|
|
8921
|
+
for (let x = start; x < end; x++) {
|
|
8922
|
+
const cell = cellAt(row, x);
|
|
8923
|
+
if (cell.continuation) continue;
|
|
8924
|
+
bytes += Buffer.byteLength(cell.ch || " ", "utf8");
|
|
8925
|
+
}
|
|
8926
|
+
return bytes;
|
|
8927
|
+
};
|
|
8928
|
+
const estimatedSpanPatchBytes = (row, spans, y, cols, clearToEol) => {
|
|
8929
|
+
let bytes = 0;
|
|
8930
|
+
for (const span of spans) {
|
|
8931
|
+
bytes += estimatedCursorMoveBytes(y, span.startX);
|
|
8932
|
+
bytes += estimatedRenderableTextBytes(row, span.startX, span.endXExclusive, cols);
|
|
8933
|
+
bytes += EST_STYLE_SWITCH_BYTES;
|
|
8934
|
+
}
|
|
8935
|
+
if (clearToEol) bytes += EST_CLEAR_TO_EOL_BYTES;
|
|
8936
|
+
return bytes;
|
|
8937
|
+
};
|
|
8938
|
+
const estimatedContiguousPatchBytes = (row, spans, y, cols, clearToEol) => {
|
|
8939
|
+
if (!spans.length) return 0;
|
|
8940
|
+
const first = spans[0];
|
|
8941
|
+
const last = spans[spans.length - 1];
|
|
8942
|
+
return estimatedCursorMoveBytes(y, first.startX) + estimatedRenderableTextBytes(row, first.startX, last.endXExclusive, cols) + EST_STYLE_SWITCH_BYTES + (clearToEol ? EST_CLEAR_TO_EOL_BYTES : 0);
|
|
8943
|
+
};
|
|
8944
|
+
const estimatedFullRowPatchBytes = (row, y, cols, clearToEol) => {
|
|
8945
|
+
return estimatedCursorMoveBytes(y, 0) + EST_STYLE_SWITCH_BYTES + estimatedRenderableTextBytes(row, 0, cols, cols) + (clearToEol ? EST_CLEAR_TO_EOL_BYTES : 0);
|
|
8946
|
+
};
|
|
8947
|
+
const resolveDirtySpanRenderMode = (row, spans, y, cols, clearToEol) => {
|
|
8948
|
+
if (spans.length <= 0) return "spans";
|
|
8949
|
+
const forceSpanPatch = dirtyRowPatchMode === "span" || columnDiffMode === "single-span" || columnDiffMode === "multi-span";
|
|
8950
|
+
const coverage = dirtySpanCoverage(spans);
|
|
8951
|
+
const fullRowCoverageLimit = Math.max(1, Math.floor(cols * DIRTY_SPAN_FULL_ROW_THRESHOLD));
|
|
8952
|
+
if (!forceSpanPatch && dirtyRowPatchMode === "auto" && useConservativeDirtyRows && coverage > dirtySpanConservativeMaxCells) return "row";
|
|
8953
|
+
if (!forceSpanPatch && coverage >= fullRowCoverageLimit) return "row";
|
|
8954
|
+
const fullRowCost = estimatedFullRowPatchBytes(row, y, cols, clearToEol);
|
|
8955
|
+
if (spans.length === 1) {
|
|
8956
|
+
const spanCost = estimatedSpanPatchBytes(row, spans, y, cols, clearToEol);
|
|
8957
|
+
return forceSpanPatch || spanCost < fullRowCost ? "spans" : "row";
|
|
8958
|
+
}
|
|
8959
|
+
const multiCost = estimatedSpanPatchBytes(row, spans, y, cols, clearToEol);
|
|
8960
|
+
const first = spans[0];
|
|
8961
|
+
const last = spans[spans.length - 1];
|
|
8962
|
+
const contiguousWidth = Math.max(0, last.endXExclusive - first.startX);
|
|
8963
|
+
const canUseContiguous = forceSpanPatch || contiguousWidth < fullRowCoverageLimit;
|
|
8964
|
+
const contiguousCost = canUseContiguous ? estimatedContiguousPatchBytes(row, spans, y, cols, clearToEol) : Number.POSITIVE_INFINITY;
|
|
8965
|
+
const tooManySpans = !forceSpanPatch && spans.length > DIRTY_SPAN_MAX_PER_ROW;
|
|
8966
|
+
const effectiveMultiCost = tooManySpans ? Number.POSITIVE_INFINITY : multiCost;
|
|
8967
|
+
if (forceSpanPatch) return contiguousCost < multiCost ? "contiguous" : "spans";
|
|
8968
|
+
if (contiguousCost < effectiveMultiCost && contiguousCost < fullRowCost) return "contiguous";
|
|
8969
|
+
if (!tooManySpans && multiCost < fullRowCost) return "spans";
|
|
8970
|
+
return "row";
|
|
8971
|
+
};
|
|
8972
|
+
const expandSpanStart = (row, startX, cols) => {
|
|
8973
|
+
var _row$x;
|
|
8974
|
+
let x = Math.max(0, Math.min(cols, Math.floor(startX)));
|
|
8975
|
+
while (x > 0 && ((_row$x = row[x]) === null || _row$x === void 0 ? void 0 : _row$x.continuation)) x--;
|
|
8976
|
+
const prev = row[x - 1];
|
|
8977
|
+
if (prev && prev.width === 2 && !prev.continuation) x--;
|
|
8978
|
+
return Math.max(0, x);
|
|
8979
|
+
};
|
|
8980
|
+
const expandSpanEnd = (row, endXExclusive, cols) => {
|
|
8981
|
+
var _row$x2;
|
|
8982
|
+
let x = Math.max(0, Math.min(cols, Math.floor(endXExclusive)));
|
|
8983
|
+
const last = row[x - 1];
|
|
8984
|
+
if (last && last.width === 2 && !last.continuation) x++;
|
|
8985
|
+
while (x < cols && ((_row$x2 = row[x]) === null || _row$x2 === void 0 ? void 0 : _row$x2.continuation)) x++;
|
|
8986
|
+
return Math.min(cols, x);
|
|
8987
|
+
};
|
|
8988
|
+
const previousCellLooksLikeWideContinuation = (referenceTextIds, base, x) => {
|
|
8989
|
+
if (x <= 0 || x >= fpCols) return false;
|
|
8990
|
+
return referenceTextIds[base + x] === continuationTextId;
|
|
8991
|
+
};
|
|
8992
|
+
const expandSpanForReferenceWideGlyphs = (span, referenceTextIds, base, cols) => {
|
|
8993
|
+
let startX = span.startX;
|
|
8994
|
+
let endXExclusive = span.endXExclusive;
|
|
8995
|
+
const limit = Math.max(0, Math.min(cols, fpCols));
|
|
8996
|
+
if (startX > 0) {
|
|
8997
|
+
if (previousCellLooksLikeWideContinuation(referenceTextIds, base, startX)) startX--;
|
|
8998
|
+
else if (previousCellLooksLikeWideContinuation(referenceTextIds, base, startX - 1)) startX = Math.max(0, startX - 2);
|
|
8999
|
+
}
|
|
9000
|
+
while (endXExclusive < limit) {
|
|
9001
|
+
if (!previousCellLooksLikeWideContinuation(referenceTextIds, base, endXExclusive)) break;
|
|
9002
|
+
endXExclusive++;
|
|
9003
|
+
}
|
|
9004
|
+
return {
|
|
9005
|
+
startX,
|
|
9006
|
+
endXExclusive
|
|
9007
|
+
};
|
|
9008
|
+
};
|
|
9009
|
+
const widenSpanToCurrentGlyphBoundaries = (row, span, cols) => {
|
|
9010
|
+
return {
|
|
9011
|
+
startX: expandSpanStart(row, span.startX, cols),
|
|
9012
|
+
endXExclusive: expandSpanEnd(row, span.endXExclusive, cols)
|
|
9013
|
+
};
|
|
9014
|
+
};
|
|
9015
|
+
const expandSpansForReferenceWideGlyphs = (spans, row, y, cols, referenceTextIds) => {
|
|
9016
|
+
if (!spans.length || !fpCols || y < 0 || y >= fpRows) return spans;
|
|
9017
|
+
const base = y * fpCols;
|
|
9018
|
+
const expanded = [];
|
|
9019
|
+
for (const span of spans) {
|
|
9020
|
+
const next = widenSpanToCurrentGlyphBoundaries(row, expandSpanForReferenceWideGlyphs(span, referenceTextIds, base, cols), cols);
|
|
9021
|
+
const prev = expanded[expanded.length - 1];
|
|
9022
|
+
if (prev && next.startX <= prev.endXExclusive + DIRTY_SPAN_MERGE_GAP_CELLS) {
|
|
9023
|
+
expanded[expanded.length - 1] = {
|
|
9024
|
+
startX: prev.startX,
|
|
9025
|
+
endXExclusive: Math.max(prev.endXExclusive, next.endXExclusive)
|
|
9026
|
+
};
|
|
9027
|
+
continue;
|
|
9028
|
+
}
|
|
9029
|
+
expanded.push(next);
|
|
9030
|
+
}
|
|
9031
|
+
return expanded;
|
|
9032
|
+
};
|
|
9033
|
+
const pushDirtySpan = (spans, row, startX, endXExclusive, cols) => {
|
|
9034
|
+
let expandedStart = Math.max(0, Math.min(cols, startX));
|
|
9035
|
+
let expandedEnd = Math.max(expandedStart, Math.min(cols, endXExclusive));
|
|
9036
|
+
if (expandedEnd <= expandedStart) return;
|
|
9037
|
+
expandedStart = expandSpanStart(row, expandedStart, cols);
|
|
9038
|
+
expandedEnd = expandSpanEnd(row, expandedEnd, cols);
|
|
9039
|
+
expandedStart = Math.max(0, Math.min(cols, expandedStart));
|
|
9040
|
+
expandedEnd = Math.max(expandedStart, Math.min(cols, expandedEnd));
|
|
9041
|
+
if (expandedEnd <= expandedStart) return;
|
|
9042
|
+
const prev = spans[spans.length - 1];
|
|
9043
|
+
if (prev && expandedStart <= prev.endXExclusive + DIRTY_SPAN_MERGE_GAP_CELLS) {
|
|
9044
|
+
spans[spans.length - 1] = {
|
|
9045
|
+
startX: prev.startX,
|
|
9046
|
+
endXExclusive: Math.max(prev.endXExclusive, expandedEnd)
|
|
9047
|
+
};
|
|
9048
|
+
return;
|
|
9049
|
+
}
|
|
9050
|
+
spans.push({
|
|
9051
|
+
startX: expandedStart,
|
|
9052
|
+
endXExclusive: expandedEnd
|
|
9053
|
+
});
|
|
9054
|
+
};
|
|
9055
|
+
const collectChangedSpans = (row, cols, changedAt) => {
|
|
9056
|
+
const limit = Math.max(0, cols);
|
|
9057
|
+
if (limit <= 0) return [];
|
|
9058
|
+
const spans = [];
|
|
9059
|
+
let startX = -1;
|
|
9060
|
+
for (let x = 0; x < limit; x++) {
|
|
9061
|
+
if (changedAt(x)) {
|
|
9062
|
+
if (startX < 0) startX = x;
|
|
9063
|
+
continue;
|
|
9064
|
+
}
|
|
9065
|
+
if (startX >= 0) {
|
|
9066
|
+
pushDirtySpan(spans, row, startX, x, cols);
|
|
9067
|
+
startX = -1;
|
|
9068
|
+
}
|
|
9069
|
+
}
|
|
9070
|
+
if (startX >= 0) pushDirtySpan(spans, row, startX, limit, cols);
|
|
9071
|
+
return spans;
|
|
9072
|
+
};
|
|
9073
|
+
const resolveChangedSpans = (row, y, cols, nextSnapshot) => {
|
|
9074
|
+
if (!fpCols || cols !== fpCols || y >= fpRows || !fpPrevValid) return fullRowDirtySpan(cols);
|
|
9075
|
+
const base = y * fpCols;
|
|
9076
|
+
let spans;
|
|
9077
|
+
if (!nextSnapshot) spans = expandSpansForReferenceWideGlyphs(collectChangedSpans(row, cols, (x) => !currentCellMatchesPrevious(base, x)), row, y, cols, prevTextIds);
|
|
9078
|
+
else spans = expandSpansForReferenceWideGlyphs(collectChangedSpans(row, cols, (x) => {
|
|
9079
|
+
return nextSnapshot.fp[x] !== prevFP[base + x] || nextSnapshot.textIds[x] !== prevTextIds[base + x] || nextSnapshot.hrefIds[x] !== prevHrefIds[base + x];
|
|
9080
|
+
}), row, y, cols, prevTextIds);
|
|
9081
|
+
return activeRenderBaselineChanged() ? fullRowDirtySpan(cols) : spans;
|
|
9082
|
+
};
|
|
9083
|
+
const resolveChangedSpansAgainstFill = (row, y, cols, fillFingerprint, fillTextId = blankTextId, fillHrefId = 0) => {
|
|
9084
|
+
if (!fpCols || cols !== fpCols || y >= fpRows) return fullRowDirtySpan(cols);
|
|
9085
|
+
const base = y * fpCols;
|
|
9086
|
+
const spans = collectChangedSpans(row, cols, (x) => {
|
|
9087
|
+
return currentFP[base + x] !== fillFingerprint || currentTextIds[base + x] !== fillTextId || currentHrefIds[base + x] !== fillHrefId;
|
|
9088
|
+
});
|
|
9089
|
+
return activeRenderBaselineChanged() ? fullRowDirtySpan(cols) : spans;
|
|
9090
|
+
};
|
|
9091
|
+
const resolveChangedSpansAgainstReference = (row, y, cols, referenceFP, referenceHrefIds, referenceTextIds) => {
|
|
9092
|
+
if (!fpCols || cols !== fpCols || y >= fpRows) return fullRowDirtySpan(cols);
|
|
9093
|
+
const rowFP = rowFingerprintsForThisRenderer(y);
|
|
9094
|
+
const base = y * fpCols;
|
|
9095
|
+
const spans = expandSpansForReferenceWideGlyphs(collectChangedSpans(row, cols, (x) => {
|
|
9096
|
+
return !referenceCellMatchesCurrent(row, rowFP, base, x, cols, referenceFP, referenceHrefIds, referenceTextIds);
|
|
9097
|
+
}), row, y, cols, referenceTextIds);
|
|
9098
|
+
return activeRenderBaselineChanged() ? fullRowDirtySpan(cols) : spans;
|
|
9099
|
+
};
|
|
9100
|
+
const isEolClearEquivalentBlankCell = (cell, defaultBlankStyleKey) => {
|
|
9101
|
+
if (!cell) return true;
|
|
9102
|
+
if (cell.continuation) return false;
|
|
9103
|
+
const ch = cell.ch || " ";
|
|
9104
|
+
if (ch !== " ") return false;
|
|
9105
|
+
if (cell.width !== 1) return false;
|
|
9106
|
+
if (renderedHref(cell.style.href)) return false;
|
|
9107
|
+
return styleKey(cell.style) === defaultBlankStyleKey;
|
|
9108
|
+
};
|
|
9109
|
+
const lastExplicitPaintColumnInRow = (row, cols, defaultBlankStyleKey) => {
|
|
9110
|
+
const limit = Math.max(0, cols);
|
|
9111
|
+
for (let x = limit - 1; x >= 0; x--) {
|
|
9112
|
+
const cell = cellAt(row, x);
|
|
9113
|
+
if (!isEolClearEquivalentBlankCell(cell, defaultBlankStyleKey)) {
|
|
9114
|
+
if (cell && !cell.continuation && cell.width === 2 && x + 1 < limit) return x + 1;
|
|
9115
|
+
return x;
|
|
9116
|
+
}
|
|
9117
|
+
}
|
|
9118
|
+
return -1;
|
|
9119
|
+
};
|
|
9120
|
+
const shouldRewriteRowTail = (row, y, cols, referenceFP, referenceHrefIds, referenceTextIds, defaultBlankStyleKey = styleKeyFromParts({ bg: defaultBg })) => {
|
|
9121
|
+
if (!fpCols || y >= fpRows) return true;
|
|
9122
|
+
const currentTailStart = Math.max(0, Math.min(cols, lastExplicitPaintColumnInRow(row, cols, defaultBlankStyleKey) + 1));
|
|
9123
|
+
if (currentTailStart >= cols) return false;
|
|
9124
|
+
const blankFP = cellFingerprintFromStyleKey(" ", defaultBlankStyleKey);
|
|
9125
|
+
const base = y * fpCols;
|
|
9126
|
+
const limit = Math.min(cols, fpCols);
|
|
9127
|
+
for (let x = currentTailStart; x < limit; x++) if (referenceFP[base + x] !== blankFP || referenceTextIds[base + x] !== blankTextId || referenceHrefIds[base + x] !== 0) return true;
|
|
9128
|
+
return false;
|
|
9129
|
+
};
|
|
8696
9130
|
const rowCursorToCol1 = [];
|
|
8697
9131
|
const rowClearToEol = [];
|
|
8698
9132
|
const rowTextPartsScratch = [];
|
|
@@ -8755,12 +9189,12 @@ function createStdoutRenderer(terminal, options) {
|
|
|
8755
9189
|
rowClearToEol[y] = `\u001B[${y + 1};1H\u001B[K`;
|
|
8756
9190
|
}
|
|
8757
9191
|
};
|
|
8758
|
-
const rowHasNonDefaultBlankCell = (
|
|
9192
|
+
const rowHasNonDefaultBlankCell = (sourceFP, sourceTextIds, sourceHrefIds, y, cols) => {
|
|
8759
9193
|
if (!fpCols || y < 0 || y >= fpRows) return false;
|
|
8760
|
-
const blankFP = styleKeyFromParts({ bg: defaultBg })
|
|
9194
|
+
const blankFP = cellFingerprintFromStyleKey(" ", styleKeyFromParts({ bg: defaultBg }));
|
|
8761
9195
|
const base = y * fpCols;
|
|
8762
9196
|
const limit = Math.min(cols, fpCols);
|
|
8763
|
-
for (let x = 0; x < limit; x++) if (
|
|
9197
|
+
for (let x = 0; x < limit; x++) if (sourceFP[base + x] !== blankFP || sourceTextIds[base + x] !== blankTextId || sourceHrefIds[base + x] !== 0) return true;
|
|
8764
9198
|
return false;
|
|
8765
9199
|
};
|
|
8766
9200
|
function overlayPlaneCoverageRows(totalRows) {
|
|
@@ -8781,7 +9215,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
8781
9215
|
const raw = String(firstNonEmptyEnv(env, "VUE_TUI_SCROLL_REGIONS", "DIMCODE_TUI_SCROLL_REGIONS") ?? "").trim();
|
|
8782
9216
|
if (raw === "0" || raw === "false") return false;
|
|
8783
9217
|
if (isGhostty || isVscodeTerminal) return false;
|
|
8784
|
-
return
|
|
9218
|
+
return outputIsTTY;
|
|
8785
9219
|
})();
|
|
8786
9220
|
function prepareExplicitScrollOperations(operations, blockedRows) {
|
|
8787
9221
|
if (!operations.length || !(blockedRows === null || blockedRows === void 0 ? void 0 : blockedRows.size)) return {
|
|
@@ -8904,13 +9338,13 @@ function createStdoutRenderer(terminal, options) {
|
|
|
8904
9338
|
for (let y = band.start; y < band.end; y++) {
|
|
8905
9339
|
const srcY = y + delta;
|
|
8906
9340
|
if (srcY < band.start || srcY >= band.end) continue;
|
|
8907
|
-
const informative = rowHasNonDefaultBlankCell(currentFP, y, cols) || rowHasNonDefaultBlankCell(prevFP, srcY, cols);
|
|
9341
|
+
const informative = rowHasNonDefaultBlankCell(currentFP, currentTextIds, currentHrefIds, y, cols) || rowHasNonDefaultBlankCell(prevFP, prevTextIds, prevHrefIds, srcY, cols);
|
|
8908
9342
|
if (!informative) continue;
|
|
8909
9343
|
checked++;
|
|
8910
9344
|
const curBase = y * fpCols;
|
|
8911
9345
|
const prevBase = srcY * fpCols;
|
|
8912
9346
|
let rowMatch = true;
|
|
8913
|
-
for (let x = 0; x < cols; x++) if (
|
|
9347
|
+
for (let x = 0; x < cols; x++) if (!cellMatchesPreviousAt(curBase, prevBase, x)) {
|
|
8914
9348
|
rowMatch = false;
|
|
8915
9349
|
break;
|
|
8916
9350
|
}
|
|
@@ -8935,7 +9369,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
8935
9369
|
const curBase = y * fpCols;
|
|
8936
9370
|
const prevBase = srcY * fpCols;
|
|
8937
9371
|
let rowMatch = true;
|
|
8938
|
-
for (let x = 0; x < cols; x++) if (
|
|
9372
|
+
for (let x = 0; x < cols; x++) if (!cellMatchesPreviousAt(curBase, prevBase, x)) {
|
|
8939
9373
|
rowMatch = false;
|
|
8940
9374
|
break;
|
|
8941
9375
|
}
|
|
@@ -8953,45 +9387,83 @@ function createStdoutRenderer(terminal, options) {
|
|
|
8953
9387
|
}
|
|
8954
9388
|
return null;
|
|
8955
9389
|
}
|
|
9390
|
+
const isHighSurrogate = (code) => code >= 55296 && code <= 56319;
|
|
9391
|
+
const isLowSurrogate = (code) => code >= 56320 && code <= 57343;
|
|
9392
|
+
const codePointEnd = (data, index) => {
|
|
9393
|
+
const code = data.charCodeAt(index);
|
|
9394
|
+
if (isHighSurrogate(code) && index + 1 < data.length) {
|
|
9395
|
+
const next = data.charCodeAt(index + 1);
|
|
9396
|
+
if (isLowSurrogate(next)) return index + 2;
|
|
9397
|
+
}
|
|
9398
|
+
return index + 1;
|
|
9399
|
+
};
|
|
9400
|
+
const codePointBefore = (data, index) => {
|
|
9401
|
+
if (index <= 0) return null;
|
|
9402
|
+
const prev = data.charCodeAt(index - 1);
|
|
9403
|
+
if (isLowSurrogate(prev) && index - 2 >= 0) {
|
|
9404
|
+
const high = data.charCodeAt(index - 2);
|
|
9405
|
+
if (isHighSurrogate(high)) return data.codePointAt(index - 2) ?? null;
|
|
9406
|
+
}
|
|
9407
|
+
return data.codePointAt(index - 1) ?? null;
|
|
9408
|
+
};
|
|
9409
|
+
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;
|
|
9410
|
+
const shouldAttachToPreviousGrapheme = (data, index) => {
|
|
9411
|
+
const codePoint = data.codePointAt(index);
|
|
9412
|
+
if (codePoint == null) return false;
|
|
9413
|
+
if (codePoint === 8205) return true;
|
|
9414
|
+
if (codePointBefore(data, index) === 8205) return true;
|
|
9415
|
+
return isGraphemeContinuationCodePoint(codePoint);
|
|
9416
|
+
};
|
|
9417
|
+
const safeChunkEnd = (data, start, maxBytes) => {
|
|
9418
|
+
let end = start;
|
|
9419
|
+
let bytes = 0;
|
|
9420
|
+
const limit = Math.max(1, Math.floor(maxBytes));
|
|
9421
|
+
while (end < data.length) {
|
|
9422
|
+
const next = codePointEnd(data, end);
|
|
9423
|
+
const partBytes = Buffer.byteLength(data.slice(end, next), "utf8");
|
|
9424
|
+
if (end > start && bytes + partBytes > limit && !shouldAttachToPreviousGrapheme(data, end)) break;
|
|
9425
|
+
bytes += partBytes;
|
|
9426
|
+
end = next;
|
|
9427
|
+
while (end < data.length && shouldAttachToPreviousGrapheme(data, end)) {
|
|
9428
|
+
const attachedEnd = codePointEnd(data, end);
|
|
9429
|
+
bytes += Buffer.byteLength(data.slice(end, attachedEnd), "utf8");
|
|
9430
|
+
end = attachedEnd;
|
|
9431
|
+
}
|
|
9432
|
+
if (bytes >= limit && (end >= data.length || !shouldAttachToPreviousGrapheme(data, end))) break;
|
|
9433
|
+
}
|
|
9434
|
+
return end > start ? end : codePointEnd(data, start);
|
|
9435
|
+
};
|
|
8956
9436
|
/**
|
|
8957
9437
|
* Write data in chunks to avoid overwhelming terminal buffers.
|
|
8958
9438
|
* This is especially important for ghostty which can hang on large writes.
|
|
8959
|
-
*
|
|
8960
|
-
* CRITICAL: In ghostty, setTimeout callbacks don't execute, so we must use
|
|
8961
|
-
* synchronous chunked writes. We break large writes into smaller chunks to
|
|
8962
|
-
* reduce the chance of terminal hangs.
|
|
8963
9439
|
*/
|
|
8964
9440
|
function writeChunked(data) {
|
|
8965
|
-
if (
|
|
8966
|
-
|
|
8967
|
-
out.write(data);
|
|
8968
|
-
return;
|
|
8969
|
-
}
|
|
8970
|
-
for (let i = 0; i < data.length; i += chunkSize) {
|
|
8971
|
-
const chunk = data.slice(i, i + chunkSize);
|
|
8972
|
-
out.write(chunk);
|
|
8973
|
-
}
|
|
9441
|
+
if (Buffer.byteLength(data, "utf8") <= chunkSize) {
|
|
9442
|
+
out.write(data);
|
|
8974
9443
|
return;
|
|
8975
9444
|
}
|
|
8976
|
-
if (isDebugEnabled()) getDebugLog().render(`writeChunked: sync chunked write of ${data
|
|
9445
|
+
if (isDebugEnabled()) getDebugLog().render(`writeChunked: sync chunked write of ${Buffer.byteLength(data, "utf8")} bytes`);
|
|
8977
9446
|
try {
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
9447
|
+
for (let start = 0; start < data.length;) {
|
|
9448
|
+
const end = safeChunkEnd(data, start, chunkSize);
|
|
9449
|
+
out.write(data.slice(start, end));
|
|
9450
|
+
start = end;
|
|
8982
9451
|
}
|
|
8983
9452
|
if (isDebugEnabled()) getDebugLog().render(`writeChunked: chunked write completed`);
|
|
8984
9453
|
} catch (e) {
|
|
8985
9454
|
if (isDebugEnabled()) getDebugLog().error(`writeChunked: write error`, e);
|
|
9455
|
+
throw e;
|
|
8986
9456
|
}
|
|
8987
9457
|
}
|
|
8988
9458
|
/**
|
|
8989
9459
|
* Internal function that actually renders the frame to stdout.
|
|
8990
9460
|
* When dirtyRows is provided, only those rows are repainted.
|
|
8991
9461
|
*/
|
|
8992
|
-
function doRender(dirtyRows, scrollOperations) {
|
|
9462
|
+
function doRender(dirtyRows, scrollOperations, forceDirtyRowsRepaint = false) {
|
|
8993
9463
|
if (isDebugEnabled()) getDebugLog().render(`doRender() START: dirtyRows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) ?? "null"}`);
|
|
8994
9464
|
if (disposed) return;
|
|
9465
|
+
const currentFingerprintOwner = stdoutFingerprintOwners.get(terminal);
|
|
9466
|
+
if ((!ownsFingerprintFn || terminalFingerprintFnDirty) && (!currentFingerprintOwner || currentFingerprintOwner === fingerprintOwner) && canInstallTerminalFingerprintFn() && shouldAttemptFingerprintInstall()) installFingerprintFn(terminalFingerprintFnDirty);
|
|
8995
9467
|
cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutRenderStart();
|
|
8996
9468
|
pendingRender = false;
|
|
8997
9469
|
accumulatedAllRows = false;
|
|
@@ -8999,6 +9471,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
8999
9471
|
accumulatedDirtyCount = 0;
|
|
9000
9472
|
accumulatedDirtyMin = Number.POSITIVE_INFINITY;
|
|
9001
9473
|
accumulatedDirtyMax = -1;
|
|
9474
|
+
accumulatedDirtyRowsRequireRepaint = false;
|
|
9002
9475
|
accumulatedScrollOperations = null;
|
|
9003
9476
|
lastFrameTime = Date.now();
|
|
9004
9477
|
const renderStart = performance.now();
|
|
@@ -9008,10 +9481,15 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9008
9481
|
const bgSeq = openBg(defaultBg);
|
|
9009
9482
|
const bgOnlyStyle = { bg: defaultBg };
|
|
9010
9483
|
const bgKey = styleKeyFromParts({ bg: defaultBg });
|
|
9011
|
-
const blankFP =
|
|
9484
|
+
const blankFP = cellFingerprintFromStyleKey(" ", bgKey);
|
|
9012
9485
|
let dirtySorted = true;
|
|
9013
|
-
const forceFullRender = hrefIndexResetRequiresBaseline;
|
|
9014
|
-
if (forceFullRender)
|
|
9486
|
+
const forceFullRender = hrefIndexResetRequiresBaseline || fingerprintInternResetRequiresBaseline;
|
|
9487
|
+
if (forceFullRender) {
|
|
9488
|
+
hrefIndexResetRequiresBaseline = false;
|
|
9489
|
+
fingerprintInternResetRequiresBaseline = false;
|
|
9490
|
+
fpPrevValid = false;
|
|
9491
|
+
}
|
|
9492
|
+
activeRenderBaselineGeneration = fingerprintBaselineGeneration;
|
|
9015
9493
|
const normalizedScrollOperations = (() => {
|
|
9016
9494
|
if (!(scrollOperations === null || scrollOperations === void 0 ? void 0 : scrollOperations.length)) return null;
|
|
9017
9495
|
const outOps = [];
|
|
@@ -9030,8 +9508,9 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9030
9508
|
return outOps;
|
|
9031
9509
|
})();
|
|
9032
9510
|
let rowsToRender = (() => {
|
|
9033
|
-
if (forceFullRender) return null;
|
|
9034
|
-
if (!dirtyRows
|
|
9511
|
+
if (forceFullRender || !fpPrevValid) return null;
|
|
9512
|
+
if (!dirtyRows) return null;
|
|
9513
|
+
if (dirtyRows.length === 0) return [];
|
|
9035
9514
|
const outRows = [];
|
|
9036
9515
|
outRows.length = dirtyRows.length;
|
|
9037
9516
|
let outLen = 0;
|
|
@@ -9039,20 +9518,35 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9039
9518
|
let prev = -1;
|
|
9040
9519
|
for (let i = 0; i < dirtyRows.length; i++) {
|
|
9041
9520
|
const y = Math.floor(dirtyRows[i] ?? -1);
|
|
9042
|
-
if (y < 0 || y >= size.rows) continue;
|
|
9521
|
+
if (!Number.isFinite(y) || y < 0 || y >= size.rows) continue;
|
|
9043
9522
|
if (y <= prev) sorted = false;
|
|
9044
9523
|
prev = y;
|
|
9045
9524
|
outRows[outLen++] = y;
|
|
9046
9525
|
}
|
|
9047
9526
|
outRows.length = outLen;
|
|
9048
|
-
if (!outRows.length) return
|
|
9527
|
+
if (!outRows.length) return [];
|
|
9049
9528
|
dirtySorted = sorted;
|
|
9050
9529
|
if (!sorted) outRows.sort((a, b) => a - b);
|
|
9051
9530
|
return outRows;
|
|
9052
9531
|
})();
|
|
9532
|
+
if ((rowsToRender === null || rowsToRender === void 0 ? void 0 : rowsToRender.length) === 0 && !normalizedScrollOperations && !getImeAnchor) {
|
|
9533
|
+
if (isDebugEnabled()) getDebugLog().render(" No valid dirty rows; frame skipped");
|
|
9534
|
+
cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutNoOutput();
|
|
9535
|
+
return;
|
|
9536
|
+
}
|
|
9537
|
+
const dirtyRowSnapshots = fpPrevValid && rowsToRender ? new Map() : null;
|
|
9538
|
+
const snapshotDirtyRowFingerprints = () => {
|
|
9539
|
+
if (!dirtyRowSnapshots || !rowsToRender) return;
|
|
9540
|
+
for (const y of rowsToRender) {
|
|
9541
|
+
const row = terminal.getRow(y);
|
|
9542
|
+
dirtyRowSnapshots.set(y, snapshotRowFingerprints(row, y, size.cols));
|
|
9543
|
+
}
|
|
9544
|
+
};
|
|
9545
|
+
snapshotDirtyRowFingerprints();
|
|
9053
9546
|
if (fpPrevValid && rowsToRender) {
|
|
9054
9547
|
currentFP.set(prevFP);
|
|
9055
9548
|
currentHrefIds.set(prevHrefIds);
|
|
9549
|
+
currentTextIds.set(prevTextIds);
|
|
9056
9550
|
}
|
|
9057
9551
|
const frameParts = [];
|
|
9058
9552
|
frameParts.push(!isGhostty && useSyncOutput ? SYNC_START : "");
|
|
@@ -9072,6 +9566,14 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9072
9566
|
inverse: false,
|
|
9073
9567
|
href: null
|
|
9074
9568
|
};
|
|
9569
|
+
const closeActiveHrefBeforeCursorMove = () => {
|
|
9570
|
+
if (!enableOsc8Links || !activeStyle.href) return;
|
|
9571
|
+
frameParts.push(OSC8_CLOSE);
|
|
9572
|
+
activeStyle = {
|
|
9573
|
+
...activeStyle,
|
|
9574
|
+
href: null
|
|
9575
|
+
};
|
|
9576
|
+
};
|
|
9075
9577
|
const normalizeStyle$1 = (style) => {
|
|
9076
9578
|
return {
|
|
9077
9579
|
fg: style.fg ?? null,
|
|
@@ -9081,7 +9583,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9081
9583
|
italic: Boolean(style.italic),
|
|
9082
9584
|
underline: Boolean(style.underline),
|
|
9083
9585
|
inverse: Boolean(style.inverse),
|
|
9084
|
-
href:
|
|
9586
|
+
href: renderedHref(style.href)
|
|
9085
9587
|
};
|
|
9086
9588
|
};
|
|
9087
9589
|
const emitStyle = (nextStyle, nextKey) => {
|
|
@@ -9108,20 +9610,29 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9108
9610
|
activeStyleKey = nextKey;
|
|
9109
9611
|
activeStyle = next;
|
|
9110
9612
|
};
|
|
9111
|
-
const shouldEmitStyle = (style, key) => activeStyleKey !== key || activeStyle.href !==
|
|
9112
|
-
const renderRow = (y, row, startX = 0, endXExclusive =
|
|
9113
|
-
const spanStart = Math.max(0, Math.min(
|
|
9114
|
-
const spanEnd = Math.max(spanStart, Math.min(
|
|
9115
|
-
|
|
9613
|
+
const shouldEmitStyle = (style, key) => activeStyleKey !== key || activeStyle.href !== renderedHref(style.href);
|
|
9614
|
+
const renderRow = (y, row, startX = 0, endXExclusive = size.cols, clearToEol = true) => {
|
|
9615
|
+
const spanStart = Math.max(0, Math.min(size.cols, Math.floor(startX)));
|
|
9616
|
+
const spanEnd = Math.max(spanStart, Math.min(size.cols, Math.floor(endXExclusive)));
|
|
9617
|
+
const shouldClearToEol = clearToEol && spanEnd < size.cols;
|
|
9618
|
+
if (spanStart >= spanEnd && !shouldClearToEol) return;
|
|
9116
9619
|
hasFrameOutput = true;
|
|
9117
|
-
|
|
9118
|
-
|
|
9620
|
+
const pushCursorFixAfterCell = (parts, cellX, cell) => {
|
|
9621
|
+
const afterX = cellX + (cell.width ?? 1);
|
|
9622
|
+
if (afterX >= size.cols) return;
|
|
9623
|
+
parts.push(`\u001B[${y + 1};${afterX + 1}H`);
|
|
9624
|
+
};
|
|
9625
|
+
if (!disableCursorPos) {
|
|
9626
|
+
closeActiveHrefBeforeCursorMove();
|
|
9627
|
+
if (spanStart === 0 && lastRenderWasFullRow && y === lastRenderedY + 1) frameParts.push("\r\n");
|
|
9628
|
+
else frameParts.push(spanStart === 0 ? rowCursorToCol1[y] : `\u001B[${y + 1};${spanStart + 1}H`);
|
|
9629
|
+
}
|
|
9119
9630
|
let currentKey = null;
|
|
9120
9631
|
let currentStyle = null;
|
|
9121
9632
|
const currentTextParts = rowTextPartsScratch;
|
|
9122
9633
|
currentTextParts.length = 0;
|
|
9123
9634
|
for (let x = spanStart; x < spanEnd; x++) {
|
|
9124
|
-
const cell = row
|
|
9635
|
+
const cell = cellAt(row, x);
|
|
9125
9636
|
if (cell.continuation) continue;
|
|
9126
9637
|
const ch = cell.ch || " ";
|
|
9127
9638
|
const nextStyle = cell.style;
|
|
@@ -9130,17 +9641,12 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9130
9641
|
currentKey = key;
|
|
9131
9642
|
currentStyle = nextStyle;
|
|
9132
9643
|
currentTextParts.push(ch);
|
|
9133
|
-
if (needsCursorFix(cell, ch)) currentTextParts
|
|
9644
|
+
if (needsCursorFix(cell, ch)) pushCursorFixAfterCell(currentTextParts, x, cell);
|
|
9134
9645
|
continue;
|
|
9135
9646
|
}
|
|
9136
|
-
if (key === currentKey &&
|
|
9137
|
-
// 8-bit limit
|
|
9138
|
-
// saturate to avoid overflow
|
|
9139
|
-
// where this row's content came from in prev frame
|
|
9140
|
-
currentStyle === null || currentStyle === void 0 ? void 0 : currentStyle.href
|
|
9141
|
-
)) {
|
|
9647
|
+
if (key === currentKey && renderedHref(nextStyle.href) === renderedHref(currentStyle === null || currentStyle === void 0 ? void 0 : currentStyle.href)) {
|
|
9142
9648
|
currentTextParts.push(ch);
|
|
9143
|
-
if (needsCursorFix(cell, ch)) currentTextParts
|
|
9649
|
+
if (needsCursorFix(cell, ch)) pushCursorFixAfterCell(currentTextParts, x, cell);
|
|
9144
9650
|
continue;
|
|
9145
9651
|
}
|
|
9146
9652
|
if (shouldEmitStyle(currentStyle, currentKey)) emitStyle(currentStyle, currentKey);
|
|
@@ -9149,18 +9655,82 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9149
9655
|
currentStyle = nextStyle;
|
|
9150
9656
|
currentTextParts.length = 0;
|
|
9151
9657
|
currentTextParts.push(ch);
|
|
9152
|
-
if (needsCursorFix(cell, ch)) currentTextParts
|
|
9658
|
+
if (needsCursorFix(cell, ch)) pushCursorFixAfterCell(currentTextParts, x, cell);
|
|
9153
9659
|
}
|
|
9154
9660
|
if (currentKey != null) {
|
|
9155
9661
|
if (shouldEmitStyle(currentStyle, currentKey)) emitStyle(currentStyle, currentKey);
|
|
9156
9662
|
frameParts.push(currentTextParts.join(""));
|
|
9157
9663
|
}
|
|
9158
|
-
if (
|
|
9664
|
+
if (shouldClearToEol) {
|
|
9159
9665
|
if (activeStyleKey !== bgKey) emitStyle(bgOnlyStyle, bgKey);
|
|
9160
9666
|
frameParts.push("\x1B[K");
|
|
9161
9667
|
}
|
|
9162
9668
|
lastRenderedY = y;
|
|
9163
|
-
lastRenderWasFullRow = spanStart === 0 &&
|
|
9669
|
+
lastRenderWasFullRow = spanStart === 0 && (spanEnd >= size.cols || shouldClearToEol);
|
|
9670
|
+
};
|
|
9671
|
+
const lastRenderableColumnInCurrentRow = (rowY, cols, blankFingerprint) => {
|
|
9672
|
+
if (!fpCols || rowY < 0 || rowY >= fpRows) return -1;
|
|
9673
|
+
const base = rowY * fpCols;
|
|
9674
|
+
const limit = Math.min(cols, fpCols);
|
|
9675
|
+
for (let x = limit - 1; x >= 0; x--) if (currentFP[base + x] !== blankFingerprint || currentTextIds[base + x] !== blankTextId || currentHrefIds[base + x] !== 0) return x;
|
|
9676
|
+
return -1;
|
|
9677
|
+
};
|
|
9678
|
+
const resolveTailClearStartX = (rowY, cols, shouldRewriteTail, blankFingerprint) => {
|
|
9679
|
+
if (!shouldRewriteTail) return null;
|
|
9680
|
+
const lastRenderableX = lastRenderableColumnInCurrentRow(rowY, cols, blankFingerprint);
|
|
9681
|
+
const clearStartX = Math.max(0, Math.min(cols, lastRenderableX + 1));
|
|
9682
|
+
return clearStartX < cols ? clearStartX : null;
|
|
9683
|
+
};
|
|
9684
|
+
const renderDirtySpans = (rowY, row, rawSpans, rewriteTail) => {
|
|
9685
|
+
if (!rawSpans.length && !rewriteTail) return;
|
|
9686
|
+
const rowCols = size.cols;
|
|
9687
|
+
const clearStartX = resolveTailClearStartX(rowY, rowCols, rewriteTail, blankFP);
|
|
9688
|
+
const paintTailInsteadOfEscK = clearStartX != null && dirtyRowPatchMode === "auto" && useConservativeDirtyRows;
|
|
9689
|
+
const spansToPaint = [];
|
|
9690
|
+
for (const span of rawSpans) {
|
|
9691
|
+
const startX = Math.max(0, Math.min(rowCols, span.startX));
|
|
9692
|
+
const endXExclusive = Math.max(startX, Math.min(rowCols, span.endXExclusive));
|
|
9693
|
+
if (endXExclusive <= startX) continue;
|
|
9694
|
+
if (clearStartX != null && !paintTailInsteadOfEscK) {
|
|
9695
|
+
if (startX >= clearStartX) continue;
|
|
9696
|
+
const clippedEnd = Math.min(endXExclusive, clearStartX);
|
|
9697
|
+
if (clippedEnd > startX) spansToPaint.push({
|
|
9698
|
+
startX,
|
|
9699
|
+
endXExclusive: clippedEnd
|
|
9700
|
+
});
|
|
9701
|
+
continue;
|
|
9702
|
+
}
|
|
9703
|
+
spansToPaint.push({
|
|
9704
|
+
startX,
|
|
9705
|
+
endXExclusive
|
|
9706
|
+
});
|
|
9707
|
+
}
|
|
9708
|
+
if (paintTailInsteadOfEscK && clearStartX != null && clearStartX < rowCols) {
|
|
9709
|
+
const prev = spansToPaint[spansToPaint.length - 1];
|
|
9710
|
+
if (prev && clearStartX <= prev.endXExclusive + DIRTY_SPAN_MERGE_GAP_CELLS) spansToPaint[spansToPaint.length - 1] = {
|
|
9711
|
+
startX: prev.startX,
|
|
9712
|
+
endXExclusive: rowCols
|
|
9713
|
+
};
|
|
9714
|
+
else spansToPaint.push({
|
|
9715
|
+
startX: clearStartX,
|
|
9716
|
+
endXExclusive: rowCols
|
|
9717
|
+
});
|
|
9718
|
+
}
|
|
9719
|
+
const clearToEolForCost = clearStartX != null && clearStartX < rowCols && !paintTailInsteadOfEscK;
|
|
9720
|
+
const renderMode = !shouldUseMultiDirtySpans() && spansToPaint.length > 0 ? "contiguous" : resolveDirtySpanRenderMode(row, spansToPaint, rowY, rowCols, clearToEolForCost);
|
|
9721
|
+
if (renderMode === "row") {
|
|
9722
|
+
if (clearStartX != null && !paintTailInsteadOfEscK) renderRow(rowY, row, 0, clearStartX, true);
|
|
9723
|
+
else renderRow(rowY, row, 0, rowCols, false);
|
|
9724
|
+
return;
|
|
9725
|
+
} else if (renderMode === "contiguous") {
|
|
9726
|
+
const first = spansToPaint[0];
|
|
9727
|
+
const last = spansToPaint[spansToPaint.length - 1];
|
|
9728
|
+
if (first && last) {
|
|
9729
|
+
const rewriteEnd = clearStartX == null || paintTailInsteadOfEscK ? last.endXExclusive : Math.min(last.endXExclusive, clearStartX);
|
|
9730
|
+
if (rewriteEnd > first.startX) renderRow(rowY, row, first.startX, rewriteEnd, false);
|
|
9731
|
+
}
|
|
9732
|
+
} else for (const span of spansToPaint) renderRow(rowY, row, span.startX, span.endXExclusive, false);
|
|
9733
|
+
if (clearStartX != null && !paintTailInsteadOfEscK) renderRow(rowY, row, clearStartX, clearStartX, true);
|
|
9164
9734
|
};
|
|
9165
9735
|
const overlayCoverage = overlayPlaneCoverageRows(size.rows);
|
|
9166
9736
|
const overlayRows = Array.from(new Set([...overlayCoverage.blockedRows, ...prevOverlayBlockedRows])).sort((a, b) => a - b);
|
|
@@ -9182,9 +9752,9 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9182
9752
|
if (preparedExplicit.trimmed && isDebugEnabled()) getDebugLog().render(` Explicit scroll ops clipped around overlay rows: ${normalizedScrollOperations.map((op, index) => `${op.startY}-${op.endY - 1}:${op.delta}->${explicitScrollOperations[index].startY}-${explicitScrollOperations[index].endY - 1}:${explicitScrollOperations[index].delta}`).join(", ")}`);
|
|
9183
9753
|
} else {
|
|
9184
9754
|
allowInferredScrollRegions = false;
|
|
9185
|
-
const expandedRows = new Set(rowsToRender ?? []);
|
|
9186
|
-
for (const op of explicitScrollOperations) for (let y = op.startY; y < op.endY; y++) expandedRows.add(y);
|
|
9187
|
-
rowsToRender = Array.from(expandedRows).sort((a, b) => a - b);
|
|
9755
|
+
const expandedRows$1 = new Set(rowsToRender ?? []);
|
|
9756
|
+
for (const op of explicitScrollOperations) for (let y = op.startY; y < op.endY; y++) expandedRows$1.add(y);
|
|
9757
|
+
rowsToRender = Array.from(expandedRows$1).sort((a, b) => a - b);
|
|
9188
9758
|
dirtySorted = true;
|
|
9189
9759
|
explicitScrollOperations = null;
|
|
9190
9760
|
hiddenExplicitDirtyRows = null;
|
|
@@ -9192,27 +9762,32 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9192
9762
|
}
|
|
9193
9763
|
}
|
|
9194
9764
|
const scrollRowsCandidate = rowsToRender;
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9765
|
+
const expandedRows = new Set(rowsToRender ?? []);
|
|
9766
|
+
const includeRowsForPendingScrollOperations = (operations) => {
|
|
9767
|
+
if (!(operations === null || operations === void 0 ? void 0 : operations.length)) return;
|
|
9768
|
+
for (const op of operations) for (let y = op.startY; y < op.endY; y++) {
|
|
9198
9769
|
if (hiddenExplicitDirtyRows === null || hiddenExplicitDirtyRows === void 0 ? void 0 : hiddenExplicitDirtyRows.has(y)) continue;
|
|
9199
9770
|
expandedRows.add(y);
|
|
9200
9771
|
}
|
|
9772
|
+
};
|
|
9773
|
+
if (explicitScrollOperations && rowsToRender && (!enableScrollRegions || !fpPrevValid)) {
|
|
9774
|
+
includeRowsForPendingScrollOperations(explicitScrollOperations);
|
|
9201
9775
|
rowsToRender = Array.from(expandedRows).sort((a, b) => a - b);
|
|
9202
9776
|
dirtySorted = true;
|
|
9203
9777
|
explicitScrollOperations = null;
|
|
9204
9778
|
hiddenExplicitDirtyRows = null;
|
|
9205
9779
|
if (isDebugEnabled()) getDebugLog().render(enableScrollRegions ? " Explicit scroll ops fell back to region repaint because previous fingerprints are unavailable" : " Explicit scroll ops fell back to region repaint because scroll regions are disabled");
|
|
9206
9780
|
}
|
|
9207
|
-
const denseDirtyRows = Boolean(rowsToRender && rowsToRender.length >= size.rows * dirtyFullThreshold);
|
|
9781
|
+
const denseDirtyRows = Boolean(rowsToRender && size.rows > 1 && rowsToRender.length >= size.rows * dirtyFullThreshold);
|
|
9208
9782
|
if (denseDirtyRows) rowsToRender = null;
|
|
9209
9783
|
let profiledRowCount = rowsToRender ? rowsToRender.length : size.rows;
|
|
9210
|
-
const useScrollRegions = enableScrollRegions && fpPrevValid && scrollRowsCandidate && scrollRowsCandidate.length >= 3 && allowInferredScrollRegions;
|
|
9784
|
+
const useScrollRegions = enableScrollRegions && fpPrevValid && scrollRowsCandidate && scrollRowsCandidate.length >= 3 && !forceDirtyRowsRepaint && allowInferredScrollRegions;
|
|
9211
9785
|
let scrollHandled = false;
|
|
9212
9786
|
if (enableScrollRegions && explicitScrollOperations && scrollRowsCandidate && fpPrevValid) {
|
|
9213
9787
|
if (currentFP.length === prevFP.length) {
|
|
9214
9788
|
currentFP.set(prevFP);
|
|
9215
9789
|
currentHrefIds.set(prevHrefIds);
|
|
9790
|
+
currentTextIds.set(prevTextIds);
|
|
9216
9791
|
}
|
|
9217
9792
|
const explicitRowsToRender = new Set();
|
|
9218
9793
|
for (const y of scrollRowsCandidate) {
|
|
@@ -9232,6 +9807,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9232
9807
|
for (let x = 0; x < size.cols; x++) {
|
|
9233
9808
|
currentFP[dstBase + x] = prevFP[srcBase + x];
|
|
9234
9809
|
currentHrefIds[dstBase + x] = prevHrefIds[srcBase + x];
|
|
9810
|
+
currentTextIds[dstBase + x] = prevTextIds[srcBase + x];
|
|
9235
9811
|
}
|
|
9236
9812
|
}
|
|
9237
9813
|
for (let y = op.endY - op.delta; y < op.endY; y++) {
|
|
@@ -9240,6 +9816,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9240
9816
|
for (let x = 0; x < size.cols; x++) {
|
|
9241
9817
|
currentFP[base + x] = blankFP;
|
|
9242
9818
|
currentHrefIds[base + x] = 0;
|
|
9819
|
+
currentTextIds[base + x] = blankTextId;
|
|
9243
9820
|
}
|
|
9244
9821
|
}
|
|
9245
9822
|
} else {
|
|
@@ -9252,6 +9829,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9252
9829
|
for (let x = 0; x < size.cols; x++) {
|
|
9253
9830
|
currentFP[dstBase + x] = prevFP[srcBase + x];
|
|
9254
9831
|
currentHrefIds[dstBase + x] = prevHrefIds[srcBase + x];
|
|
9832
|
+
currentTextIds[dstBase + x] = prevTextIds[srcBase + x];
|
|
9255
9833
|
}
|
|
9256
9834
|
}
|
|
9257
9835
|
for (let y = op.startY; y < op.startY + absDelta; y++) {
|
|
@@ -9260,6 +9838,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9260
9838
|
for (let x = 0; x < size.cols; x++) {
|
|
9261
9839
|
currentFP[base + x] = blankFP;
|
|
9262
9840
|
currentHrefIds[base + x] = 0;
|
|
9841
|
+
currentTextIds[base + x] = blankTextId;
|
|
9263
9842
|
}
|
|
9264
9843
|
}
|
|
9265
9844
|
}
|
|
@@ -9268,10 +9847,28 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9268
9847
|
const explicitDirtyRows = Array.from(explicitRowsToRender).sort((a, b) => a - b);
|
|
9269
9848
|
const overlayDirtyRows = overlayTouchedRowSet ? explicitDirtyRows.filter((y) => overlayTouchedRowSet.has(y)) : [];
|
|
9270
9849
|
const paintedExplicitRows = [];
|
|
9850
|
+
const insertedRows = new Set();
|
|
9851
|
+
for (const op of explicitScrollOperations) {
|
|
9852
|
+
const absDelta = Math.abs(op.delta);
|
|
9853
|
+
if (op.delta > 0) for (let y = op.endY - absDelta; y < op.endY; y++) insertedRows.add(y);
|
|
9854
|
+
else for (let y = op.startY; y < op.startY + absDelta; y++) insertedRows.add(y);
|
|
9855
|
+
}
|
|
9271
9856
|
for (const y of explicitDirtyRows) {
|
|
9272
9857
|
const row = terminal.getRow(y);
|
|
9273
|
-
|
|
9274
|
-
|
|
9858
|
+
const overlayRow = overlayTouchedRowSet === null || overlayTouchedRowSet === void 0 ? void 0 : overlayTouchedRowSet.has(y);
|
|
9859
|
+
if (forceDirtyRowsRepaint || !shouldUseDirtySpans() || overlayRow) {
|
|
9860
|
+
fingerprintRow(row, y, size.cols);
|
|
9861
|
+
renderRow(y, row);
|
|
9862
|
+
paintedExplicitRows.push(y);
|
|
9863
|
+
continue;
|
|
9864
|
+
}
|
|
9865
|
+
const insertedRow = insertedRows.has(y);
|
|
9866
|
+
if (insertedRow) fingerprintRow(row, y, size.cols);
|
|
9867
|
+
const spans = insertedRow ? resolveChangedSpansAgainstFill(row, y, size.cols, blankFP, blankTextId, 0) : resolveChangedSpansAgainstReference(row, y, size.cols, currentFP, currentHrefIds, currentTextIds);
|
|
9868
|
+
if (!spans.length) continue;
|
|
9869
|
+
const rewriteTail = !insertedRow && shouldRewriteRowTail(row, y, size.cols, currentFP, currentHrefIds, currentTextIds, bgKey);
|
|
9870
|
+
if (!insertedRow) fingerprintRow(row, y, size.cols);
|
|
9871
|
+
renderDirtySpans(y, row, spans, rewriteTail);
|
|
9275
9872
|
paintedExplicitRows.push(y);
|
|
9276
9873
|
}
|
|
9277
9874
|
if (isDebugEnabled() && overlayDirtyRows.length) getDebugLog().render(` Explicit scroll ops overlay rows: [${overlayDirtyRows.join(", ")}]`);
|
|
@@ -9284,6 +9881,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9284
9881
|
if (currentFP.length === prevFP.length) {
|
|
9285
9882
|
currentFP.set(prevFP);
|
|
9286
9883
|
currentHrefIds.set(prevHrefIds);
|
|
9884
|
+
currentTextIds.set(prevTextIds);
|
|
9287
9885
|
}
|
|
9288
9886
|
hasFrameOutput = true;
|
|
9289
9887
|
if (isDebugEnabled()) getDebugLog().render(` Scroll region: delta=${shift.delta}, region=${shift.regionStart}-${shift.regionEnd - 1}, new rows=${shift.newRowStart}-${shift.newRowEnd - 1}`);
|
|
@@ -9303,6 +9901,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9303
9901
|
for (let x = 0; x < size.cols; x++) {
|
|
9304
9902
|
currentFP[dstBase + x] = prevFP[srcBase + x];
|
|
9305
9903
|
currentHrefIds[dstBase + x] = prevHrefIds[srcBase + x];
|
|
9904
|
+
currentTextIds[dstBase + x] = prevTextIds[srcBase + x];
|
|
9306
9905
|
}
|
|
9307
9906
|
}
|
|
9308
9907
|
for (let y = shift.regionEnd - shift.delta; y < shift.regionEnd; y++) {
|
|
@@ -9310,6 +9909,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9310
9909
|
for (let x = 0; x < size.cols; x++) {
|
|
9311
9910
|
currentFP[base + x] = blankFP;
|
|
9312
9911
|
currentHrefIds[base + x] = 0;
|
|
9912
|
+
currentTextIds[base + x] = blankTextId;
|
|
9313
9913
|
}
|
|
9314
9914
|
}
|
|
9315
9915
|
} else {
|
|
@@ -9320,6 +9920,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9320
9920
|
for (let x = 0; x < size.cols; x++) {
|
|
9321
9921
|
currentFP[dstBase + x] = prevFP[srcBase + x];
|
|
9322
9922
|
currentHrefIds[dstBase + x] = prevHrefIds[srcBase + x];
|
|
9923
|
+
currentTextIds[dstBase + x] = prevTextIds[srcBase + x];
|
|
9323
9924
|
}
|
|
9324
9925
|
}
|
|
9325
9926
|
for (let y = shift.regionStart; y < shift.regionStart + absDelta; y++) {
|
|
@@ -9327,6 +9928,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9327
9928
|
for (let x = 0; x < size.cols; x++) {
|
|
9328
9929
|
currentFP[base + x] = blankFP;
|
|
9329
9930
|
currentHrefIds[base + x] = 0;
|
|
9931
|
+
currentTextIds[base + x] = blankTextId;
|
|
9330
9932
|
}
|
|
9331
9933
|
}
|
|
9332
9934
|
}
|
|
@@ -9338,6 +9940,17 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9338
9940
|
const extraDirtyRows = overlayRowSet ? shift.extraDirtyRows.filter((y) => !overlayRowSet.has(y) || Boolean(overlayPartialRowSet === null || overlayPartialRowSet === void 0 ? void 0 : overlayPartialRowSet.has(y))) : shift.extraDirtyRows;
|
|
9339
9941
|
for (const y of extraDirtyRows) {
|
|
9340
9942
|
const row = terminal.getRow(y);
|
|
9943
|
+
if (shouldUseDirtySpans()) {
|
|
9944
|
+
const spans = resolveChangedSpansAgainstReference(row, y, size.cols, currentFP, currentHrefIds, currentTextIds);
|
|
9945
|
+
if (!spans.length) {
|
|
9946
|
+
fingerprintRow(row, y, size.cols);
|
|
9947
|
+
continue;
|
|
9948
|
+
}
|
|
9949
|
+
const rewriteTail = shouldRewriteRowTail(row, y, size.cols, currentFP, currentHrefIds, currentTextIds, bgKey);
|
|
9950
|
+
fingerprintRow(row, y, size.cols);
|
|
9951
|
+
renderDirtySpans(y, row, spans, rewriteTail);
|
|
9952
|
+
continue;
|
|
9953
|
+
}
|
|
9341
9954
|
fingerprintRow(row, y, size.cols);
|
|
9342
9955
|
renderRow(y, row);
|
|
9343
9956
|
}
|
|
@@ -9363,11 +9976,18 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9363
9976
|
}
|
|
9364
9977
|
} else {
|
|
9365
9978
|
const overlayDirtyRows = overlayTouchedRowSet ? rowsToRender.filter((y) => overlayTouchedRowSet.has(y)) : [];
|
|
9366
|
-
if (isDebugEnabled()) getDebugLog().render(` Partial render: ${rowsToRender.length} dirty rows: [${rowsToRender.join(",
|
|
9979
|
+
if (isDebugEnabled()) getDebugLog().render(` Partial render: ${rowsToRender.length} dirty rows: [${rowsToRender.join(",")}]${overlayDirtyRows.length ? ` (overlay-full-row: [${overlayDirtyRows.join(", ")}])` : ""}`);
|
|
9367
9980
|
for (const y of rowsToRender) {
|
|
9368
9981
|
const row = terminal.getRow(y);
|
|
9369
9982
|
fingerprintRow(row, y, size.cols);
|
|
9370
|
-
|
|
9983
|
+
if (forceDirtyRowsRepaint || !shouldUseDirtySpans()) {
|
|
9984
|
+
renderRow(y, row);
|
|
9985
|
+
continue;
|
|
9986
|
+
}
|
|
9987
|
+
const spans = resolveChangedSpans(row, y, size.cols, (dirtyRowSnapshots === null || dirtyRowSnapshots === void 0 ? void 0 : dirtyRowSnapshots.get(y)) ?? null);
|
|
9988
|
+
if (!spans.length) continue;
|
|
9989
|
+
const rewriteTail = shouldRewriteRowTail(row, y, size.cols, prevFP, prevHrefIds, prevTextIds, bgKey);
|
|
9990
|
+
renderDirtySpans(y, row, spans, rewriteTail);
|
|
9371
9991
|
}
|
|
9372
9992
|
}
|
|
9373
9993
|
if (!rowsToRender && lastRenderedRows > size.rows) {
|
|
@@ -9381,16 +10001,22 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9381
10001
|
frameParts.push(rowClearToEol[size.rows + i]);
|
|
9382
10002
|
}
|
|
9383
10003
|
}
|
|
9384
|
-
|
|
9385
|
-
const
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
10004
|
+
const nextLastRenderedRows = !rowsToRender ? size.rows : lastRenderedRows;
|
|
10005
|
+
const commitRenderBaseline = () => {
|
|
10006
|
+
lastRenderedRows = nextLastRenderedRows;
|
|
10007
|
+
const tmpFP = prevFP;
|
|
10008
|
+
prevFP = currentFP;
|
|
10009
|
+
currentFP = tmpFP;
|
|
10010
|
+
const tmpHrefIds = prevHrefIds;
|
|
10011
|
+
prevHrefIds = currentHrefIds;
|
|
10012
|
+
currentHrefIds = tmpHrefIds;
|
|
10013
|
+
const tmpTextIds = prevTextIds;
|
|
10014
|
+
prevTextIds = currentTextIds;
|
|
10015
|
+
currentTextIds = tmpTextIds;
|
|
10016
|
+
fpPrevValid = !hrefIndexResetRequiresBaseline && !fingerprintInternResetRequiresBaseline;
|
|
10017
|
+
prevOverlayBlockedRows = overlayCoverage.blockedRows;
|
|
10018
|
+
prevOverlayPartialRows = overlayCoverage.partialRows;
|
|
10019
|
+
};
|
|
9394
10020
|
let emittedCursorPos = null;
|
|
9395
10021
|
if (getImeAnchor) {
|
|
9396
10022
|
const anchor = getImeAnchor();
|
|
@@ -9402,11 +10028,13 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9402
10028
|
x,
|
|
9403
10029
|
y
|
|
9404
10030
|
};
|
|
10031
|
+
closeActiveHrefBeforeCursorMove();
|
|
9405
10032
|
frameParts.push(`\u001B[${y + 1};${x + 1}H`);
|
|
9406
10033
|
}
|
|
9407
10034
|
}
|
|
9408
10035
|
}
|
|
9409
10036
|
if (!hasFrameOutput) {
|
|
10037
|
+
commitRenderBaseline();
|
|
9410
10038
|
if (isDebugEnabled()) getDebugLog().render(" No-op frame skipped");
|
|
9411
10039
|
cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutNoOutput();
|
|
9412
10040
|
return;
|
|
@@ -9416,6 +10044,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9416
10044
|
frameParts.push("\x1B[?7h");
|
|
9417
10045
|
frameParts.push(!isGhostty && useSyncOutput ? SYNC_END : "");
|
|
9418
10046
|
const frame = frameParts.join("");
|
|
10047
|
+
const frameBytes = Buffer.byteLength(frame, "utf8");
|
|
9419
10048
|
if (profiler) profiler.recordRender({
|
|
9420
10049
|
durationMs: profiler.now() - renderStart,
|
|
9421
10050
|
rows: profiledRowCount,
|
|
@@ -9469,7 +10098,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9469
10098
|
}
|
|
9470
10099
|
return count;
|
|
9471
10100
|
};
|
|
9472
|
-
const frameSize =
|
|
10101
|
+
const frameSize = frameBytes;
|
|
9473
10102
|
const cursorSeqCount = countCursorMoves(frame);
|
|
9474
10103
|
const resetCount = countResets(frame);
|
|
9475
10104
|
const buildTime = (performance.now() - renderStart).toFixed(2);
|
|
@@ -9479,22 +10108,21 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9479
10108
|
const writeStart = performance.now();
|
|
9480
10109
|
const resolveWriteMode = (frameSizeBytes) => {
|
|
9481
10110
|
if (isGhostty) return "chunked";
|
|
9482
|
-
const canWriteSync = out.fd === 1;
|
|
9483
10111
|
const preferChunked = frameSizeBytes >= chunkThresholdBytes || writeEmaMs >= 24;
|
|
9484
10112
|
if (preferChunked) return "chunked";
|
|
9485
|
-
if (
|
|
10113
|
+
if (canUseSyncStdout && frameSizeBytes <= syncMaxBytes) return "sync";
|
|
9486
10114
|
return "stream";
|
|
9487
10115
|
};
|
|
9488
|
-
const writeMode = resolveWriteMode(
|
|
10116
|
+
const writeMode = resolveWriteMode(frameBytes);
|
|
9489
10117
|
if (isDebugEnabled()) getDebugLog().render(`Before write()`);
|
|
9490
10118
|
try {
|
|
9491
10119
|
if (writeMode === "chunked") {
|
|
9492
10120
|
if (isDebugEnabled()) getDebugLog().render(` Using chunked write (chunkSize=${chunkSize}, threshold=${chunkThresholdBytes}, emaMs=${writeEmaMs.toFixed(2)})`);
|
|
9493
10121
|
writeChunked(frame);
|
|
9494
|
-
} else if (writeMode === "sync" &&
|
|
10122
|
+
} else if (writeMode === "sync" && canUseSyncStdout) {
|
|
9495
10123
|
if (isDebugEnabled()) getDebugLog().render(` Using writeSync`);
|
|
9496
10124
|
try {
|
|
9497
|
-
writeFdSync(
|
|
10125
|
+
writeFdSync(outputFd, frame);
|
|
9498
10126
|
} catch {
|
|
9499
10127
|
if (isDebugEnabled()) getDebugLog().render(` writeSync failed, falling back to stream write`);
|
|
9500
10128
|
out.write(frame);
|
|
@@ -9509,11 +10137,12 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9509
10137
|
}
|
|
9510
10138
|
} catch (writeError) {
|
|
9511
10139
|
if (isDebugEnabled()) getDebugLog().error(`Write ERROR:`, writeError);
|
|
9512
|
-
|
|
9513
|
-
out.write(`\u001B[?7h${SYNC_END}`);
|
|
10140
|
+
try {
|
|
10141
|
+
out.write(`\u001B[?7h${!isGhostty && useSyncOutput ? SYNC_END : ""}`);
|
|
9514
10142
|
} catch {}
|
|
9515
10143
|
throw writeError;
|
|
9516
10144
|
}
|
|
10145
|
+
commitRenderBaseline();
|
|
9517
10146
|
{
|
|
9518
10147
|
const writeMs = performance.now() - writeStart;
|
|
9519
10148
|
writeEmaMs = writeEmaMs === 0 ? writeMs : writeEmaMs * .85 + writeMs * .15;
|
|
@@ -9526,19 +10155,19 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9526
10155
|
}
|
|
9527
10156
|
recordStdoutFrame({
|
|
9528
10157
|
at: Date.now(),
|
|
9529
|
-
bytes:
|
|
10158
|
+
bytes: frameBytes,
|
|
9530
10159
|
writeMs,
|
|
9531
10160
|
writeEmaMs,
|
|
9532
10161
|
writeMode: writeMode === "sync" ? "sync" : writeMode === "chunked" ? "chunked" : "stream"
|
|
9533
10162
|
});
|
|
9534
10163
|
if (profiler) profiler.recordWrite({
|
|
9535
10164
|
durationMs: writeMs,
|
|
9536
|
-
bytes:
|
|
10165
|
+
bytes: frameBytes,
|
|
9537
10166
|
mode: writeMode === "sync" ? "sync" : writeMode === "chunked" ? "chunked" : "stream"
|
|
9538
10167
|
});
|
|
9539
10168
|
cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutWrite({
|
|
9540
10169
|
durationMs: writeMs,
|
|
9541
|
-
bytes:
|
|
10170
|
+
bytes: frameBytes,
|
|
9542
10171
|
mode: writeMode === "sync" ? "sync" : writeMode === "chunked" ? "chunked" : "stream"
|
|
9543
10172
|
});
|
|
9544
10173
|
}
|
|
@@ -9644,6 +10273,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9644
10273
|
}
|
|
9645
10274
|
function markScrollOperationDirty(op, rowCount) {
|
|
9646
10275
|
markAccumulatedDirtyRange(op.startY, op.endY, rowCount);
|
|
10276
|
+
accumulatedDirtyRowsRequireRepaint = true;
|
|
9647
10277
|
}
|
|
9648
10278
|
function mergeScrollOperations(next) {
|
|
9649
10279
|
if (!(next === null || next === void 0 ? void 0 : next.length) || accumulatedAllRows) return;
|
|
@@ -9686,20 +10316,27 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9686
10316
|
function render(dirtyRows, sync, scrollOperations) {
|
|
9687
10317
|
if (disposed) return;
|
|
9688
10318
|
if (isDebugEnabled()) getDebugLog().render(`render() called: dirtyRows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) ?? "null"}, pending=${pendingRender}, elapsed=${Date.now() - lastFrameTime}ms`);
|
|
9689
|
-
if (
|
|
10319
|
+
if (dirtyRows == null) {
|
|
9690
10320
|
accumulatedAllRows = true;
|
|
9691
10321
|
accumulatedDirtyBits = null;
|
|
9692
10322
|
accumulatedDirtyCount = 0;
|
|
9693
10323
|
accumulatedDirtyMin = Number.POSITIVE_INFINITY;
|
|
9694
10324
|
accumulatedDirtyMax = -1;
|
|
10325
|
+
accumulatedDirtyRowsRequireRepaint = false;
|
|
9695
10326
|
accumulatedScrollOperations = null;
|
|
10327
|
+
} else if (dirtyRows.length === 0) {
|
|
10328
|
+
if (scrollOperations === null || scrollOperations === void 0 ? void 0 : scrollOperations.length) mergeScrollOperations(scrollOperations);
|
|
10329
|
+
else if (!accumulatedAllRows && accumulatedDirtyCount === 0 && !getImeAnchor) {
|
|
10330
|
+
cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutQueued(0);
|
|
10331
|
+
return;
|
|
10332
|
+
}
|
|
9696
10333
|
} else if (!accumulatedAllRows) {
|
|
9697
10334
|
const rowCount = terminal.size().rows;
|
|
9698
10335
|
mergeScrollOperations(scrollOperations);
|
|
9699
10336
|
const bits = ensureAccumulatedDirtyBits(rowCount);
|
|
9700
10337
|
for (let i = 0; i < dirtyRows.length; i++) {
|
|
9701
10338
|
const y = Math.floor(dirtyRows[i] ?? -1);
|
|
9702
|
-
if (y < 0 || y >= rowCount) continue;
|
|
10339
|
+
if (!Number.isFinite(y) || y < 0 || y >= rowCount) continue;
|
|
9703
10340
|
if (!bits[y]) {
|
|
9704
10341
|
bits[y] = 1;
|
|
9705
10342
|
accumulatedDirtyCount++;
|
|
@@ -9725,15 +10362,17 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9725
10362
|
}
|
|
9726
10363
|
const rows = buildAccumulatedRows(dirtyRows);
|
|
9727
10364
|
const pendingScrolls = buildAccumulatedScrollOperations();
|
|
10365
|
+
const forceDirtyRowsRepaint = accumulatedDirtyRowsRequireRepaint;
|
|
9728
10366
|
cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutQueued(0);
|
|
9729
|
-
doRender(rows, pendingScrolls);
|
|
10367
|
+
doRender(rows, pendingScrolls, forceDirtyRowsRepaint);
|
|
9730
10368
|
return;
|
|
9731
10369
|
}
|
|
9732
10370
|
if (elapsed >= MIN_FRAME_MS) {
|
|
9733
10371
|
const rows = buildAccumulatedRows(dirtyRows);
|
|
9734
10372
|
const pendingScrolls = buildAccumulatedScrollOperations();
|
|
10373
|
+
const forceDirtyRowsRepaint = accumulatedDirtyRowsRequireRepaint;
|
|
9735
10374
|
cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutQueued(0);
|
|
9736
|
-
doRender(rows, pendingScrolls);
|
|
10375
|
+
doRender(rows, pendingScrolls, forceDirtyRowsRepaint);
|
|
9737
10376
|
} else {
|
|
9738
10377
|
pendingRender = true;
|
|
9739
10378
|
if (renderTimer) clearTimeout(renderTimer);
|
|
@@ -9742,19 +10381,62 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9742
10381
|
if (!disposed) {
|
|
9743
10382
|
const rows = buildAccumulatedRows(dirtyRows);
|
|
9744
10383
|
const pendingScrolls = buildAccumulatedScrollOperations();
|
|
9745
|
-
|
|
10384
|
+
const forceDirtyRowsRepaint = accumulatedDirtyRowsRequireRepaint;
|
|
10385
|
+
doRender(rows, pendingScrolls, forceDirtyRowsRepaint);
|
|
9746
10386
|
}
|
|
9747
10387
|
}, queuedDelayMs);
|
|
9748
10388
|
cliLatency === null || cliLatency === void 0 || cliLatency.recordStdoutQueued(queuedDelayMs);
|
|
9749
10389
|
}
|
|
9750
10390
|
}
|
|
9751
|
-
function installFingerprintFn() {
|
|
9752
|
-
|
|
10391
|
+
function installFingerprintFn(force = false) {
|
|
10392
|
+
if (!canInstallTerminalFingerprintFn()) {
|
|
10393
|
+
if (ownsFingerprintFn && stdoutFingerprintOwners.get(terminal) === fingerprintOwner) {
|
|
10394
|
+
stdoutFingerprintOwners.delete(terminal);
|
|
10395
|
+
invalidateFingerprintBaseline();
|
|
10396
|
+
}
|
|
10397
|
+
ownsFingerprintFn = false;
|
|
10398
|
+
terminalFingerprintFnDirty = true;
|
|
10399
|
+
fingerprintInstallBlockedByExternalProvider = false;
|
|
10400
|
+
return;
|
|
10401
|
+
}
|
|
10402
|
+
const currentOwner = stdoutFingerprintOwners.get(terminal);
|
|
10403
|
+
if (!currentOwner && !ownsFingerprintFn && hasActiveExternalFingerprintProvider()) {
|
|
10404
|
+
fingerprintInstallBlockedByExternalProvider = true;
|
|
10405
|
+
ownsFingerprintFn = false;
|
|
10406
|
+
terminalFingerprintFnDirty = false;
|
|
10407
|
+
invalidateFingerprintBaseline();
|
|
10408
|
+
return;
|
|
10409
|
+
}
|
|
10410
|
+
fingerprintInstallBlockedByExternalProvider = false;
|
|
10411
|
+
if (currentOwner && currentOwner !== fingerprintOwner) {
|
|
10412
|
+
if (ownsFingerprintFn) invalidateFingerprintBaseline();
|
|
10413
|
+
ownsFingerprintFn = false;
|
|
10414
|
+
return;
|
|
10415
|
+
}
|
|
10416
|
+
if (ownsFingerprintFn && currentOwner === fingerprintOwner && !force && !terminalFingerprintFnDirty) return;
|
|
10417
|
+
stdoutFingerprintOwners.set(terminal, fingerprintOwner);
|
|
10418
|
+
ownsFingerprintFn = true;
|
|
10419
|
+
fingerprintTerminal.setFingerprintFn((ch, style) => {
|
|
9753
10420
|
return cellFingerprint(ch, style);
|
|
9754
10421
|
});
|
|
10422
|
+
terminalFingerprintFnDirty = false;
|
|
10423
|
+
invalidateFingerprintBaseline();
|
|
10424
|
+
}
|
|
10425
|
+
function releaseFingerprintFn() {
|
|
10426
|
+
if (!ownsFingerprintFn) return;
|
|
10427
|
+
if (stdoutFingerprintOwners.get(terminal) !== fingerprintOwner) {
|
|
10428
|
+
ownsFingerprintFn = false;
|
|
10429
|
+
return;
|
|
10430
|
+
}
|
|
10431
|
+
stdoutFingerprintOwners.delete(terminal);
|
|
10432
|
+
ownsFingerprintFn = false;
|
|
10433
|
+
try {
|
|
10434
|
+
var _fingerprintTerminal$2;
|
|
10435
|
+
(_fingerprintTerminal$2 = fingerprintTerminal.setFingerprintFn) === null || _fingerprintTerminal$2 === void 0 || _fingerprintTerminal$2.call(fingerprintTerminal, null);
|
|
10436
|
+
} catch {}
|
|
9755
10437
|
}
|
|
9756
10438
|
installFingerprintFn();
|
|
9757
|
-
if (altScreen &&
|
|
10439
|
+
if (altScreen && outputIsTTY) out.write("\x1B[?1049h");
|
|
9758
10440
|
if (hideCursor) out.write("\x1B[?25l");
|
|
9759
10441
|
if (clear) out.write(`${SGR_RESET}${openBg(defaultBg)}\u001B[2J\u001B[H${SGR_RESET}`);
|
|
9760
10442
|
const off = terminal.on("commit", ({ dirtyRows, scrollOperations, sync }) => {
|
|
@@ -9762,7 +10444,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9762
10444
|
render(dirtyRows, sync, scrollOperations);
|
|
9763
10445
|
});
|
|
9764
10446
|
const resizeSource = (options === null || options === void 0 ? void 0 : options.output) ?? process.stdout;
|
|
9765
|
-
const canTrackResize = Boolean(trackResize &&
|
|
10447
|
+
const canTrackResize = Boolean(trackResize && outputIsTTY && typeof (resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.on) === "function");
|
|
9766
10448
|
const onResize = () => {
|
|
9767
10449
|
const cols = Number(resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.columns);
|
|
9768
10450
|
const rows = Number(resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.rows);
|
|
@@ -9809,7 +10491,9 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9809
10491
|
accumulatedDirtyCount = 0;
|
|
9810
10492
|
accumulatedDirtyMin = Number.POSITIVE_INFINITY;
|
|
9811
10493
|
accumulatedDirtyMax = -1;
|
|
10494
|
+
accumulatedDirtyRowsRequireRepaint = false;
|
|
9812
10495
|
off();
|
|
10496
|
+
releaseFingerprintFn();
|
|
9813
10497
|
if (canTrackResize && typeof (resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.off) === "function") try {
|
|
9814
10498
|
resizeSource.off("resize", onResize);
|
|
9815
10499
|
} catch {}
|
|
@@ -9817,7 +10501,7 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9817
10501
|
resizeSource.removeListener("resize", onResize);
|
|
9818
10502
|
} catch {}
|
|
9819
10503
|
if (hideCursor) out.write("\x1B[?25h");
|
|
9820
|
-
if (altScreen &&
|
|
10504
|
+
if (altScreen && outputIsTTY) out.write("\x1B[?1049l");
|
|
9821
10505
|
profiler === null || profiler === void 0 || profiler.dispose();
|
|
9822
10506
|
}
|
|
9823
10507
|
const updateTheme = (next) => {
|
|
@@ -9827,10 +10511,8 @@ function createStdoutRenderer(terminal, options) {
|
|
|
9827
10511
|
if (nextBg !== defaultBg) defaultBg = nextBg;
|
|
9828
10512
|
}
|
|
9829
10513
|
if ("palette" in next) palette = next.palette ?? null;
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
nextColorIdx = 17;
|
|
9833
|
-
installFingerprintFn();
|
|
10514
|
+
resetStyleKeyIndex();
|
|
10515
|
+
installFingerprintFn(true);
|
|
9834
10516
|
fpRows = 0;
|
|
9835
10517
|
fpPrevValid = false;
|
|
9836
10518
|
prevOverlayBlockedRows = [];
|