@simon_he/vue-tui 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/dist/index.cjs +1478 -125
  2. package/dist/index.d.ts +119 -102
  3. package/dist/index.js +1295 -112
  4. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { isAbsolutePath, normalizePath, parsePathQuery, resolvePath, resolveUserPath as resolveUserPath$1, resolveUserPath$1 as resolveUserPath, stripTrailingSlash, suggestPaths, suggestPaths$1 } from "./path-suggest-CZQYJIIg.js";
2
- import process from "node:process";
2
+ import process$1 from "node:process";
3
3
  import { appendFileSync, writeFileSync } from "node:fs";
4
4
  import { Buffer } from "node:buffer";
5
5
  import { EventEmitter } from "node:events";
6
- import { computed, createRenderer, defineComponent, effectScope, h, inject, onBeforeUnmount, onMounted, onScopeDispose, onUnmounted, provide, reactive, ref, shallowReactive, shallowRef, toRef, vShow, watch, watchEffect } from "vue";
6
+ import { computed, createRenderer, defineComponent, effectScope, h, inject, nextTick, onBeforeUnmount, onMounted, onScopeDispose, onUnmounted, provide, reactive, ref, shallowReactive, shallowRef, toRef, vShow, watch, watchEffect } from "vue";
7
7
 
8
8
  //#region src/core/ansi-palette.ts
9
9
  const ANSI_PALETTE_HEX = Object.freeze({
@@ -1286,7 +1286,7 @@ function parseMouseSgr(sequence) {
1286
1286
  handled: false,
1287
1287
  event: null
1288
1288
  };
1289
- const env = (process === null || process === void 0 ? void 0 : process.env) ?? {};
1289
+ const env = (process$1 === null || process$1 === void 0 ? void 0 : process$1.env) ?? {};
1290
1290
  const invertWheel = String(env.DIMCODE_WHEEL_INVERT ?? env.VUE_TUI_WHEEL_INVERT ?? "") === "1";
1291
1291
  const mouseDebugEnabled = String(env.DIMCODE_MOUSE_DEBUG ?? env.VUE_TUI_MOUSE_DEBUG ?? "") === "1";
1292
1292
  const mouseDebugPath = String(env.DIMCODE_MOUSE_DEBUG_PATH ?? env.VUE_TUI_MOUSE_DEBUG_PATH ?? "/tmp/goatchain-mouse-debug.log");
@@ -1654,8 +1654,8 @@ function ctrlKeyFromChar(ch) {
1654
1654
  return null;
1655
1655
  }
1656
1656
  function createStdinDriver(options) {
1657
- const stdin = options.stdin ?? process.stdin;
1658
- const stdout = options.stdout ?? process.stdout;
1657
+ const stdin = options.stdin ?? process$1.stdin;
1658
+ const stdout = options.stdout ?? process$1.stdout;
1659
1659
  if (!stdin || !stdout) throw new Error("createStdinDriver requires Node process.stdin/stdout");
1660
1660
  const enableMouse = options.enableMouse ?? true;
1661
1661
  const enableMouseMotion = options.enableMouseMotion ?? false;
@@ -2511,12 +2511,59 @@ function createGridBuffer(cols, rows) {
2511
2511
  cursorStyle: DEFAULT_STYLE,
2512
2512
  scrollback: [],
2513
2513
  scrollbackLimit: 1e3,
2514
- rowPool: []
2514
+ rowPool: [],
2515
+ soaFingerprints: null,
2516
+ fingerprintFn: null
2515
2517
  };
2516
2518
  }
2517
2519
  function clamp$7(n, min, max) {
2518
2520
  return Math.max(min, Math.min(max, n));
2519
2521
  }
2522
+ /**
2523
+ * Register a fingerprint function on the buffer. This enables SoA fingerprint
2524
+ * pre-computation: cell writes will update fingerprints inline, and the renderer
2525
+ * can read them via getRowFingerprints() instead of per-cell hash computation.
2526
+ */
2527
+ function setFingerprintFn(buffer, fn) {
2528
+ buffer.fingerprintFn = fn;
2529
+ const len = buffer.rows * buffer.cols;
2530
+ if (!len) {
2531
+ buffer.soaFingerprints = null;
2532
+ return;
2533
+ }
2534
+ buffer.soaFingerprints = new Uint32Array(len);
2535
+ for (let y = 0; y < buffer.rows; y++) {
2536
+ const row = getBufferRow(buffer, y);
2537
+ const physY = physicalRowIndex(buffer, y);
2538
+ const base = physY * buffer.cols;
2539
+ for (let x = 0; x < buffer.cols; x++) {
2540
+ const cell = row[x];
2541
+ buffer.soaFingerprints[base + x] = fn(cell.ch, cell.style);
2542
+ }
2543
+ }
2544
+ }
2545
+ /**
2546
+ * Get pre-computed fingerprints for a logical row as a TypedArray slice.
2547
+ * Returns null if fingerprinting is not enabled on this buffer.
2548
+ */
2549
+ function getRowFingerprints(buffer, y) {
2550
+ if (!buffer.soaFingerprints) return null;
2551
+ const physY = physicalRowIndex(buffer, y);
2552
+ const base = physY * buffer.cols;
2553
+ return buffer.soaFingerprints.subarray(base, base + buffer.cols);
2554
+ }
2555
+ /** Update the SoA fingerprint for a single cell (called inline from putCell/fillRect). */
2556
+ function updateCellFingerprint(buffer, physY, x, ch, style) {
2557
+ if (!buffer.soaFingerprints || !buffer.fingerprintFn) return;
2558
+ buffer.soaFingerprints[physY * buffer.cols + x] = buffer.fingerprintFn(ch, style);
2559
+ }
2560
+ /** Update SoA fingerprints for a range of cells in a physical row. */
2561
+ function updateRangeFingerprint(buffer, physY, x0, x1, ch, style) {
2562
+ if (!buffer.soaFingerprints || !buffer.fingerprintFn) return;
2563
+ const fp = buffer.fingerprintFn(ch, style);
2564
+ const base = physY * buffer.cols;
2565
+ for (let x = x0; x < x1; x++) buffer.soaFingerprints[base + x] = fp;
2566
+ }
2520
2567
  function physicalRowIndex(buffer, y) {
2521
2568
  return (buffer.gridStart + y) % buffer.rows;
2522
2569
  }
@@ -2575,8 +2622,14 @@ function putCell(buffer, x, y, ch, style) {
2575
2622
  markDirty(buffer, y);
2576
2623
  return;
2577
2624
  }
2578
- row[x] = createCell(ch, style);
2625
+ const cell = createCell(ch, style);
2626
+ row[x] = cell;
2579
2627
  if (width === 2 && x + 1 < buffer.cols) row[x + 1] = createContinuationCell(style);
2628
+ if (buffer.soaFingerprints) {
2629
+ const physY = physicalRowIndex(buffer, y);
2630
+ updateCellFingerprint(buffer, physY, x, cell.ch, cell.style);
2631
+ if (width === 2 && x + 1 < buffer.cols) updateCellFingerprint(buffer, physY, x + 1, "", cell.style);
2632
+ }
2580
2633
  markDirty(buffer, y);
2581
2634
  }
2582
2635
  function fillRect(buffer, x, y, w, h$1, ch = " ", style) {
@@ -2596,6 +2649,10 @@ function fillRect(buffer, x, y, w, h$1, ch = " ", style) {
2596
2649
  if ((_row$x = row[x0]) === null || _row$x === void 0 ? void 0 : _row$x.continuation) clearWideIfOverwriting(row, x0);
2597
2650
  row.fill(fillCell, x0, x1);
2598
2651
  if (x1 < buffer.cols && ((_row$x2 = row[x1]) === null || _row$x2 === void 0 ? void 0 : _row$x2.continuation)) row[x1] = blank;
2652
+ if (buffer.soaFingerprints) {
2653
+ const physY = physicalRowIndex(buffer, yy);
2654
+ updateRangeFingerprint(buffer, physY, x0, x1, fillCell.ch, fillCell.style);
2655
+ }
2599
2656
  markDirty(buffer, yy);
2600
2657
  }
2601
2658
  return;
@@ -2654,6 +2711,18 @@ function blankRow(buffer) {
2654
2711
  for (let x = 0; x < buffer.cols; x++) pooled[x] = blank;
2655
2712
  return pooled;
2656
2713
  }
2714
+ function recomputeFingerprintsForRows(buffer, startY, endY) {
2715
+ if (!buffer.soaFingerprints || !buffer.fingerprintFn) return;
2716
+ for (let y = startY; y < endY; y++) {
2717
+ const row = getBufferRow(buffer, y);
2718
+ const physY = physicalRowIndex(buffer, y);
2719
+ const base = physY * buffer.cols;
2720
+ for (let x = 0; x < buffer.cols; x++) {
2721
+ const cell = row[x];
2722
+ buffer.soaFingerprints[base + x] = buffer.fingerprintFn(cell.ch, cell.style);
2723
+ }
2724
+ }
2725
+ }
2657
2726
  function scrollBuffer(buffer, lines) {
2658
2727
  const n = Math.trunc(lines);
2659
2728
  if (n === 0 || buffer.rows === 0) return;
@@ -2682,6 +2751,63 @@ function scrollBuffer(buffer, lines) {
2682
2751
  markAllDirty(buffer);
2683
2752
  buffer.cursorY = clamp$7(buffer.cursorY - n, 0, Math.max(0, buffer.rows - 1));
2684
2753
  }
2754
+ function scrollBufferRegion(buffer, startY, endY, lines) {
2755
+ const n = Math.trunc(lines);
2756
+ if (n === 0 || buffer.rows === 0) return [];
2757
+ const start = clamp$7(Math.floor(startY), 0, buffer.rows);
2758
+ const end = clamp$7(Math.floor(endY), 0, buffer.rows);
2759
+ const height = end - start;
2760
+ if (height <= 0) return [];
2761
+ const absDelta = Math.abs(n);
2762
+ const insertedRows = [];
2763
+ const replaceRegionWithBlankRows = () => {
2764
+ for (let y = start; y < end; y++) {
2765
+ const physY = physicalRowIndex(buffer, y);
2766
+ buffer.rowPool.push(buffer.grid[physY]);
2767
+ buffer.grid[physY] = blankRow(buffer);
2768
+ markDirty(buffer, y);
2769
+ insertedRows.push(y);
2770
+ }
2771
+ };
2772
+ if (absDelta >= height) {
2773
+ replaceRegionWithBlankRows();
2774
+ recomputeFingerprintsForRows(buffer, start, end);
2775
+ if (buffer.cursorY >= start && buffer.cursorY < end) buffer.cursorY = clamp$7(buffer.cursorY - n, start, Math.max(start, end - 1));
2776
+ return insertedRows;
2777
+ }
2778
+ const regionRows = [];
2779
+ for (let y = start; y < end; y++) regionRows.push(getBufferRow(buffer, y));
2780
+ const nextRows = Array.from({ length: height }, () => []);
2781
+ if (n > 0) {
2782
+ const movedCount = height - n;
2783
+ for (let i = 0; i < movedCount; i++) nextRows[i] = regionRows[i + n];
2784
+ for (let i = 0; i < n; i++) {
2785
+ buffer.rowPool.push(regionRows[i]);
2786
+ const y = end - n + i;
2787
+ nextRows[movedCount + i] = blankRow(buffer);
2788
+ markDirty(buffer, y);
2789
+ insertedRows.push(y);
2790
+ }
2791
+ } else {
2792
+ const insertedCount = -n;
2793
+ const movedCount = height - insertedCount;
2794
+ for (let i = 0; i < insertedCount; i++) {
2795
+ buffer.rowPool.push(regionRows[height - insertedCount + i]);
2796
+ const y = start + i;
2797
+ nextRows[i] = blankRow(buffer);
2798
+ markDirty(buffer, y);
2799
+ insertedRows.push(y);
2800
+ }
2801
+ for (let i = 0; i < movedCount; i++) nextRows[insertedCount + i] = regionRows[i];
2802
+ }
2803
+ for (let i = 0; i < height; i++) {
2804
+ const physY = physicalRowIndex(buffer, start + i);
2805
+ buffer.grid[physY] = nextRows[i];
2806
+ }
2807
+ recomputeFingerprintsForRows(buffer, start, end);
2808
+ if (buffer.cursorY >= start && buffer.cursorY < end) buffer.cursorY = clamp$7(buffer.cursorY - n, start, Math.max(start, end - 1));
2809
+ return insertedRows;
2810
+ }
2685
2811
  function resizeBuffer(buffer, cols, rows) {
2686
2812
  const nextCols = Math.max(0, Math.floor(cols));
2687
2813
  const nextRows = Math.max(0, Math.floor(rows));
@@ -2712,6 +2838,20 @@ function resizeBuffer(buffer, cols, rows) {
2712
2838
  buffer.dirtyAll = nextRows > 0;
2713
2839
  buffer.cursorX = clamp$7(buffer.cursorX, 0, Math.max(0, nextCols));
2714
2840
  buffer.cursorY = clamp$7(buffer.cursorY, 0, Math.max(0, nextRows - 1));
2841
+ if (buffer.fingerprintFn) {
2842
+ const len = nextRows * nextCols;
2843
+ if (len > 0) {
2844
+ buffer.soaFingerprints = new Uint32Array(len);
2845
+ for (let y = 0; y < nextRows; y++) {
2846
+ const row = nextGrid[y];
2847
+ const base = y * nextCols;
2848
+ for (let x = 0; x < nextCols; x++) {
2849
+ const cell = row[x];
2850
+ buffer.soaFingerprints[base + x] = buffer.fingerprintFn(cell.ch, cell.style);
2851
+ }
2852
+ }
2853
+ } else buffer.soaFingerprints = null;
2854
+ }
2715
2855
  }
2716
2856
  function snapshotText(buffer) {
2717
2857
  const out = [];
@@ -2874,6 +3014,32 @@ function scrollPlaneCoverage(state, lines) {
2874
3014
  state.coverage.splice(rows + n, -n);
2875
3015
  for (let i = 0; i < -n; i++) state.coverage.unshift(new Uint8Array(cols));
2876
3016
  }
3017
+ function scrollPlaneCoverageRegion(state, startY, endY, lines) {
3018
+ const n = Math.trunc(lines);
3019
+ if (n === 0 || state.coverage.length === 0) return;
3020
+ const start = Math.max(0, Math.min(state.coverage.length, Math.floor(startY)));
3021
+ const end = Math.max(0, Math.min(state.coverage.length, Math.floor(endY)));
3022
+ const height = end - start;
3023
+ if (height <= 0) return;
3024
+ const cols = state.buffer.cols;
3025
+ if (Math.abs(n) >= height) {
3026
+ for (let y = start; y < end; y++) state.coverage[y] = new Uint8Array(cols);
3027
+ return;
3028
+ }
3029
+ const region = state.coverage.slice(start, end);
3030
+ const next = Array.from({ length: height }, () => new Uint8Array(0));
3031
+ if (n > 0) {
3032
+ const movedCount = height - n;
3033
+ for (let i = 0; i < movedCount; i++) next[i] = region[i + n];
3034
+ for (let i = 0; i < n; i++) next[movedCount + i] = new Uint8Array(cols);
3035
+ } else {
3036
+ const insertedCount = -n;
3037
+ const movedCount = height - insertedCount;
3038
+ for (let i = 0; i < insertedCount; i++) next[i] = new Uint8Array(cols);
3039
+ for (let i = 0; i < movedCount; i++) next[insertedCount + i] = region[i];
3040
+ }
3041
+ for (let i = 0; i < height; i++) state.coverage[start + i] = next[i];
3042
+ }
2877
3043
  function resizePlaneState(state, cols, rows) {
2878
3044
  resizeBuffer(state.buffer, cols, rows);
2879
3045
  state.coverage = resizeCoverage(state.coverage, state.buffer.cols, state.buffer.rows);
@@ -2902,6 +3068,14 @@ function resetPlaneRowsForRender(terminal, plane, dirtyRows) {
2902
3068
  const internals = terminal[TERMINAL_PLANE_INTERNALS];
2903
3069
  internals === null || internals === void 0 || internals.resetRowsForRender(plane, dirtyRows);
2904
3070
  }
3071
+ function getPlaneRowCoverageKind(terminal, plane, y) {
3072
+ const internals = terminal[TERMINAL_PLANE_INTERNALS];
3073
+ return (internals === null || internals === void 0 ? void 0 : internals.getRowCoverageKind(plane, y)) ?? 0;
3074
+ }
3075
+ function scrollPlaneRows(terminal, plane, startY, endY, lines) {
3076
+ const internals = terminal[TERMINAL_PLANE_INTERNALS];
3077
+ internals === null || internals === void 0 || internals.scrollRows(plane, startY, endY, lines);
3078
+ }
2905
3079
  function createTerminal(opts) {
2906
3080
  const emitter = new Emitter();
2907
3081
  const compositeBuffer = createGridBuffer(opts.cols, opts.rows);
@@ -2911,6 +3085,7 @@ function createTerminal(opts) {
2911
3085
  let pendingCommit = false;
2912
3086
  let pendingCommitAllPlanes = false;
2913
3087
  let pendingCommitPlanes = null;
3088
+ const pendingPlaneScrollOps = new Map();
2914
3089
  const planeTerminals = new Map();
2915
3090
  let base;
2916
3091
  compositeBuffer.cursorVisible = false;
@@ -3031,8 +3206,145 @@ function createTerminal(opts) {
3031
3206
  scrollBuffer(state.buffer, lines);
3032
3207
  scrollPlaneCoverage(state, lines);
3033
3208
  }
3209
+ function normalizeScrollOperation(startY, endY, delta, totalRows) {
3210
+ const start = Math.max(0, Math.min(totalRows, Math.floor(startY)));
3211
+ const end = Math.max(0, Math.min(totalRows, Math.floor(endY)));
3212
+ const height = end - start;
3213
+ const lines = Math.trunc(delta);
3214
+ if (height <= 0 || lines === 0 || Math.abs(lines) >= height) return null;
3215
+ return {
3216
+ startY: start,
3217
+ endY: end,
3218
+ delta: lines
3219
+ };
3220
+ }
3221
+ function recordPendingScrollOp(plane, op) {
3222
+ const prev = pendingPlaneScrollOps.get(plane);
3223
+ if (prev && prev.startY === op.startY && prev.endY === op.endY) {
3224
+ const nextDelta = prev.delta + op.delta;
3225
+ if (nextDelta === 0) {
3226
+ pendingPlaneScrollOps.delete(plane);
3227
+ return;
3228
+ }
3229
+ const next = normalizeScrollOperation(op.startY, op.endY, nextDelta, compositeBuffer.rows);
3230
+ if (!next) {
3231
+ pendingPlaneScrollOps.delete(plane);
3232
+ return;
3233
+ }
3234
+ pendingPlaneScrollOps.set(plane, next);
3235
+ return;
3236
+ }
3237
+ pendingPlaneScrollOps.set(plane, op);
3238
+ }
3239
+ function takePendingScrollOps(planes) {
3240
+ const planesToTake = (planes === null || planes === void 0 ? void 0 : planes.length) ? planes : TERMINAL_RENDER_PLANES;
3241
+ const out = [];
3242
+ for (const plane of planesToTake) {
3243
+ const op = pendingPlaneScrollOps.get(plane);
3244
+ if (!op) continue;
3245
+ pendingPlaneScrollOps.delete(plane);
3246
+ out.push({
3247
+ plane,
3248
+ ...op
3249
+ });
3250
+ }
3251
+ return out.length ? out : null;
3252
+ }
3253
+ function higherPlanesFor(plane) {
3254
+ const planeIndex = TERMINAL_RENDER_PLANES.indexOf(plane);
3255
+ if (planeIndex < 0) return [];
3256
+ return TERMINAL_RENDER_PLANES.slice(planeIndex + 1);
3257
+ }
3258
+ function higherPlaneCoverageKind(plane, y) {
3259
+ const higherPlanes = higherPlanesFor(plane);
3260
+ if (!higherPlanes.length || y < 0 || y >= compositeBuffer.rows) return 0;
3261
+ let covered = 0;
3262
+ for (let x = 0; x < compositeBuffer.cols; x++) {
3263
+ let cellCovered = false;
3264
+ for (const higherPlane of higherPlanes) {
3265
+ var _state$coverage$y2;
3266
+ const state = planeStates.get(higherPlane);
3267
+ if (state === null || state === void 0 || (_state$coverage$y2 = state.coverage[y]) === null || _state$coverage$y2 === void 0 ? void 0 : _state$coverage$y2[x]) {
3268
+ cellCovered = true;
3269
+ break;
3270
+ }
3271
+ }
3272
+ if (cellCovered) covered++;
3273
+ }
3274
+ if (covered === 0) return 0;
3275
+ if (covered >= compositeBuffer.cols) return 2;
3276
+ return 1;
3277
+ }
3278
+ function prepareCompositeScrollOps(pendingOps) {
3279
+ if (!(pendingOps === null || pendingOps === void 0 ? void 0 : pendingOps.length)) return {
3280
+ scrollOperations: null,
3281
+ extraDirtyRows: []
3282
+ };
3283
+ const scrollOperations = [];
3284
+ const extraDirtyRows = new Set();
3285
+ for (const pending of pendingOps) {
3286
+ const blockedRows = new Set();
3287
+ const partiallyBlockedRows = new Set();
3288
+ for (let y = pending.startY; y < pending.endY; y++) {
3289
+ const coverageKind = higherPlaneCoverageKind(pending.plane, y);
3290
+ if (coverageKind === 0) continue;
3291
+ blockedRows.add(y);
3292
+ if (coverageKind === 1) partiallyBlockedRows.add(y);
3293
+ }
3294
+ if (!blockedRows.size) {
3295
+ scrollOperations.push({
3296
+ startY: pending.startY,
3297
+ endY: pending.endY,
3298
+ delta: pending.delta
3299
+ });
3300
+ continue;
3301
+ }
3302
+ const absDelta = Math.abs(pending.delta);
3303
+ for (let y = pending.startY; y < pending.endY; y++) if (partiallyBlockedRows.has(y)) extraDirtyRows.add(y);
3304
+ let bandStart = -1;
3305
+ const flushBand = (bandEnd) => {
3306
+ if (bandStart < 0 || bandEnd <= bandStart) return;
3307
+ const bandHeight = bandEnd - bandStart;
3308
+ if (absDelta >= bandHeight) {
3309
+ for (let y = bandStart; y < bandEnd; y++) extraDirtyRows.add(y);
3310
+ bandStart = -1;
3311
+ return;
3312
+ }
3313
+ scrollOperations.push({
3314
+ startY: bandStart,
3315
+ endY: bandEnd,
3316
+ delta: pending.delta
3317
+ });
3318
+ if (pending.delta > 0) for (let y = bandEnd - absDelta; y < bandEnd; y++) extraDirtyRows.add(y);
3319
+ else for (let y = bandStart; y < bandStart + absDelta; y++) extraDirtyRows.add(y);
3320
+ bandStart = -1;
3321
+ };
3322
+ for (let y = pending.startY; y < pending.endY; y++) {
3323
+ if (blockedRows.has(y)) {
3324
+ flushBand(y);
3325
+ continue;
3326
+ }
3327
+ if (bandStart < 0) bandStart = y;
3328
+ }
3329
+ flushBand(pending.endY);
3330
+ }
3331
+ return {
3332
+ scrollOperations: scrollOperations.length ? scrollOperations : null,
3333
+ extraDirtyRows: Array.from(extraDirtyRows).sort((a, b) => a - b)
3334
+ };
3335
+ }
3336
+ function scrollRowsForPlane(plane, startY, endY, lines) {
3337
+ const state = getPlaneState(plane);
3338
+ const op = normalizeScrollOperation(startY, endY, lines, state.buffer.rows);
3339
+ if (!op) return;
3340
+ scrollBufferRegion(state.buffer, op.startY, op.endY, op.delta);
3341
+ scrollPlaneCoverageRegion(state, op.startY, op.endY, op.delta);
3342
+ recordPendingScrollOp(plane, op);
3343
+ }
3034
3344
  function composeRows(rows) {
3035
3345
  const blank = createBlankCell();
3346
+ const fpFn = compositeBuffer.fingerprintFn;
3347
+ const fpArr = compositeBuffer.soaFingerprints;
3036
3348
  const rowList = rows ?? Array.from({ length: compositeBuffer.rows }, (_, index) => index);
3037
3349
  for (const y of rowList) {
3038
3350
  if (y < 0 || y >= compositeBuffer.rows) continue;
@@ -3056,6 +3368,14 @@ function createTerminal(opts) {
3056
3368
  if (x === 0 || dst[x - 1].width !== 2 || dst[x - 1].continuation) dst[x] = blank;
3057
3369
  }
3058
3370
  }
3371
+ if (fpFn && fpArr) {
3372
+ const physY = (compositeBuffer.gridStart + y) % compositeBuffer.rows;
3373
+ const base$1 = physY * cols;
3374
+ for (let x = 0; x < cols; x++) {
3375
+ const cell = dst[x];
3376
+ fpArr[base$1 + x] = fpFn(cell.ch, cell.style);
3377
+ }
3378
+ }
3059
3379
  markDirty(compositeBuffer, y);
3060
3380
  }
3061
3381
  }
@@ -3228,6 +3548,14 @@ function createTerminal(opts) {
3228
3548
  const all = state.buffer.scrollback.map((row) => row.map((cell) => cell.continuation ? " " : cell.ch || " ").join(""));
3229
3549
  if (count == null) return all;
3230
3550
  return all.slice(Math.max(0, all.length - Math.max(0, Math.floor(count))));
3551
+ },
3552
+ setFingerprintFn(fn) {
3553
+ assertNotDisposed();
3554
+ base.setFingerprintFn(fn);
3555
+ },
3556
+ getRowFingerprints(y) {
3557
+ assertNotDisposed();
3558
+ return base.getRowFingerprints(y);
3231
3559
  }
3232
3560
  };
3233
3561
  planeTerminals.set(plane, api);
@@ -3302,6 +3630,9 @@ function createTerminal(opts) {
3302
3630
  return pendingDirtyRowsForPlanes((meta === null || meta === void 0 ? void 0 : meta.planes) ?? null);
3303
3631
  }
3304
3632
  const planesToCompose = (meta === null || meta === void 0 || (_meta$planes2 = meta.planes) === null || _meta$planes2 === void 0 ? void 0 : _meta$planes2.length) ? meta.planes : TERMINAL_RENDER_PLANES;
3633
+ const pendingScrollOps = takePendingScrollOps(planesToCompose);
3634
+ const preparedScroll = prepareCompositeScrollOps(pendingScrollOps);
3635
+ const scrollOperations = preparedScroll.scrollOperations;
3305
3636
  let composeAllRows = false;
3306
3637
  const dirtyRows = new Set();
3307
3638
  for (const plane of planesToCompose) {
@@ -3314,13 +3645,17 @@ function createTerminal(opts) {
3314
3645
  }
3315
3646
  for (const y of rows) dirtyRows.add(y);
3316
3647
  }
3648
+ for (const y of preparedScroll.extraDirtyRows) dirtyRows.add(y);
3317
3649
  const normalizedDirtyRows = composeAllRows ? null : Array.from(dirtyRows).sort((a, b) => a - b);
3650
+ if (scrollOperations) for (const op of scrollOperations) scrollBufferRegion(compositeBuffer, op.startY, op.endY, op.delta);
3318
3651
  if (normalizedDirtyRows !== null && normalizedDirtyRows.length === 0) {
3319
3652
  const pendingCompositeRows = collectAndClearDirtyRows(compositeBuffer);
3320
3653
  if (pendingCompositeRows !== null && pendingCompositeRows.length === 0) return pendingCompositeRows;
3321
3654
  emitter.emit("commit", {
3322
3655
  dirtyRows: pendingCompositeRows,
3323
- planes: (meta === null || meta === void 0 ? void 0 : meta.planes) ?? null
3656
+ planes: (meta === null || meta === void 0 ? void 0 : meta.planes) ?? null,
3657
+ sync: meta === null || meta === void 0 ? void 0 : meta.sync,
3658
+ scrollOperations
3324
3659
  });
3325
3660
  return pendingCompositeRows;
3326
3661
  }
@@ -3331,7 +3666,9 @@ function createTerminal(opts) {
3331
3666
  if (committedRows !== null && committedRows.length === 0) return committedRows;
3332
3667
  emitter.emit("commit", {
3333
3668
  dirtyRows: committedRows,
3334
- planes: (meta === null || meta === void 0 ? void 0 : meta.planes) ?? null
3669
+ planes: (meta === null || meta === void 0 ? void 0 : meta.planes) ?? null,
3670
+ sync: meta === null || meta === void 0 ? void 0 : meta.sync,
3671
+ scrollOperations
3335
3672
  });
3336
3673
  return committedRows;
3337
3674
  },
@@ -3386,6 +3723,14 @@ function createTerminal(opts) {
3386
3723
  const all = state.buffer.scrollback.map((row) => row.map((cell) => cell.continuation ? " " : cell.ch || " ").join(""));
3387
3724
  if (count == null) return all;
3388
3725
  return all.slice(Math.max(0, all.length - Math.max(0, Math.floor(count))));
3726
+ },
3727
+ setFingerprintFn(fn) {
3728
+ assertNotDisposed();
3729
+ setFingerprintFn(compositeBuffer, fn);
3730
+ },
3731
+ getRowFingerprints(y) {
3732
+ assertNotDisposed();
3733
+ return getRowFingerprints(compositeBuffer, y);
3389
3734
  }
3390
3735
  };
3391
3736
  base[TERMINAL_PLANE_INTERNALS] = {
@@ -3398,6 +3743,20 @@ function createTerminal(opts) {
3398
3743
  return;
3399
3744
  }
3400
3745
  for (const y of rows) clearPlaneCoverageRow(state, y);
3746
+ },
3747
+ getRowCoverageKind(plane, y) {
3748
+ const state = getPlaneState(plane);
3749
+ if (y < 0 || y >= state.coverage.length) return 0;
3750
+ const row = state.coverage[y];
3751
+ if (!(row === null || row === void 0 ? void 0 : row.length)) return 0;
3752
+ let covered = 0;
3753
+ for (let x = 0; x < row.length; x++) if (row[x]) covered++;
3754
+ if (covered === 0) return 0;
3755
+ if (covered >= row.length) return 2;
3756
+ return 1;
3757
+ },
3758
+ scrollRows(plane, startY, endY, lines) {
3759
+ scrollRowsForPlane(plane, startY, endY, lines);
3401
3760
  }
3402
3761
  };
3403
3762
  return base;
@@ -3719,7 +4078,7 @@ function createCliEventManager(options) {
3719
4078
  handler(event);
3720
4079
  } catch (err) {
3721
4080
  try {
3722
- const env = process === null || process === void 0 ? void 0 : process.env;
4081
+ const env = process$1 === null || process$1 === void 0 ? void 0 : process$1.env;
3723
4082
  if ((env === null || env === void 0 ? void 0 : env.DIMCODE_DEBUG) === "1") {
3724
4083
  const timestamp = new Date().toISOString().split("T")[1].slice(0, -1);
3725
4084
  appendFileSync("/tmp/goatchain-debug.log", `[${timestamp}] [EVENT-MGR] ERROR in handler ${node.id}.${handlerKey}: ${err}\n`);
@@ -5057,7 +5416,7 @@ function formatClockTime(ms) {
5057
5416
  }
5058
5417
  function createTuiProfiler(name) {
5059
5418
  var _unref;
5060
- const env = process === null || process === void 0 ? void 0 : process.env;
5419
+ const env = process$1 === null || process$1 === void 0 ? void 0 : process$1.env;
5061
5420
  if (!parseEnabled(env === null || env === void 0 ? void 0 : env.DIMCODE_PROFILE_TUI)) return null;
5062
5421
  const format = parseLogFormat(env === null || env === void 0 ? void 0 : env.DIMCODE_PROFILE_TUI_FORMAT);
5063
5422
  const logDest = parseLogDest(env === null || env === void 0 ? void 0 : env.DIMCODE_PROFILE_TUI_LOG_DEST);
@@ -5536,7 +5895,7 @@ function write(data) {
5536
5895
  * Check if debug logging is enabled via environment variable
5537
5896
  */
5538
5897
  function isDebugEnabled() {
5539
- const env = process === null || process === void 0 ? void 0 : process.env;
5898
+ const env = process$1 === null || process$1 === void 0 ? void 0 : process$1.env;
5540
5899
  return (env === null || env === void 0 ? void 0 : env.DIMCODE_DEBUG) === "1" || (env === null || env === void 0 ? void 0 : env.DEBUG) === "1";
5541
5900
  }
5542
5901
 
@@ -6012,6 +6371,26 @@ function createRenderManager(terminal) {
6012
6371
  if (y > state.dirtyMaxY) state.dirtyMaxY = y;
6013
6372
  }
6014
6373
  }
6374
+ function markRows(plane, rows) {
6375
+ if (!rows.length) return;
6376
+ const state = getDirtyState(plane);
6377
+ for (let i = 0; i < rows.length; i++) {
6378
+ const y = Math.floor(rows[i] ?? -1);
6379
+ if (y < 0 || y >= terminalRows) continue;
6380
+ if (state.dirtyRowBits[y] === 0) {
6381
+ state.dirtyRowBits[y] = 1;
6382
+ state.dirtyRowCount++;
6383
+ if (y < state.dirtyMinY) state.dirtyMinY = y;
6384
+ if (y > state.dirtyMaxY) state.dirtyMaxY = y;
6385
+ }
6386
+ }
6387
+ }
6388
+ function scrollPlane(plane, startY, endY, delta) {
6389
+ scrollPlaneRows(terminal, plane, startY, endY, delta);
6390
+ }
6391
+ function invalidatePlane(plane) {
6392
+ getDirtyState(plane).allRowsDirty = true;
6393
+ }
6015
6394
  function register(node) {
6016
6395
  const id = `r${nextNodeId++}`;
6017
6396
  const rect = node.rect ?? null;
@@ -6039,8 +6418,13 @@ function createRenderManager(terminal) {
6039
6418
  const nextPlane = next.plane ?? prev.plane;
6040
6419
  const nextRect = next.rect ?? prev.rect;
6041
6420
  const { y0, y1 } = rectToYBounds(nextRect);
6042
- markRect(prev.plane, prev.rect);
6043
- markRect(nextPlane, nextRect);
6421
+ const dirtyRowsHint = next.dirtyRowsHint;
6422
+ const canUseDirtyRowsHint = nextPlane === prev.plane && dirtyRowsHint != null && dirtyRowsHint.length > 0;
6423
+ if (canUseDirtyRowsHint) markRows(nextPlane, dirtyRowsHint);
6424
+ else {
6425
+ markRect(prev.plane, prev.rect);
6426
+ markRect(nextPlane, nextRect);
6427
+ }
6044
6428
  const full = Object.freeze({
6045
6429
  ...prev,
6046
6430
  stack: next.stack ?? prev.stack,
@@ -6102,10 +6486,11 @@ function createRenderManager(terminal) {
6102
6486
  return false;
6103
6487
  }
6104
6488
  function render(options) {
6489
+ var _process;
6105
6490
  const renderStart = profiler === null || profiler === void 0 ? void 0 : profiler.now();
6106
6491
  const activePlanes = (options === null || options === void 0 ? void 0 : options.activePlanes) ?? null;
6107
6492
  const requestedPlanes = activePlanes ?? TERMINAL_RENDER_PLANES;
6108
- const env = process === null || process === void 0 ? void 0 : process.env;
6493
+ const env = (_process = process) === null || _process === void 0 ? void 0 : _process.env;
6109
6494
  if ((env === null || env === void 0 ? void 0 : env.DIMCODE_DEBUG) === "1") renderMgrDebugLog.render("[RENDER-MANAGER] render() called");
6110
6495
  if (!hasPendingDirtyWork(requestedPlanes)) {
6111
6496
  if ((env === null || env === void 0 ? void 0 : env.DIMCODE_DEBUG) === "1") renderMgrDebugLog.render("[RENDER-MANAGER] render() skipped (no dirty rows)");
@@ -6213,6 +6598,8 @@ function createRenderManager(terminal) {
6213
6598
  return {
6214
6599
  rootStack,
6215
6600
  createStack,
6601
+ invalidatePlane,
6602
+ scrollPlane,
6216
6603
  register,
6217
6604
  update,
6218
6605
  unregister,
@@ -6295,9 +6682,10 @@ function createTerminalApp(options) {
6295
6682
  let lastFlushAtMs = 0;
6296
6683
  let scheduledAtMs = 0;
6297
6684
  let timer = null;
6685
+ let pendingInvalidateAfterFlush = false;
6298
6686
  let pendingInvalidateAllPlanes = false;
6299
6687
  const pendingInvalidatePlanes = new Set();
6300
- const env = (process === null || process === void 0 ? void 0 : process.env) ?? {};
6688
+ const env = (process$1 === null || process$1 === void 0 ? void 0 : process$1.env) ?? {};
6301
6689
  const isGhostty = "GHOSTTY_RESOURCES_DIR" in env;
6302
6690
  const throttleMs = (() => {
6303
6691
  const raw = env.DIMCODE_TUI_THROTTLE_MS;
@@ -6307,7 +6695,7 @@ function createTerminalApp(options) {
6307
6695
  })();
6308
6696
  const frameThrottleMs = (() => {
6309
6697
  var _process$stdout;
6310
- if (!(process === null || process === void 0 || (_process$stdout = process.stdout) === null || _process$stdout === void 0 ? void 0 : _process$stdout.isTTY)) return 0;
6698
+ if (!(process$1 === null || process$1 === void 0 || (_process$stdout = process$1.stdout) === null || _process$stdout === void 0 ? void 0 : _process$stdout.isTTY)) return 0;
6311
6699
  return isGhostty ? 24 : 16;
6312
6700
  })();
6313
6701
  function queueInvalidatePlane(plane) {
@@ -6330,16 +6718,23 @@ function createTerminalApp(options) {
6330
6718
  pendingInvalidatePlanes.clear();
6331
6719
  return activePlanes;
6332
6720
  }
6333
- function flush() {
6721
+ function flush(sync) {
6334
6722
  if (disposed) return;
6335
6723
  scheduled = false;
6336
6724
  flushing = true;
6337
6725
  const activePlanes = takeActivePlanes();
6338
6726
  render.render({ activePlanes });
6339
- terminal.commit({ planes: activePlanes });
6727
+ terminal.commit({
6728
+ planes: activePlanes,
6729
+ sync
6730
+ });
6340
6731
  flushing = false;
6341
6732
  lastFlushAtMs = Date.now();
6342
6733
  scheduledAtMs = 0;
6734
+ if (pendingInvalidateAfterFlush) {
6735
+ pendingInvalidateAfterFlush = false;
6736
+ flush(true);
6737
+ }
6343
6738
  }
6344
6739
  function clearScheduledTimer() {
6345
6740
  if (!timer) return;
@@ -6360,7 +6755,7 @@ function createTerminalApp(options) {
6360
6755
  }
6361
6756
  clearScheduledTimer();
6362
6757
  scheduledAtMs = nowMs;
6363
- if (typeof process.nextTick === "function") process.nextTick(() => {
6758
+ if (typeof process$1.nextTick === "function") process$1.nextTick(() => {
6364
6759
  flush();
6365
6760
  });
6366
6761
  else if (typeof globalThis.setImmediate === "function") globalThis.setImmediate(() => {
@@ -6375,14 +6770,22 @@ function createTerminalApp(options) {
6375
6770
  if (flushing) return;
6376
6771
  clearScheduledTimer();
6377
6772
  scheduled = false;
6378
- flush();
6773
+ flush(true);
6379
6774
  }
6380
6775
  function invalidate(options$1) {
6381
6776
  if (disposed) return;
6382
- if (flushing) return;
6777
+ if (flushing) {
6778
+ queueInvalidatePlane(options$1 === null || options$1 === void 0 ? void 0 : options$1.plane);
6779
+ pendingInvalidateAfterFlush = true;
6780
+ return;
6781
+ }
6383
6782
  const priority = (options$1 === null || options$1 === void 0 ? void 0 : options$1.priority) ?? "normal";
6384
6783
  queueInvalidatePlane(options$1 === null || options$1 === void 0 ? void 0 : options$1.plane);
6385
6784
  profiler === null || profiler === void 0 || profiler.recordInvalidate({ plane: (options$1 === null || options$1 === void 0 ? void 0 : options$1.plane) ?? null });
6785
+ if (priority === "high") {
6786
+ flushNow();
6787
+ return;
6788
+ }
6386
6789
  const nowMs = Date.now();
6387
6790
  const throttleDelayMs = throttleMs > 0 ? Math.max(0, lastFlushAtMs + throttleMs - nowMs) : 0;
6388
6791
  const laneDelayMs = priority === "low" ? Math.max(throttleDelayMs, frameThrottleMs || 16) : priority === "normal" ? Math.max(throttleDelayMs, frameThrottleMs) : 0;
@@ -6519,6 +6922,7 @@ function createTerminalApp(options) {
6519
6922
  dispose() {
6520
6923
  if (disposed) return;
6521
6924
  disposed = true;
6925
+ clearScheduledTimer();
6522
6926
  if (mounted) app.unmount();
6523
6927
  offResize === null || offResize === void 0 || offResize();
6524
6928
  offCommit === null || offCommit === void 0 || offCommit();
@@ -6602,7 +7006,7 @@ function parseWriteMode(raw) {
6602
7006
  }
6603
7007
  function createStdoutRenderer(terminal, options) {
6604
7008
  var _ref3;
6605
- const output = (options === null || options === void 0 ? void 0 : options.output) ?? process.stdout;
7009
+ const output = (options === null || options === void 0 ? void 0 : options.output) ?? process$1.stdout;
6606
7010
  if (!output) throw new Error("createStdoutRenderer requires a Node stdout-like output");
6607
7011
  const out = output;
6608
7012
  const clear = (options === null || options === void 0 ? void 0 : options.clear) ?? true;
@@ -6615,7 +7019,7 @@ function createStdoutRenderer(terminal, options) {
6615
7019
  const profiler = createTuiProfiler("stdout-renderer");
6616
7020
  function resolveUseSyncOutput() {
6617
7021
  if ((options === null || options === void 0 ? void 0 : options.useSyncOutput) != null) return options.useSyncOutput;
6618
- const env$1 = (process === null || process === void 0 ? void 0 : process.env) ?? {};
7022
+ const env$1 = (process$1 === null || process$1 === void 0 ? void 0 : process$1.env) ?? {};
6619
7023
  const envValue = env$1.DIMCODE_SYNC_OUTPUT;
6620
7024
  if (envValue != null) {
6621
7025
  const v = String(envValue).trim().toLowerCase();
@@ -6647,8 +7051,8 @@ function createStdoutRenderer(terminal, options) {
6647
7051
  return 0;
6648
7052
  }
6649
7053
  const useSyncOutput = resolveUseSyncOutput();
6650
- const isGhostty = "GHOSTTY_RESOURCES_DIR" in ((process === null || process === void 0 ? void 0 : process.env) ?? {});
6651
- const env = (process === null || process === void 0 ? void 0 : process.env) ?? {};
7054
+ const isGhostty = "GHOSTTY_RESOURCES_DIR" in ((process$1 === null || process$1 === void 0 ? void 0 : process$1.env) ?? {});
7055
+ const env = (process$1 === null || process$1 === void 0 ? void 0 : process$1.env) ?? {};
6652
7056
  const forcedWriteMode = parseWriteMode(env.DIMCODE_TUI_WRITE_MODE ?? env.VUE_TUI_WRITE_MODE);
6653
7057
  const forcedChunkSize = Number(env.DIMCODE_TUI_CHUNK_SIZE ?? env.VUE_TUI_CHUNK_SIZE);
6654
7058
  const chunkSize = Number.isFinite(forcedChunkSize) ? Math.max(512, Math.floor(forcedChunkSize)) : 8 * 1024;
@@ -6663,12 +7067,12 @@ function createStdoutRenderer(terminal, options) {
6663
7067
  if (Number.isFinite(n)) return Math.min(1, Math.max(.05, n));
6664
7068
  return .6;
6665
7069
  })();
6666
- const disableCursorPos = isGhostty && (process === null || process === void 0 || (_ref3 = process.env) === null || _ref3 === void 0 ? void 0 : _ref3.DIMCODE_DISABLE_CURSOR_POS) === "1";
6667
- const overlayTerminal = getPlaneTerminal(terminal, "overlay");
7070
+ const disableCursorPos = isGhostty && (process$1 === null || process$1 === void 0 || (_ref3 = process$1.env) === null || _ref3 === void 0 ? void 0 : _ref3.DIMCODE_DISABLE_CURSOR_POS) === "1";
7071
+ const isVscodeTerminal = String(env.TERM_PROGRAM ?? "").trim().toLowerCase() === "vscode" || "VSCODE_PID" in env || "VSCODE_IPC_HOOK_CLI" in env;
6668
7072
  const enableOsc8Links = (() => {
6669
7073
  if (!out.isTTY) return false;
6670
7074
  const raw = String(env.DIMCODE_TUI_LINKS ?? "").trim().toLowerCase();
6671
- if (!raw) return !(String(env.TERM_PROGRAM ?? "").trim().toLowerCase() === "vscode" || "VSCODE_PID" in env || "VSCODE_IPC_HOOK_CLI" in env);
7075
+ if (!raw) return !isVscodeTerminal;
6672
7076
  return !(raw === "0" || raw === "false" || raw === "no");
6673
7077
  })();
6674
7078
  let disposed = false;
@@ -6676,11 +7080,15 @@ function createStdoutRenderer(terminal, options) {
6676
7080
  let pendingRender = false;
6677
7081
  let renderTimer = null;
6678
7082
  let writeEmaMs = 0;
7083
+ let lastCursorX = null;
7084
+ let lastCursorY = null;
6679
7085
  let accumulatedAllRows = false;
6680
7086
  let accumulatedDirtyBits = null;
6681
7087
  let accumulatedDirtyCount = 0;
6682
7088
  let accumulatedDirtyMin = Number.POSITIVE_INFINITY;
6683
7089
  let accumulatedDirtyMax = -1;
7090
+ let accumulatedScrollOperations = null;
7091
+ let accumulatedSkipUnchangedDirtyRows = true;
6684
7092
  const MIN_FRAME_MS = !out.isTTY ? 0 : isGhostty ? 24 : 16;
6685
7093
  function clampCellToViewport(cell, size) {
6686
7094
  const maxX = Math.max(0, Math.floor(size.cols) - 1);
@@ -6697,25 +7105,43 @@ function createStdoutRenderer(terminal, options) {
6697
7105
  function resolveColorMode() {
6698
7106
  const opt = (options === null || options === void 0 ? void 0 : options.colorMode) ?? "auto";
6699
7107
  if (opt !== "auto") return opt;
6700
- const env$1 = (process === null || process === void 0 ? void 0 : process.env) ?? {};
7108
+ const env$1 = (process$1 === null || process$1 === void 0 ? void 0 : process$1.env) ?? {};
6701
7109
  return detectTerminalColorCapability({
6702
7110
  env: env$1,
6703
7111
  isTTY: Boolean(out.isTTY),
6704
- platform: String((process === null || process === void 0 ? void 0 : process.platform) ?? "")
7112
+ platform: String((process$1 === null || process$1 === void 0 ? void 0 : process$1.platform) ?? "")
6705
7113
  }).mode;
6706
7114
  }
6707
7115
  const colorMode = resolveColorMode();
6708
7116
  const enableDim = colorMode === "truecolor";
7117
+ const COLOR_INDEX = {
7118
+ black: 1,
7119
+ red: 2,
7120
+ green: 3,
7121
+ yellow: 4,
7122
+ blue: 5,
7123
+ magenta: 6,
7124
+ cyan: 7,
7125
+ white: 8,
7126
+ blackBright: 9,
7127
+ redBright: 10,
7128
+ greenBright: 11,
7129
+ yellowBright: 12,
7130
+ blueBright: 13,
7131
+ magentaBright: 14,
7132
+ cyanBright: 15,
7133
+ whiteBright: 16
7134
+ };
6709
7135
  let styleKeyCache$1 = new WeakMap();
6710
7136
  function styleKey$1(style) {
6711
7137
  const cached = styleKeyCache$1.get(style);
6712
- if (cached) return cached;
6713
- const key = `${style.fg ?? ""}|${style.bg ?? defaultBg}|${style.bold ? 1 : 0}|${enableDim && style.dim ? 1 : 0}|${style.italic ? 1 : 0}|${style.underline ? 1 : 0}|${style.inverse ? 1 : 0}|${style.href ?? ""}`;
7138
+ if (cached !== void 0) return cached;
7139
+ const key = (COLOR_INDEX[style.fg] ?? 0) | (COLOR_INDEX[style.bg ?? defaultBg] ?? 0) << 5 | (style.bold ? 1024 : 0) | (enableDim && style.dim ? 2048 : 0) | (style.italic ? 4096 : 0) | (style.underline ? 8192 : 0) | (style.inverse ? 16384 : 0) | (style.href ? 32768 : 0);
6714
7140
  styleKeyCache$1.set(style, key);
6715
7141
  return key;
6716
7142
  }
6717
7143
  function styleKeyFromParts(style) {
6718
- return `${style.fg ?? ""}|${style.bg ?? defaultBg}|${style.bold ? 1 : 0}|${enableDim && style.dim ? 1 : 0}|${style.italic ? 1 : 0}|${style.underline ? 1 : 0}|${style.inverse ? 1 : 0}|${style.href ?? ""}`;
7144
+ return (COLOR_INDEX[style.fg] ?? 0) | (COLOR_INDEX[style.bg ?? defaultBg] ?? 0) << 5 | (style.bold ? 1024 : 0) | (enableDim && style.dim ? 2048 : 0) | (style.italic ? 4096 : 0) | (style.underline ? 8192 : 0) | (style.inverse ? 16384 : 0) | (style.href ? 32768 : 0);
6719
7145
  }
6720
7146
  function openColor(fg) {
6721
7147
  if (!fg) return "";
@@ -6747,7 +7173,45 @@ function createStdoutRenderer(terminal, options) {
6747
7173
  return result;
6748
7174
  }
6749
7175
  let lastRenderedRows = 0;
6750
- let renderedRowCache = [];
7176
+ let fpCols = 0;
7177
+ let fpRows = 0;
7178
+ let currentFP = new Uint32Array(0);
7179
+ let prevFP = new Uint32Array(0);
7180
+ let fpPrevValid = false;
7181
+ let prevOverlayBlockedRows = [];
7182
+ let prevOverlayPartialRows = [];
7183
+ function ensureFingerprints(cols, rows) {
7184
+ if (cols === fpCols && rows === fpRows) return;
7185
+ fpCols = cols;
7186
+ fpRows = rows;
7187
+ const len = cols * rows;
7188
+ currentFP = new Uint32Array(len);
7189
+ prevFP = new Uint32Array(len);
7190
+ fpPrevValid = false;
7191
+ prevOverlayBlockedRows = [];
7192
+ prevOverlayPartialRows = [];
7193
+ }
7194
+ function charHash16(ch) {
7195
+ if (ch.length <= 1) return ch.charCodeAt(0) || 0;
7196
+ let h$1 = 33052;
7197
+ for (let i = 0; i < ch.length; i++) {
7198
+ h$1 ^= ch.charCodeAt(i);
7199
+ h$1 = h$1 * 257 & 65535;
7200
+ }
7201
+ return h$1;
7202
+ }
7203
+ function fingerprintRow(row, y, cols) {
7204
+ const rowFP = terminal.getRowFingerprints(y);
7205
+ if (rowFP && rowFP.length >= cols) {
7206
+ currentFP.set(rowFP.subarray(0, cols), y * fpCols);
7207
+ return;
7208
+ }
7209
+ const base = y * fpCols;
7210
+ for (let x = 0; x < cols; x++) {
7211
+ const cell = row[x];
7212
+ currentFP[base + x] = styleKey$1(cell.style) << 16 | charHash16(cell.ch);
7213
+ }
7214
+ }
6751
7215
  const rowCursorToCol1 = [];
6752
7216
  const rowClearToEol = [];
6753
7217
  const rowTextPartsScratch = [];
@@ -6765,13 +7229,6 @@ function createStdoutRenderer(terminal, options) {
6765
7229
  rowClearToEol[y] = `\u001B[${y + 1};1H\u001B[K`;
6766
7230
  }
6767
7231
  };
6768
- const ensureRenderedRowCache = (rows) => {
6769
- if (renderedRowCache.length > rows) {
6770
- renderedRowCache.length = rows;
6771
- return;
6772
- }
6773
- while (renderedRowCache.length < rows) renderedRowCache.push([]);
6774
- };
6775
7232
  const expandSpanStart = (row, startX) => {
6776
7233
  var _row$x;
6777
7234
  let x = Math.max(0, Math.min(row.length, startX));
@@ -6788,30 +7245,279 @@ function createStdoutRenderer(terminal, options) {
6788
7245
  }
6789
7246
  return x;
6790
7247
  };
6791
- const resolveChangedSpan = (row, prevRow) => {
6792
- if (!prevRow || prevRow.length !== row.length) return row.length ? {
7248
+ const resolveChangedSpan = (row, y, cols) => {
7249
+ if (!fpCols || y >= fpRows || !fpPrevValid) return row.length ? {
6793
7250
  startX: 0,
6794
7251
  endXExclusive: row.length
6795
7252
  } : null;
7253
+ const base = y * fpCols;
6796
7254
  let startX = -1;
6797
7255
  let endXExclusive = -1;
6798
- for (let x = 0; x < row.length; x++) {
6799
- if (row[x] === prevRow[x]) continue;
7256
+ for (let x = 0; x < cols; x++) {
7257
+ if (currentFP[base + x] === prevFP[base + x]) continue;
6800
7258
  if (startX === -1) startX = x;
6801
7259
  endXExclusive = x + 1;
6802
7260
  }
6803
- if (startX === -1 || endXExclusive === -1) return null;
6804
- const expandedStart = Math.min(expandSpanStart(row, startX), expandSpanStart(prevRow, startX));
6805
- const expandedEnd = Math.max(expandSpanEnd(row, endXExclusive), expandSpanEnd(prevRow, endXExclusive));
7261
+ if (startX === -1) return null;
7262
+ const expandedStart = expandSpanStart(row, startX);
7263
+ const expandedEnd = expandSpanEnd(row, endXExclusive);
6806
7264
  return expandedEnd > expandedStart ? {
6807
7265
  startX: expandedStart,
6808
7266
  endXExclusive: expandedEnd
6809
7267
  } : null;
6810
7268
  };
6811
- function overlayPlaneHasVisibleContent() {
6812
- const lines = overlayTerminal.snapshot().lines;
6813
- for (const line of lines) for (let i = 0; i < line.length; i++) if (line.charCodeAt(i) !== 32) return true;
7269
+ const resolveChangedSpanAgainstFill = (row, y, cols, fillFingerprint) => {
7270
+ if (!fpCols || y >= fpRows) return row.length ? {
7271
+ startX: 0,
7272
+ endXExclusive: row.length
7273
+ } : null;
7274
+ const base = y * fpCols;
7275
+ let startX = -1;
7276
+ let endXExclusive = -1;
7277
+ for (let x = 0; x < cols; x++) {
7278
+ if (currentFP[base + x] === fillFingerprint) continue;
7279
+ if (startX === -1) startX = x;
7280
+ endXExclusive = x + 1;
7281
+ }
7282
+ if (startX === -1) return null;
7283
+ const expandedStart = expandSpanStart(row, startX);
7284
+ const expandedEnd = expandSpanEnd(row, endXExclusive);
7285
+ return expandedEnd > expandedStart ? {
7286
+ startX: expandedStart,
7287
+ endXExclusive: expandedEnd
7288
+ } : null;
7289
+ };
7290
+ const resolveChangedSpanAgainstReference = (row, y, cols) => {
7291
+ if (!fpCols || y >= fpRows) return row.length ? {
7292
+ startX: 0,
7293
+ endXExclusive: row.length
7294
+ } : null;
7295
+ const rowFP = terminal.getRowFingerprints(y);
7296
+ const base = y * fpCols;
7297
+ let startX = -1;
7298
+ let endXExclusive = -1;
7299
+ for (let x = 0; x < cols; x++) {
7300
+ const fingerprint = rowFP && rowFP.length >= cols ? rowFP[x] : styleKey$1(row[x].style) << 16 | charHash16(row[x].ch);
7301
+ if (fingerprint === currentFP[base + x]) continue;
7302
+ if (startX === -1) startX = x;
7303
+ endXExclusive = x + 1;
7304
+ }
7305
+ if (startX === -1) return null;
7306
+ const expandedStart = expandSpanStart(row, startX);
7307
+ const expandedEnd = expandSpanEnd(row, endXExclusive);
7308
+ return expandedEnd > expandedStart ? {
7309
+ startX: expandedStart,
7310
+ endXExclusive: expandedEnd
7311
+ } : null;
7312
+ };
7313
+ const rowMatchesPreviousFrame = (y, cols) => {
7314
+ if (!fpCols || y < 0 || y >= fpRows || !fpPrevValid) return false;
7315
+ const rowFP = terminal.getRowFingerprints(y);
7316
+ const row = rowFP && rowFP.length >= cols ? null : terminal.getRow(y);
7317
+ const base = y * fpCols;
7318
+ for (let x = 0; x < cols; x++) {
7319
+ const fingerprint = rowFP && rowFP.length >= cols ? rowFP[x] : styleKey$1(row[x].style) << 16 | charHash16(row[x].ch);
7320
+ if (fingerprint !== prevFP[base + x]) return false;
7321
+ }
7322
+ return true;
7323
+ };
7324
+ const rowHasNonDefaultBlankCell = (source, y, cols) => {
7325
+ if (!fpCols || y < 0 || y >= fpRows) return false;
7326
+ const blankFP = styleKeyFromParts({ bg: defaultBg }) << 16 | charHash16(" ");
7327
+ const base = y * fpCols;
7328
+ const limit = Math.min(cols, fpCols);
7329
+ for (let x = 0; x < limit; x++) if (source[base + x] !== blankFP) return true;
6814
7330
  return false;
7331
+ };
7332
+ function overlayPlaneCoverageRows(totalRows) {
7333
+ const blockedRows = [];
7334
+ const partialRows = [];
7335
+ for (let y = 0; y < totalRows; y++) {
7336
+ const kind = getPlaneRowCoverageKind(terminal, "overlay", y);
7337
+ if (!kind) continue;
7338
+ blockedRows.push(y);
7339
+ if (kind === 1) partialRows.push(y);
7340
+ }
7341
+ return {
7342
+ blockedRows,
7343
+ partialRows
7344
+ };
7345
+ }
7346
+ const enableScrollRegions = (() => {
7347
+ const raw = String(env.DIMCODE_TUI_SCROLL_REGIONS ?? "").trim();
7348
+ if (raw === "0" || raw === "false") return false;
7349
+ if (isGhostty || isVscodeTerminal) return false;
7350
+ return out.isTTY !== false;
7351
+ })();
7352
+ function prepareExplicitScrollOperations(operations, blockedRows) {
7353
+ if (!operations.length || !(blockedRows === null || blockedRows === void 0 ? void 0 : blockedRows.size)) return {
7354
+ operations,
7355
+ hiddenRows: new Set(),
7356
+ trimmed: false,
7357
+ blockedInterior: false
7358
+ };
7359
+ const hiddenRows = new Set();
7360
+ const prepared = [];
7361
+ let trimmed = false;
7362
+ for (const op of operations) {
7363
+ let startY = op.startY;
7364
+ let endY = op.endY;
7365
+ while (startY < endY && blockedRows.has(startY)) {
7366
+ hiddenRows.add(startY);
7367
+ startY++;
7368
+ trimmed = true;
7369
+ }
7370
+ while (endY > startY && blockedRows.has(endY - 1)) {
7371
+ hiddenRows.add(endY - 1);
7372
+ endY--;
7373
+ trimmed = true;
7374
+ }
7375
+ for (let y = startY; y < endY; y++) {
7376
+ if (!blockedRows.has(y)) continue;
7377
+ return {
7378
+ operations: null,
7379
+ hiddenRows,
7380
+ trimmed,
7381
+ blockedInterior: true
7382
+ };
7383
+ }
7384
+ if (endY <= startY || Math.abs(op.delta) >= endY - startY) return {
7385
+ operations: null,
7386
+ hiddenRows,
7387
+ trimmed,
7388
+ blockedInterior: false
7389
+ };
7390
+ prepared.push(startY === op.startY && endY === op.endY ? op : {
7391
+ startY,
7392
+ endY,
7393
+ delta: op.delta
7394
+ });
7395
+ }
7396
+ return {
7397
+ operations: prepared,
7398
+ hiddenRows,
7399
+ trimmed,
7400
+ blockedInterior: false
7401
+ };
7402
+ }
7403
+ /**
7404
+ * Detect if the current frame represents a pure vertical scroll by comparing
7405
+ * current fingerprints against previous fingerprints with various shift offsets.
7406
+ * Returns the shift delta and which rows are newly revealed, or null if no
7407
+ * scroll pattern is detected.
7408
+ */
7409
+ function largestDirtyBand(rows, blockedRows) {
7410
+ if (rows.length === 0) return null;
7411
+ const blocked = (blockedRows === null || blockedRows === void 0 ? void 0 : blockedRows.length) ? new Set(blockedRows) : null;
7412
+ let bestStart = -1;
7413
+ let bestEnd = -1;
7414
+ let bandStart = -1;
7415
+ let prev = -1;
7416
+ for (let i = 0; i < rows.length; i++) {
7417
+ const y = rows[i];
7418
+ if (blocked === null || blocked === void 0 ? void 0 : blocked.has(y)) {
7419
+ if (bandStart !== -1 && prev + 1 - bandStart > bestEnd - bestStart) {
7420
+ bestStart = bandStart;
7421
+ bestEnd = prev + 1;
7422
+ }
7423
+ bandStart = -1;
7424
+ prev = -1;
7425
+ continue;
7426
+ }
7427
+ if (bandStart === -1) {
7428
+ bandStart = y;
7429
+ prev = y;
7430
+ if (bestStart === -1) {
7431
+ bestStart = y;
7432
+ bestEnd = y + 1;
7433
+ }
7434
+ continue;
7435
+ }
7436
+ if (y === prev + 1) {
7437
+ prev = y;
7438
+ continue;
7439
+ }
7440
+ if (prev + 1 - bandStart > bestEnd - bestStart) {
7441
+ bestStart = bandStart;
7442
+ bestEnd = prev + 1;
7443
+ }
7444
+ bandStart = y;
7445
+ prev = y;
7446
+ }
7447
+ if (bandStart !== -1 && prev + 1 - bandStart > bestEnd - bestStart) {
7448
+ bestStart = bandStart;
7449
+ bestEnd = prev + 1;
7450
+ }
7451
+ if (bestStart === -1) return null;
7452
+ const outsideRows = [];
7453
+ for (const y of rows) if ((blocked === null || blocked === void 0 ? void 0 : blocked.has(y)) || y < bestStart || y >= bestEnd) outsideRows.push(y);
7454
+ return {
7455
+ start: bestStart,
7456
+ end: bestEnd,
7457
+ outsideRows
7458
+ };
7459
+ }
7460
+ function detectScrollShift(cols, dirtyRows, blockedRows) {
7461
+ if (!fpPrevValid || !fpCols || cols !== fpCols) return null;
7462
+ const band = largestDirtyBand(dirtyRows, blockedRows);
7463
+ if (!band || band.end - band.start < 3) return null;
7464
+ const maxDelta = Math.min(12, Math.max(5, band.end - band.start - 1));
7465
+ for (let delta = -maxDelta; delta <= maxDelta; delta++) {
7466
+ if (delta === 0) continue;
7467
+ let matches = 0;
7468
+ let checked = 0;
7469
+ let mismatches = 0;
7470
+ for (let y = band.start; y < band.end; y++) {
7471
+ const srcY = y + delta;
7472
+ if (srcY < band.start || srcY >= band.end) continue;
7473
+ const informative = rowHasNonDefaultBlankCell(currentFP, y, cols) || rowHasNonDefaultBlankCell(prevFP, srcY, cols);
7474
+ if (!informative) continue;
7475
+ checked++;
7476
+ const curBase = y * fpCols;
7477
+ const prevBase = srcY * fpCols;
7478
+ let rowMatch = true;
7479
+ for (let x = 0; x < cols; x++) if (currentFP[curBase + x] !== prevFP[prevBase + x]) {
7480
+ rowMatch = false;
7481
+ break;
7482
+ }
7483
+ if (rowMatch) matches++;
7484
+ else mismatches++;
7485
+ }
7486
+ if (checked > 0 && matches >= 3 && mismatches <= Math.ceil(checked * .2)) {
7487
+ const absDelta = Math.abs(delta);
7488
+ let newRowStart;
7489
+ let newRowEnd;
7490
+ if (delta > 0) {
7491
+ newRowStart = Math.max(band.start, band.end - absDelta);
7492
+ newRowEnd = band.end;
7493
+ } else {
7494
+ newRowStart = band.start;
7495
+ newRowEnd = Math.min(band.end, band.start + absDelta);
7496
+ }
7497
+ const extraDirty = [...band.outsideRows];
7498
+ for (let y = band.start; y < band.end; y++) {
7499
+ const srcY = y + delta;
7500
+ if (srcY < band.start || srcY >= band.end) continue;
7501
+ const curBase = y * fpCols;
7502
+ const prevBase = srcY * fpCols;
7503
+ let rowMatch = true;
7504
+ for (let x = 0; x < cols; x++) if (currentFP[curBase + x] !== prevFP[prevBase + x]) {
7505
+ rowMatch = false;
7506
+ break;
7507
+ }
7508
+ if (!rowMatch) extraDirty.push(y);
7509
+ }
7510
+ return {
7511
+ regionStart: band.start,
7512
+ regionEnd: band.end,
7513
+ delta,
7514
+ newRowStart,
7515
+ newRowEnd,
7516
+ extraDirtyRows: extraDirty
7517
+ };
7518
+ }
7519
+ }
7520
+ return null;
6815
7521
  }
6816
7522
  /**
6817
7523
  * Write data in chunks to avoid overwhelming terminal buffers.
@@ -6849,7 +7555,7 @@ function createStdoutRenderer(terminal, options) {
6849
7555
  * Internal function that actually renders the frame to stdout.
6850
7556
  * When dirtyRows is provided, only those rows are repainted.
6851
7557
  */
6852
- function doRender(dirtyRows) {
7558
+ function doRender(dirtyRows, scrollOperations, skipUnchangedDirtyRows = false) {
6853
7559
  if (isDebugEnabled()) getDebugLog().render(`doRender() START: dirtyRows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) ?? "null"}`);
6854
7560
  if (disposed) return;
6855
7561
  pendingRender = false;
@@ -6858,15 +7564,50 @@ function createStdoutRenderer(terminal, options) {
6858
7564
  accumulatedDirtyCount = 0;
6859
7565
  accumulatedDirtyMin = Number.POSITIVE_INFINITY;
6860
7566
  accumulatedDirtyMax = -1;
7567
+ accumulatedScrollOperations = null;
7568
+ accumulatedSkipUnchangedDirtyRows = true;
6861
7569
  lastFrameTime = Date.now();
6862
7570
  const renderStart = performance.now();
6863
7571
  const size = terminal.size();
6864
7572
  ensureRowEscapes(Math.max(size.rows, lastRenderedRows));
6865
- ensureRenderedRowCache(size.rows);
7573
+ ensureFingerprints(size.cols, size.rows);
6866
7574
  const bgSeq = openBg(defaultBg);
6867
7575
  const bgOnlyStyle = { bg: defaultBg };
6868
7576
  const bgKey = styleKeyFromParts({ bg: defaultBg });
7577
+ const blankFP = bgKey << 16 | charHash16(" ");
7578
+ const cellFingerprint = (cell) => styleKey$1(cell.style) << 16 | charHash16(cell.ch);
7579
+ const lastOccupiedColumnInRow = (row, cols) => {
7580
+ const limit = Math.min(row.length, cols);
7581
+ for (let x = limit - 1; x >= 0; x--) if (cellFingerprint(row[x]) !== blankFP) return x;
7582
+ return -1;
7583
+ };
7584
+ const lastOccupiedColumnInReference = (source, y, cols) => {
7585
+ if (!fpCols || y < 0 || y >= fpRows) return -1;
7586
+ const limit = Math.min(fpCols, cols);
7587
+ const base = y * fpCols;
7588
+ for (let x = limit - 1; x >= 0; x--) if (source[base + x] !== blankFP) return x;
7589
+ return -1;
7590
+ };
7591
+ const shouldRewriteRowTail = (row, y, cols, reference) => lastOccupiedColumnInRow(row, cols) < lastOccupiedColumnInReference(reference, y, cols);
6869
7592
  let dirtySorted = true;
7593
+ const normalizedScrollOperations = (() => {
7594
+ if (!(scrollOperations === null || scrollOperations === void 0 ? void 0 : scrollOperations.length)) return null;
7595
+ const outOps = [];
7596
+ for (const op of scrollOperations) {
7597
+ const startY = Math.max(0, Math.min(size.rows, Math.floor(op.startY)));
7598
+ const endY = Math.max(0, Math.min(size.rows, Math.floor(op.endY)));
7599
+ const delta = Math.trunc(op.delta);
7600
+ if (endY <= startY || delta === 0 || Math.abs(delta) >= endY - startY) continue;
7601
+ outOps.push({
7602
+ startY,
7603
+ endY,
7604
+ delta
7605
+ });
7606
+ }
7607
+ if (!outOps.length) return null;
7608
+ outOps.sort((a, b) => a.startY - b.startY);
7609
+ return outOps;
7610
+ })();
6870
7611
  let rowsToRender = (() => {
6871
7612
  if (!dirtyRows || dirtyRows.length === 0) return null;
6872
7613
  const outRows = [];
@@ -6887,11 +7628,13 @@ function createStdoutRenderer(terminal, options) {
6887
7628
  if (!sorted) outRows.sort((a, b) => a - b);
6888
7629
  return outRows;
6889
7630
  })();
6890
- if (rowsToRender && rowsToRender.length >= size.rows * dirtyFullThreshold) rowsToRender = null;
6891
7631
  const frameParts = [];
6892
7632
  frameParts.push(!isGhostty && useSyncOutput ? SYNC_START : "");
6893
7633
  frameParts.push("\x1B[?7l");
6894
7634
  frameParts.push(SGR_RESET, bgSeq);
7635
+ let lastRenderedY = -1;
7636
+ let lastRenderWasFullRow = false;
7637
+ let hasFrameOutput = false;
6895
7638
  let activeStyleKey = null;
6896
7639
  let activeStyle = {
6897
7640
  fg: null,
@@ -6942,11 +7685,10 @@ function createStdoutRenderer(terminal, options) {
6942
7685
  const renderRow$1 = (y, row, startX = 0, endXExclusive = row.length, clearToEol = true) => {
6943
7686
  const spanStart = Math.max(0, Math.min(row.length, Math.floor(startX)));
6944
7687
  const spanEnd = Math.max(spanStart, Math.min(row.length, Math.floor(endXExclusive)));
6945
- if (spanStart >= spanEnd && !clearToEol) {
6946
- renderedRowCache[y] = row.slice();
6947
- return;
6948
- }
6949
- if (!disableCursorPos) frameParts.push(spanStart === 0 ? rowCursorToCol1[y] : `\u001B[${y + 1};${spanStart + 1}H`);
7688
+ if (spanStart >= spanEnd && !clearToEol) return;
7689
+ hasFrameOutput = true;
7690
+ if (!disableCursorPos) if (spanStart === 0 && lastRenderWasFullRow && y === lastRenderedY + 1) frameParts.push("\r\n");
7691
+ else frameParts.push(spanStart === 0 ? rowCursorToCol1[y] : `\u001B[${y + 1};${spanStart + 1}H`);
6950
7692
  let currentKey = null;
6951
7693
  let currentStyle = null;
6952
7694
  const currentTextParts = rowTextPartsScratch;
@@ -6964,7 +7706,7 @@ function createStdoutRenderer(terminal, options) {
6964
7706
  if (needsWideCursorFix(cell, ch)) currentTextParts.push(`\u001B[${y + 1};${x + 1 + (cell.width ?? 1)}H`);
6965
7707
  continue;
6966
7708
  }
6967
- if (key === currentKey) {
7709
+ if (key === currentKey && !(key & 32768 && nextStyle.href !== (currentStyle === null || currentStyle === void 0 ? void 0 : currentStyle.href))) {
6968
7710
  currentTextParts.push(ch);
6969
7711
  if (needsWideCursorFix(cell, ch)) currentTextParts.push(`\u001B[${y + 1};${x + 1 + (cell.width ?? 1)}H`);
6970
7712
  continue;
@@ -6985,29 +7727,233 @@ function createStdoutRenderer(terminal, options) {
6985
7727
  if (activeStyleKey !== bgKey) emitStyle(bgOnlyStyle, bgKey);
6986
7728
  frameParts.push("\x1B[K");
6987
7729
  }
6988
- renderedRowCache[y] = row.slice();
7730
+ lastRenderedY = y;
7731
+ lastRenderWasFullRow = spanStart === 0 && clearToEol;
6989
7732
  };
6990
- if (!rowsToRender) {
7733
+ const overlayCoverage = overlayPlaneCoverageRows(size.rows);
7734
+ const overlayRows = Array.from(new Set([...overlayCoverage.blockedRows, ...prevOverlayBlockedRows])).sort((a, b) => a - b);
7735
+ const overlayRowSet = overlayRows.length ? new Set(overlayRows) : null;
7736
+ const overlayPartialRows = Array.from(new Set([...overlayCoverage.partialRows, ...prevOverlayPartialRows])).sort((a, b) => a - b);
7737
+ const overlayPartialRowSet = overlayPartialRows.length ? new Set(overlayPartialRows) : null;
7738
+ let explicitScrollOperations = normalizedScrollOperations;
7739
+ let hiddenExplicitDirtyRows = null;
7740
+ let allowInferredScrollRegions = true;
7741
+ if (explicitScrollOperations && overlayRows.length) {
7742
+ const preparedExplicit = prepareExplicitScrollOperations(explicitScrollOperations, overlayRowSet);
7743
+ hiddenExplicitDirtyRows = preparedExplicit.hiddenRows.size ? preparedExplicit.hiddenRows : null;
7744
+ if (preparedExplicit.operations) {
7745
+ explicitScrollOperations = preparedExplicit.operations;
7746
+ 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(", ")}`);
7747
+ } else {
7748
+ allowInferredScrollRegions = false;
7749
+ const expandedRows = new Set(rowsToRender ?? []);
7750
+ for (const op of explicitScrollOperations) for (let y = op.startY; y < op.endY; y++) expandedRows.add(y);
7751
+ rowsToRender = Array.from(expandedRows).sort((a, b) => a - b);
7752
+ dirtySorted = true;
7753
+ explicitScrollOperations = null;
7754
+ hiddenExplicitDirtyRows = null;
7755
+ if (isDebugEnabled()) getDebugLog().render(preparedExplicit.blockedInterior ? " Explicit scroll ops split by interior overlay rows; falling back to region repaint" : " Explicit scroll ops could not be clipped safely; falling back to region repaint");
7756
+ }
7757
+ }
7758
+ const scrollRowsCandidate = rowsToRender;
7759
+ if (explicitScrollOperations && rowsToRender && (!enableScrollRegions || !fpPrevValid)) {
7760
+ const expandedRows = new Set(rowsToRender);
7761
+ for (const op of explicitScrollOperations) for (let y = op.startY; y < op.endY; y++) {
7762
+ if (hiddenExplicitDirtyRows === null || hiddenExplicitDirtyRows === void 0 ? void 0 : hiddenExplicitDirtyRows.has(y)) continue;
7763
+ expandedRows.add(y);
7764
+ }
7765
+ rowsToRender = Array.from(expandedRows).sort((a, b) => a - b);
7766
+ dirtySorted = true;
7767
+ explicitScrollOperations = null;
7768
+ hiddenExplicitDirtyRows = null;
7769
+ 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");
7770
+ }
7771
+ const denseDirtyRows = Boolean(rowsToRender && rowsToRender.length >= size.rows * dirtyFullThreshold);
7772
+ if (denseDirtyRows) rowsToRender = null;
7773
+ let profiledRowCount = rowsToRender ? rowsToRender.length : size.rows;
7774
+ const useScrollRegions = enableScrollRegions && fpPrevValid && scrollRowsCandidate && scrollRowsCandidate.length >= 3 && allowInferredScrollRegions;
7775
+ let scrollHandled = false;
7776
+ if (enableScrollRegions && explicitScrollOperations && scrollRowsCandidate && fpPrevValid) {
7777
+ if (currentFP.length === prevFP.length) currentFP.set(prevFP);
7778
+ const insertedRows = new Set();
7779
+ const explicitRowsToRender = new Set();
7780
+ for (const y of scrollRowsCandidate) {
7781
+ if (hiddenExplicitDirtyRows === null || hiddenExplicitDirtyRows === void 0 ? void 0 : hiddenExplicitDirtyRows.has(y)) continue;
7782
+ explicitRowsToRender.add(y);
7783
+ }
7784
+ if (isDebugEnabled()) getDebugLog().render(` Explicit scroll ops: ${explicitScrollOperations.map((op) => `${op.startY}-${op.endY - 1}:${op.delta}`).join(", ")}`);
7785
+ for (const op of explicitScrollOperations) {
7786
+ hasFrameOutput = true;
7787
+ frameParts.push(`\u001B[${op.startY + 1};${op.endY}r`);
7788
+ if (op.delta > 0) {
7789
+ frameParts.push(`\u001B[${op.endY};1H`);
7790
+ frameParts.push(`\u001B[${op.delta}S`);
7791
+ for (let y = op.startY; y < op.endY - op.delta; y++) {
7792
+ const dstBase = y * fpCols;
7793
+ const srcBase = (y + op.delta) * fpCols;
7794
+ for (let x = 0; x < size.cols; x++) currentFP[dstBase + x] = prevFP[srcBase + x];
7795
+ }
7796
+ for (let y = op.endY - op.delta; y < op.endY; y++) {
7797
+ const base = y * fpCols;
7798
+ insertedRows.add(y);
7799
+ explicitRowsToRender.add(y);
7800
+ for (let x = 0; x < size.cols; x++) currentFP[base + x] = blankFP;
7801
+ }
7802
+ } else {
7803
+ const absDelta = -op.delta;
7804
+ frameParts.push(`\u001B[${op.startY + 1};1H`);
7805
+ frameParts.push(`\u001B[${absDelta}T`);
7806
+ for (let y = op.endY - 1; y >= op.startY + absDelta; y--) {
7807
+ const dstBase = y * fpCols;
7808
+ const srcBase = (y - absDelta) * fpCols;
7809
+ for (let x = 0; x < size.cols; x++) currentFP[dstBase + x] = prevFP[srcBase + x];
7810
+ }
7811
+ for (let y = op.startY; y < op.startY + absDelta; y++) {
7812
+ const base = y * fpCols;
7813
+ insertedRows.add(y);
7814
+ explicitRowsToRender.add(y);
7815
+ for (let x = 0; x < size.cols; x++) currentFP[base + x] = blankFP;
7816
+ }
7817
+ }
7818
+ frameParts.push("\x1B[r");
7819
+ }
7820
+ const explicitDirtyRows = Array.from(explicitRowsToRender).sort((a, b) => a - b);
7821
+ const overlayDirtyRows = overlayRowSet ? explicitDirtyRows.filter((y) => overlayRowSet.has(y)) : [];
7822
+ const paintedExplicitRows = [];
7823
+ for (const y of explicitDirtyRows) {
7824
+ const row = terminal.getRow(y);
7825
+ const overlayRow = overlayRowSet === null || overlayRowSet === void 0 ? void 0 : overlayRowSet.has(y);
7826
+ if (overlayRow) {
7827
+ fingerprintRow(row, y, size.cols);
7828
+ renderRow$1(y, row);
7829
+ paintedExplicitRows.push(y);
7830
+ continue;
7831
+ }
7832
+ const span = (() => {
7833
+ if (insertedRows.has(y)) {
7834
+ fingerprintRow(row, y, size.cols);
7835
+ return resolveChangedSpanAgainstFill(row, y, size.cols, blankFP);
7836
+ }
7837
+ return resolveChangedSpanAgainstReference(row, y, size.cols);
7838
+ })();
7839
+ if (!span) {
7840
+ if (!skipUnchangedDirtyRows) fingerprintRow(row, y, size.cols);
7841
+ if (!skipUnchangedDirtyRows) {
7842
+ renderRow$1(y, row);
7843
+ paintedExplicitRows.push(y);
7844
+ }
7845
+ continue;
7846
+ }
7847
+ const rewriteTail = !insertedRows.has(y) && shouldRewriteRowTail(row, y, size.cols, currentFP);
7848
+ fingerprintRow(row, y, size.cols);
7849
+ renderRow$1(y, row, span.startX, rewriteTail ? row.length : span.endXExclusive, rewriteTail || span.endXExclusive >= row.length);
7850
+ paintedExplicitRows.push(y);
7851
+ }
7852
+ if (isDebugEnabled() && overlayDirtyRows.length) getDebugLog().render(` Explicit scroll ops overlay rows: [${overlayDirtyRows.join(", ")}]`);
7853
+ profiledRowCount = explicitDirtyRows.length;
7854
+ scrollHandled = true;
7855
+ } else if (useScrollRegions && scrollRowsCandidate) {
7856
+ for (let y = 0; y < size.rows; y++) fingerprintRow(terminal.getRow(y), y, size.cols);
7857
+ const shift = detectScrollShift(size.cols, scrollRowsCandidate, overlayRows);
7858
+ if (shift) {
7859
+ if (currentFP.length === prevFP.length) currentFP.set(prevFP);
7860
+ hasFrameOutput = true;
7861
+ if (isDebugEnabled()) getDebugLog().render(` Scroll region: delta=${shift.delta}, region=${shift.regionStart}-${shift.regionEnd - 1}, new rows=${shift.newRowStart}-${shift.newRowEnd - 1}`);
7862
+ frameParts.push(`\u001B[${shift.regionStart + 1};${shift.regionEnd}r`);
7863
+ if (shift.delta > 0) {
7864
+ frameParts.push(`\u001B[${shift.regionEnd};1H`);
7865
+ frameParts.push(`\u001B[${shift.delta}S`);
7866
+ } else {
7867
+ frameParts.push(`\u001B[${shift.regionStart + 1};1H`);
7868
+ frameParts.push(`\u001B[${-shift.delta}T`);
7869
+ }
7870
+ frameParts.push("\x1B[r");
7871
+ if (shift.delta > 0) {
7872
+ for (let y = shift.regionStart; y < shift.regionEnd - shift.delta; y++) {
7873
+ const dstBase = y * fpCols;
7874
+ const srcBase = (y + shift.delta) * fpCols;
7875
+ for (let x = 0; x < size.cols; x++) currentFP[dstBase + x] = prevFP[srcBase + x];
7876
+ }
7877
+ for (let y = shift.regionEnd - shift.delta; y < shift.regionEnd; y++) {
7878
+ const base = y * fpCols;
7879
+ for (let x = 0; x < size.cols; x++) currentFP[base + x] = blankFP;
7880
+ }
7881
+ } else {
7882
+ const absDelta = -shift.delta;
7883
+ for (let y = shift.regionEnd - 1; y >= shift.regionStart + absDelta; y--) {
7884
+ const dstBase = y * fpCols;
7885
+ const srcBase = (y - absDelta) * fpCols;
7886
+ for (let x = 0; x < size.cols; x++) currentFP[dstBase + x] = prevFP[srcBase + x];
7887
+ }
7888
+ for (let y = shift.regionStart; y < shift.regionStart + absDelta; y++) {
7889
+ const base = y * fpCols;
7890
+ for (let x = 0; x < size.cols; x++) currentFP[base + x] = blankFP;
7891
+ }
7892
+ }
7893
+ for (let y = shift.newRowStart; y < shift.newRowEnd; y++) {
7894
+ const row = terminal.getRow(y);
7895
+ fingerprintRow(row, y, size.cols);
7896
+ renderRow$1(y, row);
7897
+ }
7898
+ const extraDirtyRows = overlayRowSet ? shift.extraDirtyRows.filter((y) => !overlayRowSet.has(y) || Boolean(overlayPartialRowSet === null || overlayPartialRowSet === void 0 ? void 0 : overlayPartialRowSet.has(y))) : shift.extraDirtyRows;
7899
+ for (const y of extraDirtyRows) {
7900
+ const row = terminal.getRow(y);
7901
+ const overlayRow = overlayRowSet === null || overlayRowSet === void 0 ? void 0 : overlayRowSet.has(y);
7902
+ if (overlayRow) {
7903
+ renderRow$1(y, row);
7904
+ fingerprintRow(row, y, size.cols);
7905
+ continue;
7906
+ }
7907
+ const span = resolveChangedSpanAgainstReference(row, y, size.cols);
7908
+ if (!span) {
7909
+ if (!skipUnchangedDirtyRows) {
7910
+ fingerprintRow(row, y, size.cols);
7911
+ renderRow$1(y, row);
7912
+ }
7913
+ continue;
7914
+ }
7915
+ const rewriteTail = shouldRewriteRowTail(row, y, size.cols, currentFP);
7916
+ renderRow$1(y, row, span.startX, rewriteTail ? row.length : span.endXExclusive, rewriteTail || span.endXExclusive >= row.length);
7917
+ fingerprintRow(row, y, size.cols);
7918
+ }
7919
+ for (let y = shift.newRowStart; y < shift.newRowEnd; y++) fingerprintRow(terminal.getRow(y), y, size.cols);
7920
+ const renderedRows = new Set();
7921
+ for (let y = shift.newRowStart; y < shift.newRowEnd; y++) renderedRows.add(y);
7922
+ for (const y of extraDirtyRows) renderedRows.add(y);
7923
+ profiledRowCount = renderedRows.size;
7924
+ scrollHandled = true;
7925
+ }
7926
+ }
7927
+ if (!scrollHandled && denseDirtyRows && scrollRowsCandidate && useScrollRegions) {
7928
+ rowsToRender = scrollRowsCandidate;
7929
+ profiledRowCount = rowsToRender.length;
7930
+ if (isDebugEnabled()) getDebugLog().render(` Dense dirty rows fell back to partial render: ${rowsToRender.length} rows`);
7931
+ }
7932
+ if (scrollHandled) {} else if (!rowsToRender) {
6991
7933
  if (isDebugEnabled()) getDebugLog().render(` Full render: ${size.rows} rows`);
6992
7934
  for (let y = 0; y < size.rows; y++) {
6993
7935
  const row = terminal.getRow(y);
7936
+ fingerprintRow(row, y, size.cols);
6994
7937
  renderRow$1(y, row);
6995
7938
  }
6996
7939
  } else {
6997
- const repaintFullDirtyRows = overlayPlaneHasVisibleContent();
6998
- if (isDebugEnabled()) getDebugLog().render(` Partial render: ${rowsToRender.length} dirty rows: [${rowsToRender.join(", ")}]${repaintFullDirtyRows ? " (overlay-full-row)" : ""}`);
7940
+ const overlayDirtyRows = overlayRowSet ? rowsToRender.filter((y) => overlayRowSet.has(y)) : [];
7941
+ if (isDebugEnabled()) getDebugLog().render(` Partial render: ${rowsToRender.length} dirty rows: [${rowsToRender.join(", ")}]${overlayDirtyRows.length ? ` (overlay-full-row: [${overlayDirtyRows.join(", ")}])` : ""}`);
6999
7942
  for (const y of rowsToRender) {
7000
7943
  const row = terminal.getRow(y);
7001
- if (repaintFullDirtyRows) {
7944
+ fingerprintRow(row, y, size.cols);
7945
+ const overlayRow = overlayRowSet === null || overlayRowSet === void 0 ? void 0 : overlayRowSet.has(y);
7946
+ if (overlayRow) {
7002
7947
  renderRow$1(y, row);
7003
7948
  continue;
7004
7949
  }
7005
- const span = resolveChangedSpan(row, renderedRowCache[y]);
7950
+ const span = resolveChangedSpan(row, y, size.cols);
7006
7951
  if (!span) {
7007
- renderRow$1(y, row);
7952
+ if (!skipUnchangedDirtyRows) renderRow$1(y, row);
7008
7953
  continue;
7009
7954
  }
7010
- renderRow$1(y, row, span.startX, span.endXExclusive, span.endXExclusive >= row.length);
7955
+ const rewriteTail = shouldRewriteRowTail(row, y, size.cols, prevFP);
7956
+ renderRow$1(y, row, span.startX, rewriteTail ? row.length : span.endXExclusive, rewriteTail || span.endXExclusive >= row.length);
7011
7957
  }
7012
7958
  }
7013
7959
  if (!rowsToRender && lastRenderedRows > size.rows) {
@@ -7016,17 +7962,37 @@ function createStdoutRenderer(terminal, options) {
7016
7962
  frameParts.push(SGR_RESET, bgSeq);
7017
7963
  activeStyleKey = bgKey;
7018
7964
  }
7019
- for (let i = 0; i < extraRows; i++) frameParts.push(rowClearToEol[size.rows + i]);
7965
+ for (let i = 0; i < extraRows; i++) {
7966
+ hasFrameOutput = true;
7967
+ frameParts.push(rowClearToEol[size.rows + i]);
7968
+ }
7020
7969
  }
7021
7970
  if (!rowsToRender) lastRenderedRows = size.rows;
7022
- if (!rowsToRender || renderedRowCache.length !== size.rows) renderedRowCache.length = size.rows;
7971
+ const tmpFP = prevFP;
7972
+ prevFP = currentFP;
7973
+ currentFP = tmpFP;
7974
+ fpPrevValid = true;
7975
+ prevOverlayBlockedRows = overlayCoverage.blockedRows;
7976
+ prevOverlayPartialRows = overlayCoverage.partialRows;
7977
+ let emittedCursorPos = null;
7023
7978
  if (getImeAnchor) {
7024
7979
  const anchor = getImeAnchor();
7025
7980
  if (anchor) {
7026
7981
  const { x, y } = clampCellToViewport(anchor, size);
7027
- frameParts.push(`\u001B[${y + 1};${x + 1}H`);
7982
+ if (hasFrameOutput || x !== lastCursorX || y !== lastCursorY) {
7983
+ hasFrameOutput = true;
7984
+ emittedCursorPos = {
7985
+ x,
7986
+ y
7987
+ };
7988
+ frameParts.push(`\u001B[${y + 1};${x + 1}H`);
7989
+ }
7028
7990
  }
7029
7991
  }
7992
+ if (!hasFrameOutput) {
7993
+ if (isDebugEnabled()) getDebugLog().render(" No-op frame skipped");
7994
+ return;
7995
+ }
7030
7996
  if (enableOsc8Links && activeStyle.href) frameParts.push(OSC8_CLOSE);
7031
7997
  frameParts.push(SGR_RESET);
7032
7998
  frameParts.push("\x1B[?7h");
@@ -7034,9 +8000,9 @@ function createStdoutRenderer(terminal, options) {
7034
8000
  const frame = frameParts.join("");
7035
8001
  if (profiler) profiler.recordRender({
7036
8002
  durationMs: profiler.now() - renderStart,
7037
- rows: rowsToRender ? rowsToRender.length : size.rows,
8003
+ rows: profiledRowCount,
7038
8004
  nodes: 0,
7039
- fullRepaint: !rowsToRender,
8005
+ fullRepaint: !rowsToRender && !scrollHandled,
7040
8006
  sorted: dirtySorted
7041
8007
  });
7042
8008
  if (isDebugEnabled()) {
@@ -7134,6 +8100,13 @@ function createStdoutRenderer(terminal, options) {
7134
8100
  {
7135
8101
  const writeMs = performance.now() - writeStart;
7136
8102
  writeEmaMs = writeEmaMs === 0 ? writeMs : writeEmaMs * .85 + writeMs * .15;
8103
+ if (emittedCursorPos) {
8104
+ lastCursorX = emittedCursorPos.x;
8105
+ lastCursorY = emittedCursorPos.y;
8106
+ } else {
8107
+ lastCursorX = null;
8108
+ lastCursorY = null;
8109
+ }
7137
8110
  recordStdoutFrame({
7138
8111
  at: Date.now(),
7139
8112
  bytes: frame.length,
@@ -7158,9 +8131,20 @@ function createStdoutRenderer(terminal, options) {
7158
8131
  * Prevents excessive renders that cause flickering in Bun binaries.
7159
8132
  * Accumulates dirty rows while waiting for next frame to ensure no updates are lost.
7160
8133
  */
7161
- function render(dirtyRows) {
8134
+ function render(dirtyRows, sync, scrollOperations, skipUnchangedDirtyRows = false) {
7162
8135
  if (disposed) return;
7163
8136
  if (isDebugEnabled()) getDebugLog().render(`render() called: dirtyRows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) ?? "null"}, pending=${pendingRender}, elapsed=${Date.now() - lastFrameTime}ms`);
8137
+ const isSmallSyncPatch = Boolean(sync && (dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) && dirtyRows.length <= 12 && !(scrollOperations === null || scrollOperations === void 0 ? void 0 : scrollOperations.length));
8138
+ if (isSmallSyncPatch) skipUnchangedDirtyRows = false;
8139
+ if ((dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) && skipUnchangedDirtyRows && fpPrevValid && !(scrollOperations === null || scrollOperations === void 0 ? void 0 : scrollOperations.length) && dirtyRows.length < terminal.size().rows) {
8140
+ const cols = terminal.size().cols;
8141
+ const nextDirtyRows = dirtyRows.filter((y) => !rowMatchesPreviousFrame(y, cols));
8142
+ if (nextDirtyRows.length === 0) {
8143
+ if (isDebugEnabled()) getDebugLog().render(`Skipped unchanged dirty commit: ${dirtyRows.length} rows`);
8144
+ return;
8145
+ }
8146
+ dirtyRows = nextDirtyRows;
8147
+ }
7164
8148
  const ensureDirtyBits = (rowCount) => {
7165
8149
  if (!accumulatedDirtyBits || accumulatedDirtyBits.length !== rowCount) {
7166
8150
  accumulatedDirtyBits = new Uint8Array(rowCount);
@@ -7182,12 +8166,43 @@ function createStdoutRenderer(terminal, options) {
7182
8166
  out$1.length = outLen;
7183
8167
  return outLen ? out$1 : null;
7184
8168
  };
8169
+ const buildAccumulatedScrollOperations = () => {
8170
+ return (accumulatedScrollOperations === null || accumulatedScrollOperations === void 0 ? void 0 : accumulatedScrollOperations.length) ? accumulatedScrollOperations : null;
8171
+ };
8172
+ const mergeScrollOperations = (next) => {
8173
+ if (!(next === null || next === void 0 ? void 0 : next.length) || accumulatedAllRows) return;
8174
+ const merged = accumulatedScrollOperations ? accumulatedScrollOperations.slice() : [];
8175
+ for (const op of next) {
8176
+ const startY = Math.floor(op.startY);
8177
+ const endY = Math.floor(op.endY);
8178
+ const delta = Math.trunc(op.delta);
8179
+ if (endY <= startY || delta === 0) continue;
8180
+ const existingIndex = merged.findIndex((existing) => existing.startY === startY && existing.endY === endY);
8181
+ if (existingIndex >= 0) {
8182
+ const existing = merged[existingIndex];
8183
+ const nextDelta = existing.delta + delta;
8184
+ if (nextDelta === 0) merged.splice(existingIndex, 1);
8185
+ else merged[existingIndex] = {
8186
+ startY,
8187
+ endY,
8188
+ delta: nextDelta
8189
+ };
8190
+ } else merged.push({
8191
+ startY,
8192
+ endY,
8193
+ delta
8194
+ });
8195
+ }
8196
+ accumulatedScrollOperations = merged.length ? merged : null;
8197
+ };
7185
8198
  if (!dirtyRows || dirtyRows.length === 0) {
7186
8199
  accumulatedAllRows = true;
7187
8200
  accumulatedDirtyBits = null;
7188
8201
  accumulatedDirtyCount = 0;
7189
8202
  accumulatedDirtyMin = Number.POSITIVE_INFINITY;
7190
8203
  accumulatedDirtyMax = -1;
8204
+ accumulatedScrollOperations = null;
8205
+ accumulatedSkipUnchangedDirtyRows = false;
7191
8206
  } else if (!accumulatedAllRows) {
7192
8207
  const rowCount = terminal.size().rows;
7193
8208
  const bits = ensureDirtyBits(rowCount);
@@ -7201,13 +8216,29 @@ function createStdoutRenderer(terminal, options) {
7201
8216
  if (y > accumulatedDirtyMax) accumulatedDirtyMax = y;
7202
8217
  }
7203
8218
  }
8219
+ mergeScrollOperations(scrollOperations);
8220
+ accumulatedSkipUnchangedDirtyRows && (accumulatedSkipUnchangedDirtyRows = skipUnchangedDirtyRows);
7204
8221
  }
7205
- if (pendingRender) return;
8222
+ if (pendingRender && !sync) return;
7206
8223
  const now$2 = Date.now();
7207
8224
  const elapsed = now$2 - lastFrameTime;
8225
+ if (sync) {
8226
+ if (pendingRender) {
8227
+ pendingRender = false;
8228
+ if (renderTimer) {
8229
+ clearTimeout(renderTimer);
8230
+ renderTimer = null;
8231
+ }
8232
+ }
8233
+ const rows = buildAccumulatedRows();
8234
+ const pendingScrolls = buildAccumulatedScrollOperations();
8235
+ doRender(rows, pendingScrolls, accumulatedSkipUnchangedDirtyRows);
8236
+ return;
8237
+ }
7208
8238
  if (elapsed >= MIN_FRAME_MS) {
7209
8239
  const rows = buildAccumulatedRows();
7210
- doRender(rows);
8240
+ const pendingScrolls = buildAccumulatedScrollOperations();
8241
+ doRender(rows, pendingScrolls, accumulatedSkipUnchangedDirtyRows);
7211
8242
  } else {
7212
8243
  pendingRender = true;
7213
8244
  if (renderTimer) clearTimeout(renderTimer);
@@ -7215,19 +8246,23 @@ function createStdoutRenderer(terminal, options) {
7215
8246
  renderTimer = null;
7216
8247
  if (!disposed) {
7217
8248
  const rows = buildAccumulatedRows();
7218
- doRender(rows);
8249
+ const pendingScrolls = buildAccumulatedScrollOperations();
8250
+ doRender(rows, pendingScrolls, accumulatedSkipUnchangedDirtyRows);
7219
8251
  }
7220
8252
  }, MIN_FRAME_MS - elapsed);
7221
8253
  }
7222
8254
  }
8255
+ terminal.setFingerprintFn((ch, style) => {
8256
+ return styleKey$1(style) << 16 | charHash16(ch);
8257
+ });
7223
8258
  if (altScreen && out.isTTY) out.write("\x1B[?1049h");
7224
8259
  if (hideCursor) out.write("\x1B[?25l");
7225
8260
  if (clear) out.write(`${SGR_RESET}${openBg(defaultBg)}\u001B[2J\u001B[H${SGR_RESET}`);
7226
- const off = terminal.on("commit", ({ dirtyRows }) => {
7227
- if (isDebugEnabled()) getDebugLog().render(`Commit event: dirtyRows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) ?? "null"}, rows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.join(",")) ?? "all"}`);
7228
- render(dirtyRows);
8261
+ const off = terminal.on("commit", ({ dirtyRows, scrollOperations, sync }) => {
8262
+ if (isDebugEnabled()) getDebugLog().render(`Commit event: dirtyRows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.length) ?? "null"}, rows=${(dirtyRows === null || dirtyRows === void 0 ? void 0 : dirtyRows.join(",")) ?? "all"}${(scrollOperations === null || scrollOperations === void 0 ? void 0 : scrollOperations.length) ? `, scrollOps=${scrollOperations.map((op) => `${op.startY}-${op.endY - 1}:${op.delta}`).join("|")}` : ""}${sync ? " (sync)" : ""}`);
8263
+ render(dirtyRows, sync, scrollOperations, true);
7229
8264
  });
7230
- const resizeSource = (options === null || options === void 0 ? void 0 : options.output) ?? process.stdout;
8265
+ const resizeSource = (options === null || options === void 0 ? void 0 : options.output) ?? process$1.stdout;
7231
8266
  const canTrackResize = Boolean(trackResize && out.isTTY && typeof (resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.on) === "function");
7232
8267
  const onResize = () => {
7233
8268
  const cols = Number(resizeSource === null || resizeSource === void 0 ? void 0 : resizeSource.columns);
@@ -7255,6 +8290,8 @@ function createStdoutRenderer(terminal, options) {
7255
8290
  rows: size.rows
7256
8291
  });
7257
8292
  out.write(`\u001B[${cy + 1};${cx + 1}H`);
8293
+ lastCursorX = cx;
8294
+ lastCursorY = cy;
7258
8295
  }
7259
8296
  /** Show or hide the terminal cursor */
7260
8297
  function showCursor(visible) {
@@ -7288,7 +8325,10 @@ function createStdoutRenderer(terminal, options) {
7288
8325
  if (next.defaultBg && next.defaultBg !== defaultBg) defaultBg = next.defaultBg;
7289
8326
  if ("palette" in next) palette = next.palette;
7290
8327
  styleKeyCache$1 = new WeakMap();
7291
- renderedRowCache = [];
8328
+ fpRows = 0;
8329
+ fpPrevValid = false;
8330
+ prevOverlayBlockedRows = [];
8331
+ prevOverlayPartialRows = [];
7292
8332
  render();
7293
8333
  };
7294
8334
  return {
@@ -7592,7 +8632,7 @@ function createDomRenderer(terminal, container) {
7592
8632
  //#region src/runtime/index.ts
7593
8633
  function isNodeLike() {
7594
8634
  var _process$versions;
7595
- return typeof (process === null || process === void 0 || (_process$versions = process.versions) === null || _process$versions === void 0 ? void 0 : _process$versions.node) === "string";
8635
+ return typeof (process$1 === null || process$1 === void 0 || (_process$versions = process$1.versions) === null || _process$versions === void 0 ? void 0 : _process$versions.node) === "string";
7596
8636
  }
7597
8637
  function detectEnv() {
7598
8638
  const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
@@ -7732,26 +8772,39 @@ function useTerminal() {
7732
8772
  //#endregion
7733
8773
  //#region src/vue/composables/use-render-node.ts
7734
8774
  const pendingInvalidateByScheduler = new WeakMap();
7735
- function requestBatchedInvalidate(scheduler, plane) {
8775
+ function mergePriority(prev, next) {
8776
+ if (prev === "high" || next === "high") return "high";
8777
+ if (prev === "normal" || next === "normal") return "normal";
8778
+ return "low";
8779
+ }
8780
+ function requestBatchedInvalidate(scheduler, plane, priority) {
7736
8781
  let state = pendingInvalidateByScheduler.get(scheduler);
7737
8782
  if (!state) {
7738
8783
  state = {
7739
8784
  queued: false,
7740
- plane: null
8785
+ plane: null,
8786
+ priority: "low"
7741
8787
  };
7742
8788
  pendingInvalidateByScheduler.set(scheduler, state);
7743
8789
  }
7744
8790
  if (state.queued) {
7745
8791
  state.plane = state.plane == null || state.plane === plane ? plane : null;
8792
+ state.priority = mergePriority(state.priority, priority);
7746
8793
  return;
7747
8794
  }
7748
8795
  state.plane = plane;
8796
+ state.priority = priority;
7749
8797
  state.queued = true;
7750
8798
  queueMicrotask(() => {
7751
8799
  state.queued = false;
7752
8800
  const queuedPlane = state.plane;
8801
+ const queuedPriority = state.priority;
7753
8802
  state.plane = null;
7754
- scheduler.invalidate(queuedPlane ? { plane: queuedPlane } : void 0);
8803
+ state.priority = "low";
8804
+ scheduler.invalidate(queuedPlane || queuedPriority !== "normal" ? {
8805
+ plane: queuedPlane ?? void 0,
8806
+ priority: queuedPriority
8807
+ } : void 0);
7755
8808
  });
7756
8809
  }
7757
8810
  function useRenderNode(getOptions) {
@@ -7759,11 +8812,13 @@ function useRenderNode(getOptions) {
7759
8812
  const parentStack = useRenderStack();
7760
8813
  const plane = inject(RenderPlaneContextKey, ref("default"));
7761
8814
  const id = ref(null);
8815
+ const lastPlane = ref(plane.value);
7762
8816
  const options = computed(() => getOptions());
7763
8817
  const stop = watchEffect(() => {
7764
8818
  const opt = options.value;
7765
8819
  opt.deps;
7766
8820
  const stack = opt.stack ?? parentStack.value;
8821
+ lastPlane.value = plane.value;
7767
8822
  if (!stack) return;
7768
8823
  if (!id.value) {
7769
8824
  const node = render.register({
@@ -7774,23 +8829,24 @@ function useRenderNode(getOptions) {
7774
8829
  paint: opt.paint
7775
8830
  });
7776
8831
  id.value = node.id;
7777
- requestBatchedInvalidate(scheduler, plane.value);
8832
+ requestBatchedInvalidate(scheduler, plane.value, opt.priority ?? "normal");
7778
8833
  return;
7779
8834
  }
7780
8835
  render.update(id.value, {
7781
8836
  stack,
7782
8837
  zIndex: opt.zIndex ?? 0,
7783
8838
  rect: opt.rect ?? null,
8839
+ dirtyRowsHint: opt.dirtyRowsHint,
7784
8840
  plane: plane.value,
7785
8841
  paint: opt.paint
7786
8842
  });
7787
- requestBatchedInvalidate(scheduler, plane.value);
8843
+ requestBatchedInvalidate(scheduler, plane.value, opt.priority ?? "normal");
7788
8844
  });
7789
8845
  onBeforeUnmount(() => {
7790
8846
  stop();
7791
8847
  if (id.value) {
7792
8848
  render.unregister(id.value);
7793
- requestBatchedInvalidate(scheduler, plane.value);
8849
+ requestBatchedInvalidate(scheduler, lastPlane.value, "normal");
7794
8850
  }
7795
8851
  });
7796
8852
  return { id };
@@ -8981,6 +10037,8 @@ function createPromptMentionPlugin(options = {}) {
8981
10037
  install: (ctx) => {
8982
10038
  const getProps = () => ctx.getProps();
8983
10039
  const promptOverlayStack = ctx.render.createStack(ctx.render.rootStack, 1e4);
10040
+ let lastTopClampedRect = null;
10041
+ let lastPromptRect = null;
8984
10042
  const derivedStyleCache = new WeakMap();
8985
10043
  const reactiveProps = reactive({
8986
10044
  promptSuggestions: getProps().promptSuggestions,
@@ -9028,6 +10086,20 @@ function createPromptMentionPlugin(options = {}) {
9028
10086
  multilineToken: "",
9029
10087
  mentionToken: ctx.mentionToken
9030
10088
  });
10089
+ watch(() => {
10090
+ if (!ctx.visible.value || !promptMention.promptVisible.value) return null;
10091
+ const rect = promptMention.promptRect.value;
10092
+ return [
10093
+ Math.floor(rect.x),
10094
+ Math.floor(rect.y),
10095
+ Math.floor(rect.w),
10096
+ Math.floor(rect.h)
10097
+ ].join(":");
10098
+ }, (nextRectKey, prevRectKey) => {
10099
+ if (!prevRectKey || nextRectKey === prevRectKey) return;
10100
+ ctx.render.invalidatePlane("default");
10101
+ ctx.scheduler.invalidate({ priority: "high" });
10102
+ });
9031
10103
  ctx.registerChipStyleProvider({
9032
10104
  getStyle: (baseStyle, chip) => {
9033
10105
  if (chip.kind !== "mention") return null;
@@ -9203,6 +10275,20 @@ function createPromptMentionPlugin(options = {}) {
9203
10275
  useRenderNode(() => ({
9204
10276
  zIndex: getProps().zIndex + 5,
9205
10277
  stack: promptOverlayStack,
10278
+ dirtyRowsHint: (() => {
10279
+ const currentRect = ctx.visible.value && promptMention.promptVisible.value ? {
10280
+ y: Math.floor(promptMention.promptRect.value.y),
10281
+ h: Math.max(0, Math.floor(promptMention.promptRect.value.h))
10282
+ } : null;
10283
+ const rects = [lastPromptRect, currentRect].filter(Boolean);
10284
+ if (!rects.length) return void 0;
10285
+ const maxRows = ctx.terminal.size().rows;
10286
+ const start = Math.max(0, Math.min(...rects.map((rect) => rect.y)) - 1);
10287
+ const end = Math.min(maxRows, Math.max(...rects.map((rect) => rect.y + rect.h)) + 1);
10288
+ if (end <= start) return void 0;
10289
+ return Array.from({ length: end - start }, (_, index) => start + index);
10290
+ })(),
10291
+ priority: "high",
9206
10292
  rect: ctx.visible.value && promptMention.promptVisible.value ? promptMention.promptRect.value : {
9207
10293
  x: 0,
9208
10294
  y: 0,
@@ -9229,6 +10315,10 @@ function createPromptMentionPlugin(options = {}) {
9229
10315
  if (!ctx.visible.value || !promptMention.promptVisible.value) return;
9230
10316
  const r = promptMention.promptRect.value;
9231
10317
  if (r.w < 3 || r.h < 3) return;
10318
+ lastPromptRect = {
10319
+ y: Math.floor(r.y),
10320
+ h: Math.max(0, Math.floor(r.h))
10321
+ };
9232
10322
  const base = getProps().style ?? ctx.defaultStyle.value;
9233
10323
  const selectedOverride = { bg: "blue" };
9234
10324
  const styleKey$1 = base && typeof base === "object" ? base : null;
@@ -9278,6 +10368,19 @@ function createPromptMentionPlugin(options = {}) {
9278
10368
  const contentW = Math.max(0, innerW - sidePad * 2);
9279
10369
  const contentX = x0 + 1 + sidePad;
9280
10370
  const borderStyle = derived.borderStyle;
10371
+ if (lastTopClampedRect && y0 > 0) {
10372
+ ctx.terminal.write(spaces(lastTopClampedRect.w), {
10373
+ x: lastTopClampedRect.x,
10374
+ y: 0,
10375
+ style: getProps().style ?? ctx.defaultStyle.value
10376
+ });
10377
+ lastTopClampedRect = null;
10378
+ }
10379
+ if (y0 === 0) lastTopClampedRect = {
10380
+ x: x0,
10381
+ w
10382
+ };
10383
+ else lastTopClampedRect = null;
9281
10384
  ctx.terminal.write(`┌${repeatChar("─", innerW)}┐`, {
9282
10385
  x: x0,
9283
10386
  y: y0,
@@ -11543,6 +12646,8 @@ const TerminalProvider = defineComponent({
11543
12646
  let rafToken = 0;
11544
12647
  let pendingInvalidate = false;
11545
12648
  let timer = null;
12649
+ let holdNormalInvalidates = false;
12650
+ let holdReleaseToken = 0;
11546
12651
  let pendingInvalidateAllPlanes = false;
11547
12652
  const pendingInvalidatePlanes = new Set();
11548
12653
  let imeComposing = false;
@@ -11571,11 +12676,14 @@ const TerminalProvider = defineComponent({
11571
12676
  pendingInvalidatePlanes.clear();
11572
12677
  return activePlanes;
11573
12678
  }
11574
- function flush() {
12679
+ function flush(sync = false) {
11575
12680
  if (unmounting) return;
11576
12681
  const activePlanes = takeActivePlanes();
11577
12682
  render.render({ activePlanes });
11578
- terminal.commit({ planes: activePlanes });
12683
+ terminal.commit({
12684
+ planes: activePlanes,
12685
+ sync
12686
+ });
11579
12687
  updateImePositionAfterFlush === null || updateImePositionAfterFlush === void 0 || updateImePositionAfterFlush();
11580
12688
  }
11581
12689
  function clearTimer() {
@@ -11583,6 +12691,16 @@ const TerminalProvider = defineComponent({
11583
12691
  clearTimeout(timer);
11584
12692
  timer = null;
11585
12693
  }
12694
+ function scheduleNormalInvalidateRelease() {
12695
+ const token = ++holdReleaseToken;
12696
+ nextTick(() => {
12697
+ if (token !== holdReleaseToken) return;
12698
+ holdNormalInvalidates = false;
12699
+ if (unmounting || !pendingInvalidate) return;
12700
+ pendingInvalidate = false;
12701
+ invalidate({ priority: "normal" });
12702
+ });
12703
+ }
11586
12704
  function flushNow() {
11587
12705
  if (unmounting) return;
11588
12706
  pendingInvalidate = false;
@@ -11592,7 +12710,9 @@ const TerminalProvider = defineComponent({
11592
12710
  raf = 0;
11593
12711
  }
11594
12712
  clearTimer();
11595
- flush();
12713
+ holdNormalInvalidates = true;
12714
+ scheduleNormalInvalidateRelease();
12715
+ flush(true);
11596
12716
  }
11597
12717
  function invalidate(options) {
11598
12718
  if (unmounting) return;
@@ -11603,6 +12723,10 @@ const TerminalProvider = defineComponent({
11603
12723
  flushNow();
11604
12724
  return;
11605
12725
  }
12726
+ if (holdNormalInvalidates) {
12727
+ pendingInvalidate = true;
12728
+ return;
12729
+ }
11606
12730
  if (priority === "low") {
11607
12731
  if (timer || raf) return;
11608
12732
  profiler === null || profiler === void 0 || profiler.recordInvalidate({ plane: (options === null || options === void 0 ? void 0 : options.plane) ?? null });
@@ -12145,6 +13269,8 @@ const TerminalProvider = defineComponent({
12145
13269
  onBeforeUnmount(() => {
12146
13270
  unmounting = true;
12147
13271
  offCommit === null || offCommit === void 0 || offCommit();
13272
+ clearTimer();
13273
+ holdReleaseToken++;
12148
13274
  if (raf > 0) cancelAnimationFrame(raf);
12149
13275
  raf = 0;
12150
13276
  scope.stop();
@@ -13073,7 +14199,10 @@ const TInput = defineComponent({
13073
14199
  emit("update:modelValue", nextValue);
13074
14200
  emit("input", nextValue);
13075
14201
  if (commit) emit("change", nextValue);
13076
- scheduler.invalidate();
14202
+ scheduler.flushNow();
14203
+ nextTick(() => {
14204
+ scheduler.flushNow();
14205
+ });
13077
14206
  }
13078
14207
  function applyMove(nextCursor, extend) {
13079
14208
  const prev = cursor.value;
@@ -13090,7 +14219,7 @@ const TInput = defineComponent({
13090
14219
  } else desiredCol.value = indexToLineCellColInline(value, props.multilineTexts, props.mentions, cursor.value).col;
13091
14220
  ensureCursorVisible();
13092
14221
  syncImeAnchorNow();
13093
- scheduler.invalidate();
14222
+ scheduler.flushNow();
13094
14223
  }
13095
14224
  watch([
13096
14225
  () => absRect.value.w,
@@ -13209,7 +14338,11 @@ const TInput = defineComponent({
13209
14338
  terminal,
13210
14339
  scheduler,
13211
14340
  defaultStyle,
13212
- render,
14341
+ render: {
14342
+ rootStack: render.rootStack,
14343
+ createStack: render.createStack,
14344
+ invalidatePlane: render.invalidatePlane
14345
+ },
13213
14346
  visible,
13214
14347
  rawAbsRect,
13215
14348
  eventZ,
@@ -13306,7 +14439,7 @@ const TInput = defineComponent({
13306
14439
  emitMentions(nextMentions);
13307
14440
  ensureCursorVisible();
13308
14441
  syncImeAnchorNow();
13309
- scheduler.invalidate();
14442
+ scheduler.flushNow();
13310
14443
  }
13311
14444
  function removePendingPasteImage(placeholderPath) {
13312
14445
  const current = getCurrentMentions();
@@ -13966,10 +15099,12 @@ const TInput = defineComponent({
13966
15099
  clearAll();
13967
15100
  return;
13968
15101
  }
13969
- focused.value = false;
13970
- anchor.value = null;
13971
- emit("blur");
13972
- scheduler.invalidate();
15102
+ const manager = events.value;
15103
+ if (manager && manager.getFocused()) {
15104
+ manager.focus(null);
15105
+ return;
15106
+ }
15107
+ applyBlur();
13973
15108
  return;
13974
15109
  }
13975
15110
  if (isPrintableKey(e)) {
@@ -14207,6 +15342,7 @@ const TInput = defineComponent({
14207
15342
  var _chipStyleProvider$va;
14208
15343
  return {
14209
15344
  zIndex: props.zIndex,
15345
+ priority: pendingValue.value != null ? "high" : "normal",
14210
15346
  rect: visible.value ? absRect.value : {
14211
15347
  x: 0,
14212
15348
  y: 0,
@@ -14239,6 +15375,7 @@ const TInput = defineComponent({
14239
15375
  blinkOn.value,
14240
15376
  defaultStyle.value,
14241
15377
  ((_chipStyleProvider$va = chipStyleProvider.value) === null || _chipStyleProvider$va === void 0 ? void 0 : _chipStyleProvider$va.version.value) ?? 0,
15378
+ pendingValue.value,
14242
15379
  props.skillHighlightStyle,
14243
15380
  props.skillTrigger
14244
15381
  ],
@@ -14857,19 +15994,23 @@ const TJsonEditor = defineComponent({
14857
15994
  //#endregion
14858
15995
  //#region src/vue/utils/wheel-scroll.ts
14859
15996
  const LINE_UNIT_THRESHOLD = 3;
14860
- const PIXELS_PER_LINE = 100;
15997
+ const PIXELS_PER_LINE = .75;
14861
15998
  const ACCEL_WINDOW_MS = 120;
14862
- const MAX_ACCEL = 2.5;
15999
+ const MAX_ACCEL = 26;
14863
16000
  const EDGE_BOUNCE_MS = 120;
14864
16001
  const MIN_TICK_INTERVAL_MS = 6;
14865
16002
  function clamp$3(n, min, max) {
14866
16003
  return Math.max(min, Math.min(max, n));
14867
16004
  }
14868
- function isLineUnitDelta(deltaY) {
16005
+ function isLineUnitDelta(deltaY, mode = "auto") {
16006
+ if (mode === "line") return true;
16007
+ if (mode === "pixel") return false;
14869
16008
  const abs = Math.abs(deltaY);
14870
16009
  return abs > 0 && abs <= LINE_UNIT_THRESHOLD && Number.isInteger(deltaY);
14871
16010
  }
14872
- function normalizeDelta(deltaY) {
16011
+ function normalizeDelta(deltaY, mode = "auto") {
16012
+ if (mode === "line") return deltaY;
16013
+ if (mode === "pixel") return deltaY / PIXELS_PER_LINE;
14873
16014
  const abs = Math.abs(deltaY);
14874
16015
  if (abs <= LINE_UNIT_THRESHOLD && Number.isInteger(deltaY)) return deltaY;
14875
16016
  return deltaY / PIXELS_PER_LINE;
@@ -14888,7 +16029,7 @@ function createWheelScrollState() {
14888
16029
  lastEdgeAt: 0
14889
16030
  };
14890
16031
  }
14891
- function applyWheelScroll(state, deltaY, scrollTop, maxTop, now$2 = Date.now()) {
16032
+ function applyWheelScroll(state, deltaY, scrollTop, maxTop, now$2 = Date.now(), deltaMode = "auto") {
14892
16033
  if (!Number.isFinite(deltaY) || deltaY === 0 || maxTop <= 0) {
14893
16034
  if (maxTop <= 0) {
14894
16035
  state.accumulator = 0;
@@ -14902,7 +16043,7 @@ function applyWheelScroll(state, deltaY, scrollTop, maxTop, now$2 = Date.now())
14902
16043
  };
14903
16044
  }
14904
16045
  const dt = state.lastAt ? now$2 - state.lastAt : Infinity;
14905
- const lineUnits = isLineUnitDelta(deltaY);
16046
+ const lineUnits = isLineUnitDelta(deltaY, deltaMode);
14906
16047
  if (lineUnits && dt !== Infinity && dt >= 0 && dt < MIN_TICK_INTERVAL_MS) return {
14907
16048
  nextTop: scrollTop,
14908
16049
  dir: 0,
@@ -14910,7 +16051,7 @@ function applyWheelScroll(state, deltaY, scrollTop, maxTop, now$2 = Date.now())
14910
16051
  };
14911
16052
  const accel = lineUnits ? 1 : state.lastAt ? accelFactor(now$2, state.lastAt) : 1;
14912
16053
  state.lastAt = now$2;
14913
- state.accumulator += normalizeDelta(deltaY) * accel;
16054
+ state.accumulator += normalizeDelta(deltaY, deltaMode) * accel;
14914
16055
  const lines = Math.trunc(state.accumulator);
14915
16056
  if (lines === 0) return {
14916
16057
  nextTop: scrollTop,
@@ -14960,6 +16101,26 @@ function applyWheelScroll(state, deltaY, scrollTop, maxTop, now$2 = Date.now())
14960
16101
  function clamp$2(n, min, max) {
14961
16102
  return Math.max(min, Math.min(max, n));
14962
16103
  }
16104
+ function getWheelScrollInput$1(e) {
16105
+ const deltaY = Number(e.deltaY ?? 0);
16106
+ const deltaMode = typeof e.deltaMode === "number" ? e.deltaMode : void 0;
16107
+ if (Number.isInteger(deltaY) && deltaY !== 0 && Math.abs(deltaY) >= 100 && Math.abs(deltaY) % 100 === 0 && (deltaMode == null || deltaMode === 0)) return {
16108
+ deltaY: deltaY / 100,
16109
+ mode: "line"
16110
+ };
16111
+ if (deltaMode === 1) return {
16112
+ deltaY,
16113
+ mode: "line"
16114
+ };
16115
+ if (deltaMode === 0) return {
16116
+ deltaY,
16117
+ mode: "pixel"
16118
+ };
16119
+ return {
16120
+ deltaY,
16121
+ mode: "auto"
16122
+ };
16123
+ }
14963
16124
  const TList = defineComponent({
14964
16125
  name: "TList",
14965
16126
  props: {
@@ -15152,11 +16313,12 @@ const TList = defineComponent({
15152
16313
  if (idx >= 0 && idx < props.items.length) commit(idx);
15153
16314
  },
15154
16315
  wheel: (e) => {
15155
- const delta = e.deltaY ?? 0;
16316
+ const { deltaY, mode } = getWheelScrollInput$1(e);
16317
+ const delta = deltaY;
15156
16318
  if (!delta) return;
15157
16319
  const h$1 = Math.max(0, props.h);
15158
16320
  const maxTop = Math.max(0, props.items.length - h$1);
15159
- const { nextTop, dir } = applyWheelScroll(wheelState, delta, scrollTop.value, maxTop);
16321
+ const { nextTop, dir } = applyWheelScroll(wheelState, delta, scrollTop.value, maxTop, Date.now(), mode);
15160
16322
  if (!dir || nextTop === scrollTop.value) return;
15161
16323
  scrollTop.value = nextTop;
15162
16324
  const visibleStart = nextTop;
@@ -15493,6 +16655,26 @@ const TMultilineModal = defineComponent({
15493
16655
  function clamp$1(n, min, max) {
15494
16656
  return Math.max(min, Math.min(max, n));
15495
16657
  }
16658
+ function getWheelScrollInput(e) {
16659
+ const deltaY = Number(e.deltaY ?? 0);
16660
+ const deltaMode = typeof e.deltaMode === "number" ? e.deltaMode : void 0;
16661
+ if (Number.isInteger(deltaY) && deltaY !== 0 && Math.abs(deltaY) >= 100 && Math.abs(deltaY) % 100 === 0 && (deltaMode == null || deltaMode === 0)) return {
16662
+ deltaY: deltaY / 100,
16663
+ mode: "line"
16664
+ };
16665
+ if (deltaMode === 1) return {
16666
+ deltaY,
16667
+ mode: "line"
16668
+ };
16669
+ if (deltaMode === 0) return {
16670
+ deltaY,
16671
+ mode: "pixel"
16672
+ };
16673
+ return {
16674
+ deltaY,
16675
+ mode: "auto"
16676
+ };
16677
+ }
15496
16678
  function isCtrlLike(e) {
15497
16679
  return Boolean(e.ctrlKey || e.metaKey);
15498
16680
  }
@@ -15910,11 +17092,12 @@ const TPathPicker = defineComponent({
15910
17092
  await trySelect(s.absPath);
15911
17093
  },
15912
17094
  wheel: (e) => {
15913
- const delta = e.deltaY ?? 0;
17095
+ const { deltaY, mode } = getWheelScrollInput(e);
17096
+ const delta = deltaY;
15914
17097
  if (!delta) return;
15915
17098
  const h$1 = Math.max(0, listH.value);
15916
17099
  const maxTop = Math.max(0, suggestions.value.length - h$1);
15917
- const { nextTop, dir } = applyWheelScroll(wheelState, delta, scrollTop.value, maxTop);
17100
+ const { nextTop, dir } = applyWheelScroll(wheelState, delta, scrollTop.value, maxTop, Date.now(), mode);
15918
17101
  if (!dir || nextTop === scrollTop.value) return;
15919
17102
  scrollTop.value = nextTop;
15920
17103
  const visibleStart = nextTop;