@simon_he/vue-tui 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2533,6 +2533,13 @@ function normalizeInt(value) {
2533
2533
  const n = Math.floor(Number(value));
2534
2534
  return Number.isFinite(n) ? n : 0;
2535
2535
  }
2536
+ function normalizeChangedRange(range, count) {
2537
+ if (!range)
2538
+ return null;
2539
+ const start = clamp3(normalizeInt(range.start), 0, count);
2540
+ const end = clamp3(normalizeInt(range.end), start, count);
2541
+ return { start, end };
2542
+ }
2536
2543
  function getWheelScrollInput2(e) {
2537
2544
  const deltaY = Number(e.deltaY ?? 0);
2538
2545
  const deltaMode = typeof e.deltaMode === "number" ? e.deltaMode : void 0;
@@ -2557,6 +2564,10 @@ var TVirtualRows = (0, import_vue10.defineComponent)({
2557
2564
  zIndex: { type: Number, default: 0 },
2558
2565
  itemCount: { type: Number, required: true },
2559
2566
  itemVersion: { type: Number, required: true },
2567
+ itemChangedRange: {
2568
+ type: Function,
2569
+ default: void 0
2570
+ },
2560
2571
  getItem: { type: Function, required: true },
2561
2572
  paintItem: {
2562
2573
  type: Function,
@@ -2756,6 +2767,23 @@ var TVirtualRows = (0, import_vue10.defineComponent)({
2756
2767
  function invalidateSelf(priority = "normal") {
2757
2768
  scheduler.invalidate({ priority, plane: plane.value, reason: "scroll" });
2758
2769
  }
2770
+ function markScrollDamage(prevTop, nextTop, strategy = "auto") {
2771
+ const r = normalizedRect();
2772
+ const h14 = r.h;
2773
+ const delta = nextTop - prevTop;
2774
+ if (h14 <= 0 || !delta)
2775
+ return;
2776
+ const size = terminal.size();
2777
+ const ownsFullRows = Math.floor(r.x) === 0 && Math.floor(r.w) >= size.cols;
2778
+ const withinTerminalRows = r.y >= 0 && r.y + h14 <= size.rows;
2779
+ const canUseScrollPlane = strategy === "auto" && props.rowScrollMode === "unsafe-full-row" && rendererCapabilities.value.scrollOperations && ownsFullRows && withinTerminalRows && !isClipped() && Math.abs(delta) < h14 && !dirtyRowsHint?.length;
2780
+ if (canUseScrollPlane) {
2781
+ render.unsafeScrollPlaneRows(plane.value, r.y, r.y + h14, delta);
2782
+ markRowsDirty(exposedRowsForDelta2(r.y, h14, delta));
2783
+ } else {
2784
+ markViewportDirty();
2785
+ }
2786
+ }
2759
2787
  function applyScrollTop(nextTop, options) {
2760
2788
  const r = normalizedRect();
2761
2789
  const h14 = r.h;
@@ -2776,16 +2804,7 @@ var TVirtualRows = (0, import_vue10.defineComponent)({
2776
2804
  setCurrentScrollTop(clampedTop);
2777
2805
  if (options?.emitUpdate !== false)
2778
2806
  emit("update:scrollTop", clampedTop);
2779
- const size = terminal.size();
2780
- const ownsFullRows = Math.floor(r.x) === 0 && Math.floor(r.w) >= size.cols;
2781
- const withinTerminalRows = r.y >= 0 && r.y + h14 <= size.rows;
2782
- const canUseScrollPlane = (options?.strategy ?? "auto") === "auto" && props.rowScrollMode === "unsafe-full-row" && rendererCapabilities.value.scrollOperations && ownsFullRows && withinTerminalRows && !isClipped() && Math.abs(delta) < h14 && !dirtyRowsHint?.length;
2783
- if (canUseScrollPlane) {
2784
- render.unsafeScrollPlaneRows(plane.value, r.y, r.y + h14, delta);
2785
- markRowsDirty(exposedRowsForDelta2(r.y, h14, delta));
2786
- } else {
2787
- markViewportDirty();
2788
- }
2807
+ markScrollDamage(prevTop, clampedTop, options?.strategy);
2789
2808
  if (options?.emitScroll)
2790
2809
  emitScroll(clampedTop);
2791
2810
  return true;
@@ -2852,8 +2871,11 @@ var TVirtualRows = (0, import_vue10.defineComponent)({
2852
2871
  invalidateSelf("normal");
2853
2872
  }
2854
2873
  function invalidateRange(start, end) {
2874
+ markItemRangeDirty(start, end);
2875
+ }
2876
+ function markItemRangeDirty(start, end) {
2855
2877
  if (!hasPaintableViewport())
2856
- return;
2878
+ return false;
2857
2879
  const r = normalizedRect();
2858
2880
  const { y: clipY } = clipOffsets();
2859
2881
  const top = currentScrollTop() + clipY;
@@ -2866,9 +2888,10 @@ var TVirtualRows = (0, import_vue10.defineComponent)({
2866
2888
  rows.push(y);
2867
2889
  }
2868
2890
  if (!rows.length)
2869
- return;
2891
+ return true;
2870
2892
  markRowsDirty(rows);
2871
2893
  invalidateSelf("normal");
2894
+ return true;
2872
2895
  }
2873
2896
  function invalidateIndex(index) {
2874
2897
  const n = normalizeInt(index);
@@ -3108,8 +3131,9 @@ var TVirtualRows = (0, import_vue10.defineComponent)({
3108
3131
  return;
3109
3132
  }
3110
3133
  cancelWheelScrollFrame();
3134
+ const prevTop = controlledScrollTop.value;
3111
3135
  controlledScrollTop.value = nextTop;
3112
- markViewportDirty();
3136
+ markScrollDamage(prevTop, nextTop);
3113
3137
  selection.refresh(pendingSelectionScrollFocusRemap ? { remapFocus: true } : void 0);
3114
3138
  pendingSelectionScrollFocusRemap = false;
3115
3139
  invalidateSelf("high");
@@ -3133,7 +3157,7 @@ var TVirtualRows = (0, import_vue10.defineComponent)({
3133
3157
  () => absRect.value.w,
3134
3158
  () => absRect.value.h
3135
3159
  ],
3136
- () => {
3160
+ (next, prev) => {
3137
3161
  resetWheelScrollState(wheelState);
3138
3162
  const nextTop = clampScrollTop(currentScrollTop());
3139
3163
  if (isScrollControlled()) {
@@ -3144,6 +3168,16 @@ var TVirtualRows = (0, import_vue10.defineComponent)({
3144
3168
  } else {
3145
3169
  setCurrentScrollTop(nextTop);
3146
3170
  }
3171
+ const onlyItemVersionChanged = Boolean(
3172
+ prev && next[0] === prev[0] && next[1] !== prev[1] && next[2] === prev[2] && next[3] === prev[3] && next[4] === prev[4] && next[5] === prev[5] && next[6] === prev[6] && next[7] === prev[7] && next[8] === prev[8] && next[9] === prev[9]
3173
+ );
3174
+ if (onlyItemVersionChanged) {
3175
+ const changedRange = normalizeChangedRange(props.itemChangedRange?.(), itemCount.value);
3176
+ if (changedRange) {
3177
+ markItemRangeDirty(changedRange.start, changedRange.end);
3178
+ return;
3179
+ }
3180
+ }
3147
3181
  markViewportDirty();
3148
3182
  invalidateSelf("normal");
3149
3183
  },
@@ -3171,7 +3205,6 @@ var TVirtualRows = (0, import_vue10.defineComponent)({
3171
3205
  absRect.value,
3172
3206
  fullRect.value,
3173
3207
  itemCount.value,
3174
- props.itemVersion,
3175
3208
  props.getItem,
3176
3209
  props.paintItem,
3177
3210
  props.selectionSpanText,
@@ -3260,8 +3293,11 @@ var EMPTY_LAYOUT = Object.freeze({
3260
3293
  visualRows: Object.freeze([]),
3261
3294
  rowStarts: /* @__PURE__ */ new Map(),
3262
3295
  rowEnds: /* @__PURE__ */ new Map(),
3263
- regions: Object.freeze([])
3296
+ regions: Object.freeze([]),
3297
+ fixedRows: false,
3298
+ rowCount: 0
3264
3299
  });
3300
+ var MAX_KEYED_ROW_LAYOUT_CACHE_ENTRIES = 2048;
3265
3301
  function clamp4(n, min, max) {
3266
3302
  return Math.max(min, Math.min(max, n));
3267
3303
  }
@@ -3269,6 +3305,13 @@ function normalizeInt2(value) {
3269
3305
  const n = Math.floor(Number(value));
3270
3306
  return Number.isFinite(n) ? n : 0;
3271
3307
  }
3308
+ function normalizedChangedRange(range, count) {
3309
+ if (!range)
3310
+ return null;
3311
+ const start = clamp4(normalizeInt2(range.start), 0, count);
3312
+ const end = clamp4(normalizeInt2(range.end), start, count);
3313
+ return { start, end };
3314
+ }
3272
3315
  function sourceSegmentsForRegion(row) {
3273
3316
  if (row.kind === "message")
3274
3317
  return row.segments;
@@ -3305,8 +3348,27 @@ function rowHasRegionId(row, rowKey, id) {
3305
3348
  }
3306
3349
  return false;
3307
3350
  }
3308
- function sameCachedLayout(entry, next) {
3309
- return Boolean(entry) && (next.usesSourceRowVersion || entry.version === next.version) && entry.rowVersion === next.rowVersion && entry.usesSourceRowVersion === next.usesSourceRowVersion && (next.usesSourceRowVersion || entry.row === next.row) && entry.rowIndex === next.rowIndex && entry.rowKey === next.rowKey && entry.width === next.width && entry.wrap === next.wrap && entry.baseStyle === next.baseStyle && entry.hoverStyle === next.hoverStyle && entry.focusStyle === next.focusStyle && entry.hoverRegionId === next.hoverRegionId && entry.focusedRegionId === next.focusedRegionId;
3351
+ function cachedLayoutHasRegionId(entry, id) {
3352
+ if (!entry || !id)
3353
+ return false;
3354
+ for (const visualRow of entry.visualRows) {
3355
+ if (visualRow.hitRegions.some((region) => region.id === id))
3356
+ return true;
3357
+ }
3358
+ return false;
3359
+ }
3360
+ function rowLayoutIdentityKey(rowKey, rowVersion) {
3361
+ return JSON.stringify([rowKey, rowVersion]);
3362
+ }
3363
+ function reindexVisualRows(rows, rowIndex) {
3364
+ return rows.map((visualRow) => ({
3365
+ ...visualRow,
3366
+ rowIndex,
3367
+ hitRegions: visualRow.hitRegions.map((region) => ({ ...region, rowIndex }))
3368
+ }));
3369
+ }
3370
+ function sameCachedLayout(entry, next, matchRowIndex = true) {
3371
+ return Boolean(entry) && (next.usesSourceRowVersion || entry.version === next.version) && entry.rowVersion === next.rowVersion && entry.usesSourceRowVersion === next.usesSourceRowVersion && (next.usesSourceRowVersion || entry.row === next.row) && (!matchRowIndex || entry.rowIndex === next.rowIndex) && entry.rowKey === next.rowKey && entry.width === next.width && entry.wrap === next.wrap && entry.baseStyle === next.baseStyle && entry.hoverStyle === next.hoverStyle && entry.focusStyle === next.focusStyle && entry.hoverRegionId === next.hoverRegionId && entry.focusedRegionId === next.focusedRegionId;
3310
3372
  }
3311
3373
  var TTranscriptView = (0, import_vue11.defineComponent)({
3312
3374
  name: "TTranscriptView",
@@ -3356,6 +3418,10 @@ var TTranscriptView = (0, import_vue11.defineComponent)({
3356
3418
  const hoveredRegion = (0, import_vue11.ref)(null);
3357
3419
  const focusedRegion = (0, import_vue11.ref)(null);
3358
3420
  const rowLayoutCache = /* @__PURE__ */ new Map();
3421
+ const keyedRowLayoutCache = /* @__PURE__ */ new Map();
3422
+ let previousWrappedLayout = null;
3423
+ let previousWrappedDeps = null;
3424
+ let canUseWrappedChangedRangeRepaint = false;
3359
3425
  let warnedLargeFlattenedRows = false;
3360
3426
  function isScrollControlled() {
3361
3427
  return Object.prototype.hasOwnProperty.call(instance?.vnode.props ?? {}, "scrollTop");
@@ -3363,14 +3429,181 @@ var TTranscriptView = (0, import_vue11.defineComponent)({
3363
3429
  function currentScrollTop() {
3364
3430
  return normalizeInt2(isScrollControlled() ? props.scrollTop : innerScrollTop.value);
3365
3431
  }
3432
+ function currentScrollTopForRowCount(count) {
3433
+ const viewportRows = Math.max(0, normalizeInt2(props.h));
3434
+ const maxTop = Math.max(0, count - viewportRows);
3435
+ return clamp4(currentScrollTop(), 0, maxTop);
3436
+ }
3437
+ function rememberKeyedRowLayout(entry) {
3438
+ if (!entry.usesSourceRowVersion)
3439
+ return;
3440
+ keyedRowLayoutCache.set(rowLayoutIdentityKey(entry.rowKey, entry.rowVersion), entry);
3441
+ if (keyedRowLayoutCache.size <= MAX_KEYED_ROW_LAYOUT_CACHE_ENTRIES)
3442
+ return;
3443
+ const first = keyedRowLayoutCache.keys().next().value;
3444
+ if (first != null)
3445
+ keyedRowLayoutCache.delete(first);
3446
+ }
3447
+ function layoutSourceRow(rowIndex, options) {
3448
+ const sourceRowVersion = props.source.getRowVersion?.(rowIndex);
3449
+ let cached = rowLayoutCache.get(rowIndex);
3450
+ const usesSourceRowVersion = sourceRowVersion != null;
3451
+ let row = cached?.row;
3452
+ let rowKey = cached?.rowKey;
3453
+ let canUseCachedRow = false;
3454
+ if (usesSourceRowVersion && cached?.usesSourceRowVersion && cached.rowVersion === sourceRowVersion) {
3455
+ if (props.source.getRowKey) {
3456
+ rowKey = props.source.getRowKey(rowIndex);
3457
+ canUseCachedRow = rowKey === cached.rowKey;
3458
+ } else {
3459
+ rowKey = cached.rowKey;
3460
+ canUseCachedRow = true;
3461
+ }
3462
+ }
3463
+ if (!canUseCachedRow && usesSourceRowVersion && props.source.getRowKey) {
3464
+ rowKey = props.source.getRowKey(rowIndex);
3465
+ cached = keyedRowLayoutCache.get(rowLayoutIdentityKey(rowKey, sourceRowVersion));
3466
+ if (cached) {
3467
+ row = cached.row;
3468
+ canUseCachedRow = true;
3469
+ }
3470
+ }
3471
+ if (!canUseCachedRow || row == null || rowKey == null) {
3472
+ row = props.source.getRow(rowIndex);
3473
+ rowKey = props.source.getRowKey?.(rowIndex) ?? row.key;
3474
+ canUseCachedRow = false;
3475
+ }
3476
+ const nextHoverRegionId = canUseCachedRow ? cachedLayoutHasRegionId(cached, options.hoverRegionId) ? options.hoverRegionId : null : rowHasRegionId(row, rowKey, options.hoverRegionId) ? options.hoverRegionId : null;
3477
+ const nextFocusedRegionId = canUseCachedRow ? cachedLayoutHasRegionId(cached, options.focusedRegionId) ? options.focusedRegionId : null : rowHasRegionId(row, rowKey, options.focusedRegionId) ? options.focusedRegionId : null;
3478
+ const cacheKey = {
3479
+ version: props.version,
3480
+ rowVersion: sourceRowVersion ?? props.version,
3481
+ usesSourceRowVersion,
3482
+ row,
3483
+ rowIndex,
3484
+ rowKey,
3485
+ width: options.width,
3486
+ wrap: props.wrap,
3487
+ baseStyle: options.baseStyle,
3488
+ hoverStyle: props.hoverStyle,
3489
+ focusStyle: props.focusStyle,
3490
+ hoverRegionId: nextHoverRegionId,
3491
+ focusedRegionId: nextFocusedRegionId
3492
+ };
3493
+ const cacheMatches = sameCachedLayout(cached, cacheKey);
3494
+ const reindexableCacheMatches = !cacheMatches && canUseCachedRow && sameCachedLayout(cached, cacheKey, false);
3495
+ const rows = cacheMatches ? cached.visualRows : reindexableCacheMatches ? reindexVisualRows(cached.visualRows, rowIndex) : layoutTranscriptRow({
3496
+ row,
3497
+ rowIndex,
3498
+ rowKey,
3499
+ width: options.width,
3500
+ baseStyle: options.baseStyle,
3501
+ hoverRegionId: nextHoverRegionId,
3502
+ focusedRegionId: nextFocusedRegionId,
3503
+ hoverStyle: props.hoverStyle,
3504
+ focusStyle: props.focusStyle,
3505
+ wrap: props.wrap
3506
+ });
3507
+ if (rows !== cached?.visualRows) {
3508
+ const entry = { ...cacheKey, visualRows: rows };
3509
+ rowLayoutCache.set(rowIndex, entry);
3510
+ rememberKeyedRowLayout(entry);
3511
+ }
3512
+ return { rows, cacheHit: cacheMatches || reindexableCacheMatches };
3513
+ }
3514
+ function pruneRowLayoutCache(count) {
3515
+ for (const rowIndex of rowLayoutCache.keys()) {
3516
+ if (rowIndex >= count)
3517
+ rowLayoutCache.delete(rowIndex);
3518
+ }
3519
+ }
3520
+ function sameWrappedDeps(a, b) {
3521
+ return Boolean(a) && a.width === b.width && a.baseStyle === b.baseStyle && a.hoverStyle === b.hoverStyle && a.focusStyle === b.focusStyle && a.hoverRegionId === b.hoverRegionId && a.focusedRegionId === b.focusedRegionId;
3522
+ }
3523
+ function visibleRegionsForVisualRows(visualRows) {
3524
+ const top = clamp4(currentScrollTop(), 0, visualRows.length);
3525
+ const bottom = Math.min(visualRows.length, top + Math.max(0, normalizeInt2(props.h)));
3526
+ const regions = [];
3527
+ for (let index = top; index < bottom; index++) {
3528
+ regions.push(...visualRows[index]?.hitRegions ?? []);
3529
+ }
3530
+ return regions;
3531
+ }
3532
+ function tryUpdateWrappedLayoutFromChangedRange(count, changedRange, deps) {
3533
+ const previous = previousWrappedLayout;
3534
+ if (!previous || previous.fixedRows || previous.rowCount !== count)
3535
+ return null;
3536
+ if (!changedRange || !sameWrappedDeps(previousWrappedDeps, deps))
3537
+ return null;
3538
+ const changedCount = changedRange.end - changedRange.start;
3539
+ if (changedCount <= 0) {
3540
+ canUseWrappedChangedRangeRepaint = true;
3541
+ return {
3542
+ state: { ...previous, regions: visibleRegionsForVisualRows(previous.visualRows) },
3543
+ cacheHit: count,
3544
+ cacheMiss: 0
3545
+ };
3546
+ }
3547
+ const oldVisualStart = previous.rowStarts.get(changedRange.start);
3548
+ const oldVisualEnd = previous.rowEnds.get(changedRange.end - 1);
3549
+ if (oldVisualStart == null || oldVisualEnd == null)
3550
+ return null;
3551
+ const nextRows = [];
3552
+ const changedRowHeights = [];
3553
+ let cacheHit = count - changedCount;
3554
+ let cacheMiss = 0;
3555
+ for (let rowIndex = changedRange.start; rowIndex < changedRange.end; rowIndex++) {
3556
+ const result = layoutSourceRow(rowIndex, {
3557
+ width: deps.width,
3558
+ baseStyle: deps.baseStyle,
3559
+ hoverRegionId: deps.hoverRegionId,
3560
+ focusedRegionId: deps.focusedRegionId
3561
+ });
3562
+ if (result.cacheHit)
3563
+ cacheHit++;
3564
+ else
3565
+ cacheMiss++;
3566
+ changedRowHeights.push(result.rows.length);
3567
+ nextRows.push(...result.rows);
3568
+ }
3569
+ if (nextRows.length !== oldVisualEnd - oldVisualStart)
3570
+ return null;
3571
+ canUseWrappedChangedRangeRepaint = true;
3572
+ const visualRows = previous.visualRows;
3573
+ for (let index = 0; index < nextRows.length; index++) {
3574
+ visualRows[oldVisualStart + index] = nextRows[index];
3575
+ }
3576
+ const rowStarts = previous.rowStarts;
3577
+ const rowEnds = previous.rowEnds;
3578
+ let visualCursor = oldVisualStart;
3579
+ for (let rowIndex = changedRange.start, offset = 0; rowIndex < changedRange.end; rowIndex++, offset++) {
3580
+ rowStarts.set(rowIndex, visualCursor);
3581
+ visualCursor += changedRowHeights[offset] ?? 0;
3582
+ rowEnds.set(rowIndex, visualCursor);
3583
+ }
3584
+ const state = {
3585
+ visualRows,
3586
+ rowStarts,
3587
+ rowEnds,
3588
+ regions: visibleRegionsForVisualRows(visualRows),
3589
+ fixedRows: false,
3590
+ rowCount: count
3591
+ };
3592
+ previousWrappedLayout = state;
3593
+ return { state, cacheHit, cacheMiss };
3594
+ }
3366
3595
  const layoutState = (0, import_vue11.computed)(() => {
3367
3596
  void props.version;
3597
+ canUseWrappedChangedRangeRepaint = false;
3368
3598
  const perfEnabled = observability.framePerf.enabled.value;
3369
3599
  const perfStartedAt = perfEnabled ? framePerfNow() : 0;
3370
3600
  const width = Math.max(1, Math.floor(props.w));
3371
3601
  const count = Math.max(0, normalizeInt2(props.source.rowCount()));
3372
3602
  if (!count) {
3373
3603
  rowLayoutCache.clear();
3604
+ keyedRowLayoutCache.clear();
3605
+ previousWrappedLayout = null;
3606
+ previousWrappedDeps = null;
3374
3607
  if (perfEnabled) {
3375
3608
  observability.framePerf.recordComponent({
3376
3609
  name: "TTranscriptView",
@@ -3387,7 +3620,10 @@ var TTranscriptView = (0, import_vue11.defineComponent)({
3387
3620
  }
3388
3621
  return EMPTY_LAYOUT;
3389
3622
  }
3623
+ const fixedRows = props.wrap === false;
3390
3624
  const visualRows = [];
3625
+ if (fixedRows)
3626
+ visualRows.length = count;
3391
3627
  const rowStarts = /* @__PURE__ */ new Map();
3392
3628
  const rowEnds = /* @__PURE__ */ new Map();
3393
3629
  const regions = [];
@@ -3396,59 +3632,81 @@ var TTranscriptView = (0, import_vue11.defineComponent)({
3396
3632
  const focusedRegionId = focusedRegion.value?.id ?? null;
3397
3633
  let cacheHit = 0;
3398
3634
  let cacheMiss = 0;
3399
- for (let rowIndex = 0; rowIndex < count; rowIndex++) {
3400
- const row = props.source.getRow(rowIndex);
3401
- const rowKey = props.source.getRowKey?.(rowIndex) ?? row.key;
3402
- const sourceRowVersion = props.source.getRowVersion?.(rowIndex);
3403
- const localHoverRegionId = rowHasRegionId(row, rowKey, hoverRegionId) ? hoverRegionId : null;
3404
- const localFocusedRegionId = rowHasRegionId(row, rowKey, focusedRegionId) ? focusedRegionId : null;
3405
- const cacheKey = {
3406
- version: props.version,
3407
- rowVersion: sourceRowVersion ?? props.version,
3408
- usesSourceRowVersion: sourceRowVersion != null,
3409
- row,
3410
- rowIndex,
3411
- rowKey,
3412
- width,
3413
- wrap: props.wrap,
3414
- baseStyle,
3415
- hoverStyle: props.hoverStyle,
3416
- focusStyle: props.focusStyle,
3417
- hoverRegionId: localHoverRegionId,
3418
- focusedRegionId: localFocusedRegionId
3419
- };
3420
- rowStarts.set(rowIndex, visualRows.length);
3421
- const cached = rowLayoutCache.get(rowIndex);
3422
- const cacheMatches = sameCachedLayout(cached, cacheKey);
3423
- if (cacheMatches)
3424
- cacheHit++;
3425
- else
3426
- cacheMiss++;
3427
- const rows = cacheMatches ? cached.visualRows : layoutTranscriptRow({
3428
- row,
3429
- rowIndex,
3430
- rowKey,
3431
- width,
3432
- baseStyle,
3433
- hoverRegionId: localHoverRegionId,
3434
- focusedRegionId: localFocusedRegionId,
3435
- hoverStyle: props.hoverStyle,
3436
- focusStyle: props.focusStyle,
3437
- wrap: props.wrap
3438
- });
3439
- if (rows !== cached?.visualRows)
3440
- rowLayoutCache.set(rowIndex, { ...cacheKey, visualRows: rows });
3441
- for (const visualRow of rows) {
3442
- visualRows.push(visualRow);
3443
- regions.push(...visualRow.hitRegions);
3635
+ const sourceChangedRange = normalizedChangedRange(props.source.getChangedRange?.(), count);
3636
+ const wrappedDeps = {
3637
+ width,
3638
+ baseStyle,
3639
+ hoverStyle: props.hoverStyle,
3640
+ focusStyle: props.focusStyle,
3641
+ hoverRegionId,
3642
+ focusedRegionId
3643
+ };
3644
+ if (fixedRows) {
3645
+ previousWrappedLayout = null;
3646
+ previousWrappedDeps = null;
3647
+ const top = currentScrollTopForRowCount(count);
3648
+ const bottom = Math.min(count, top + Math.max(0, normalizeInt2(props.h)));
3649
+ for (let rowIndex = top; rowIndex < bottom; rowIndex++) {
3650
+ rowStarts.set(rowIndex, rowIndex);
3651
+ rowEnds.set(rowIndex, rowIndex + 1);
3652
+ const result = layoutSourceRow(rowIndex, {
3653
+ width,
3654
+ baseStyle,
3655
+ hoverRegionId,
3656
+ focusedRegionId
3657
+ });
3658
+ if (result.cacheHit)
3659
+ cacheHit++;
3660
+ else
3661
+ cacheMiss++;
3662
+ const row = result.rows[0];
3663
+ visualRows[rowIndex] = row;
3664
+ regions.push(...row.hitRegions);
3665
+ }
3666
+ } else {
3667
+ const incremental = tryUpdateWrappedLayoutFromChangedRange(
3668
+ count,
3669
+ sourceChangedRange,
3670
+ wrappedDeps
3671
+ );
3672
+ if (incremental) {
3673
+ if (perfEnabled) {
3674
+ observability.framePerf.recordComponent({
3675
+ name: "TTranscriptView",
3676
+ id: instance?.uid == null ? void 0 : String(instance.uid),
3677
+ phase: "layout",
3678
+ durationMs: framePerfNow() - perfStartedAt,
3679
+ itemCount: count,
3680
+ renderedCount: incremental.state.visualRows.length,
3681
+ cacheHit: incremental.cacheHit,
3682
+ cacheMiss: incremental.cacheMiss,
3683
+ width,
3684
+ version: props.version
3685
+ });
3686
+ }
3687
+ return incremental.state;
3688
+ }
3689
+ for (let rowIndex = 0; rowIndex < count; rowIndex++) {
3690
+ rowStarts.set(rowIndex, visualRows.length);
3691
+ const result = layoutSourceRow(rowIndex, {
3692
+ width,
3693
+ baseStyle,
3694
+ hoverRegionId,
3695
+ focusedRegionId
3696
+ });
3697
+ if (result.cacheHit)
3698
+ cacheHit++;
3699
+ else
3700
+ cacheMiss++;
3701
+ for (const visualRow of result.rows) {
3702
+ visualRows.push(visualRow);
3703
+ regions.push(...visualRow.hitRegions);
3704
+ }
3705
+ rowEnds.set(rowIndex, visualRows.length);
3444
3706
  }
3445
- rowEnds.set(rowIndex, visualRows.length);
3446
- }
3447
- for (const rowIndex of rowLayoutCache.keys()) {
3448
- if (rowIndex >= count)
3449
- rowLayoutCache.delete(rowIndex);
3450
3707
  }
3451
- if (!warnedLargeFlattenedRows && globalThis.__VT_DEBUG_PERF__ && visualRows.length > 5e3) {
3708
+ pruneRowLayoutCache(count);
3709
+ if (!warnedLargeFlattenedRows && !fixedRows && globalThis.__VT_DEBUG_PERF__ && visualRows.length > 5e3) {
3452
3710
  warnedLargeFlattenedRows = true;
3453
3711
  console.warn(
3454
3712
  "[vue-tui] TTranscriptView flattens all transcript rows; use TLogView or windowed source for large retained output."
@@ -3461,14 +3719,22 @@ var TTranscriptView = (0, import_vue11.defineComponent)({
3461
3719
  phase: "layout",
3462
3720
  durationMs: framePerfNow() - perfStartedAt,
3463
3721
  itemCount: count,
3464
- renderedCount: visualRows.length,
3722
+ renderedCount: fixedRows ? cacheHit + cacheMiss : visualRows.length,
3465
3723
  cacheHit,
3466
3724
  cacheMiss,
3467
3725
  width,
3468
3726
  version: props.version
3469
3727
  });
3470
3728
  }
3471
- return { visualRows, rowStarts, rowEnds, regions };
3729
+ const state = { visualRows, rowStarts, rowEnds, regions, fixedRows, rowCount: count };
3730
+ if (fixedRows) {
3731
+ previousWrappedLayout = null;
3732
+ previousWrappedDeps = null;
3733
+ } else {
3734
+ previousWrappedLayout = state;
3735
+ previousWrappedDeps = wrappedDeps;
3736
+ }
3737
+ return state;
3472
3738
  });
3473
3739
  const maxScrollTop = (0, import_vue11.computed)(
3474
3740
  () => Math.max(0, layoutState.value.visualRows.length - Math.max(0, normalizeInt2(props.h)))
@@ -3517,8 +3783,11 @@ var TTranscriptView = (0, import_vue11.defineComponent)({
3517
3783
  return [];
3518
3784
  const rows = layoutState.value.visualRows;
3519
3785
  const indexes = [];
3520
- for (let i = 0; i < rows.length; i++) {
3521
- if (rows[i].hitRegions.some((candidate) => candidate.id === region.id))
3786
+ const top = layoutState.value.fixedRows ? clamp4(currentScrollTop(), 0, rows.length) : 0;
3787
+ const bottom = layoutState.value.fixedRows ? Math.min(rows.length, top + Math.max(0, normalizeInt2(props.h))) : rows.length;
3788
+ for (let i = top; i < bottom; i++) {
3789
+ const row = getVisualRow(i);
3790
+ if (row?.hitRegions.some((candidate) => candidate.id === region.id))
3522
3791
  indexes.push(i);
3523
3792
  }
3524
3793
  return indexes;
@@ -3551,7 +3820,7 @@ var TTranscriptView = (0, import_vue11.defineComponent)({
3551
3820
  const seen = /* @__PURE__ */ new Set();
3552
3821
  const regions = [];
3553
3822
  for (let i = top; i < bottom; i++) {
3554
- for (const region of rows[i]?.hitRegions ?? []) {
3823
+ for (const region of getVisualRow(i)?.hitRegions ?? []) {
3555
3824
  if (seen.has(region.id))
3556
3825
  continue;
3557
3826
  seen.add(region.id);
@@ -3565,7 +3834,7 @@ var TTranscriptView = (0, import_vue11.defineComponent)({
3565
3834
  const top = clamp4(currentScrollTop(), 0, rows.length);
3566
3835
  const bottom = Math.min(rows.length, top + Math.max(0, normalizeInt2(props.h)));
3567
3836
  for (let i = top; i < bottom; i++) {
3568
- if (rows[i]?.hitRegions.some((region) => region.id === id))
3837
+ if (getVisualRow(i)?.hitRegions.some((region) => region.id === id))
3569
3838
  return true;
3570
3839
  }
3571
3840
  return false;
@@ -3632,8 +3901,8 @@ var TTranscriptView = (0, import_vue11.defineComponent)({
3632
3901
  }
3633
3902
  function scrollToRow(index, options) {
3634
3903
  const rowIndex = clamp4(normalizeInt2(index), 0, Math.max(0, props.source.rowCount() - 1));
3635
- const visualIndex = layoutState.value.rowStarts.get(rowIndex) ?? 0;
3636
- const rowEnd = layoutState.value.rowEnds.get(rowIndex) ?? visualIndex + 1;
3904
+ const visualIndex = layoutState.value.fixedRows ? rowIndex : layoutState.value.rowStarts.get(rowIndex) ?? 0;
3905
+ const rowEnd = layoutState.value.fixedRows ? rowIndex + 1 : layoutState.value.rowEnds.get(rowIndex) ?? visualIndex + 1;
3637
3906
  const viewportRows = Math.max(1, normalizeInt2(props.h));
3638
3907
  const rowHeight = Math.max(1, rowEnd - visualIndex);
3639
3908
  let top = visualIndex;
@@ -3646,8 +3915,8 @@ var TTranscriptView = (0, import_vue11.defineComponent)({
3646
3915
  setScrollTop(top);
3647
3916
  }
3648
3917
  function invalidateRow(index) {
3649
- const start = layoutState.value.rowStarts.get(index);
3650
- const end = layoutState.value.rowEnds.get(index);
3918
+ const start = layoutState.value.fixedRows ? index : layoutState.value.rowStarts.get(index);
3919
+ const end = layoutState.value.fixedRows ? index + 1 : layoutState.value.rowEnds.get(index);
3651
3920
  if (start == null || end == null)
3652
3921
  return;
3653
3922
  rowsRef.value?.invalidateRange(start, end);
@@ -3655,12 +3924,27 @@ var TTranscriptView = (0, import_vue11.defineComponent)({
3655
3924
  function invalidateRange(start, end) {
3656
3925
  const first = clamp4(normalizeInt2(start), 0, props.source.rowCount());
3657
3926
  const last = clamp4(normalizeInt2(end), first, props.source.rowCount());
3658
- const visualStart = layoutState.value.rowStarts.get(first);
3659
- const visualEnd = layoutState.value.rowEnds.get(last - 1);
3927
+ const visualStart = layoutState.value.fixedRows ? first : layoutState.value.rowStarts.get(first);
3928
+ const visualEnd = layoutState.value.fixedRows ? last : layoutState.value.rowEnds.get(last - 1);
3660
3929
  if (visualStart == null || visualEnd == null)
3661
3930
  return;
3662
3931
  rowsRef.value?.invalidateRange(visualStart, visualEnd);
3663
3932
  }
3933
+ function virtualRowsChangedRange() {
3934
+ const state = layoutState.value;
3935
+ const changedRange = normalizedChangedRange(props.source.getChangedRange?.(), state.rowCount);
3936
+ if (!changedRange)
3937
+ return null;
3938
+ if (state.fixedRows || changedRange.end <= changedRange.start)
3939
+ return changedRange;
3940
+ if (!canUseWrappedChangedRangeRepaint)
3941
+ return null;
3942
+ const visualStart = state.rowStarts.get(changedRange.start);
3943
+ const visualEnd = state.rowEnds.get(changedRange.end - 1);
3944
+ if (visualStart == null || visualEnd == null)
3945
+ return null;
3946
+ return { start: visualStart, end: visualEnd };
3947
+ }
3664
3948
  expose({
3665
3949
  scrollToBottom,
3666
3950
  scrollToTop,
@@ -3764,7 +4048,22 @@ var TTranscriptView = (0, import_vue11.defineComponent)({
3764
4048
  return text;
3765
4049
  }
3766
4050
  function getVisualRow(index) {
3767
- return layoutState.value.visualRows[index];
4051
+ const state = layoutState.value;
4052
+ const cached = state.visualRows[index];
4053
+ if (cached || !state.fixedRows)
4054
+ return cached;
4055
+ if (index < 0 || index >= state.visualRows.length)
4056
+ return void 0;
4057
+ const result = layoutSourceRow(index, {
4058
+ width: Math.max(1, Math.floor(props.w)),
4059
+ baseStyle: props.style ?? defaultStyle.value,
4060
+ hoverRegionId: hoveredRegion.value?.id ?? null,
4061
+ focusedRegionId: focusedRegion.value?.id ?? null
4062
+ });
4063
+ const row = result.rows[0];
4064
+ if (row)
4065
+ state.visualRows[index] = row;
4066
+ return row;
3768
4067
  }
3769
4068
  function selectionTextForVisualRow(visualRow) {
3770
4069
  if (!visualRow)
@@ -3792,6 +4091,7 @@ var TTranscriptView = (0, import_vue11.defineComponent)({
3792
4091
  zIndex: props.zIndex,
3793
4092
  itemCount: layoutState.value.visualRows.length,
3794
4093
  itemVersion: props.version,
4094
+ itemChangedRange: virtualRowsChangedRange,
3795
4095
  getItem: getVisualRow,
3796
4096
  paintItem: paintVisualRow,
3797
4097
  renderItemNodes: renderVisualRowNodes,
@@ -4675,6 +4975,20 @@ function wrapStyledSegmentsByCells(segments, width) {
4675
4975
  rowCells += cells;
4676
4976
  };
4677
4977
  for (const seg of segments) {
4978
+ const cells = textCellWidth(seg.text);
4979
+ if (cells === seg.text.length) {
4980
+ for (let i = 0; i < seg.text.length; ) {
4981
+ if (rowCells >= width)
4982
+ openRow();
4983
+ const take = Math.min(width - rowCells, seg.text.length - i);
4984
+ if (take <= 0)
4985
+ break;
4986
+ row.push({ text: seg.text.slice(i, i + take), cells: take, style: seg.style });
4987
+ rowCells += take;
4988
+ i += take;
4989
+ }
4990
+ continue;
4991
+ }
4678
4992
  forEachTextCellSegment(seg.text, (piece) => {
4679
4993
  if (!piece.text || piece.cells <= 0)
4680
4994
  return;
@@ -4683,6 +4997,116 @@ function wrapStyledSegmentsByCells(segments, width) {
4683
4997
  }
4684
4998
  return rows;
4685
4999
  }
5000
+ function countWrappedTextRows(text, width) {
5001
+ width = Math.max(1, Math.floor(width));
5002
+ if (!text)
5003
+ return 1;
5004
+ const cells = textCellWidth(text);
5005
+ if (cells <= 0)
5006
+ return 1;
5007
+ if (cells === text.length)
5008
+ return Math.max(1, Math.ceil(cells / width));
5009
+ let rows = 1;
5010
+ let rowCells = 0;
5011
+ forEachTextCellSegment(text, (piece) => {
5012
+ if (!piece.text || piece.cells <= 0)
5013
+ return;
5014
+ const pieceCells = piece.cells;
5015
+ if (rowCells > 0 && rowCells + pieceCells > width) {
5016
+ rows++;
5017
+ rowCells = 0;
5018
+ }
5019
+ if (pieceCells > width) {
5020
+ let remaining = pieceCells;
5021
+ while (remaining > 0) {
5022
+ if (rowCells >= width) {
5023
+ rows++;
5024
+ rowCells = 0;
5025
+ }
5026
+ const take = Math.min(width - rowCells, remaining);
5027
+ rowCells += take;
5028
+ remaining -= take;
5029
+ }
5030
+ return;
5031
+ }
5032
+ if (rowCells >= width) {
5033
+ rows++;
5034
+ rowCells = 0;
5035
+ }
5036
+ rowCells += pieceCells;
5037
+ });
5038
+ return rows;
5039
+ }
5040
+ function countWrappedPlainTextRows(text, width) {
5041
+ width = Math.max(1, Math.floor(width));
5042
+ if (!text)
5043
+ return 1;
5044
+ let rows = 0;
5045
+ let rowCells = 0;
5046
+ let sawText = false;
5047
+ forEachTextCellSegment(text, (piece) => {
5048
+ if (!piece.text || piece.cells <= 0)
5049
+ return;
5050
+ sawText = true;
5051
+ const cells = piece.cells;
5052
+ if (rowCells > 0 && rowCells + cells > width) {
5053
+ rows++;
5054
+ rowCells = 0;
5055
+ }
5056
+ rowCells += cells;
5057
+ if (rowCells >= width) {
5058
+ rows++;
5059
+ rowCells = 0;
5060
+ }
5061
+ });
5062
+ return Math.max(1, rows + (rowCells > 0 || !sawText ? 1 : 0));
5063
+ }
5064
+ function countAsciiAnsiTextCells(text) {
5065
+ let cells = 0;
5066
+ for (let i = 0; i < text.length; i++) {
5067
+ const code = text.charCodeAt(i);
5068
+ if (code > 127)
5069
+ return null;
5070
+ if (code === 27) {
5071
+ const next = text[i + 1];
5072
+ if (next === "[") {
5073
+ let j = i + 2;
5074
+ while (j < text.length) {
5075
+ const c = text.charCodeAt(j);
5076
+ if (c >= 64 && c <= 126)
5077
+ break;
5078
+ j++;
5079
+ }
5080
+ if (j >= text.length)
5081
+ return null;
5082
+ i = j;
5083
+ continue;
5084
+ }
5085
+ if (next === "]") {
5086
+ let j = i + 2;
5087
+ while (j < text.length) {
5088
+ const c = text.charCodeAt(j);
5089
+ if (c === 7)
5090
+ break;
5091
+ if (c === 27 && text[j + 1] === "\\") {
5092
+ j++;
5093
+ break;
5094
+ }
5095
+ j++;
5096
+ }
5097
+ if (j >= text.length)
5098
+ return null;
5099
+ i = j;
5100
+ continue;
5101
+ }
5102
+ return null;
5103
+ }
5104
+ if (code <= 31 || code === 127)
5105
+ return null;
5106
+ cells++;
5107
+ }
5108
+ return cells;
5109
+ }
4686
5110
  var TLogView = (0, import_vue12.defineComponent)({
4687
5111
  name: "TLogView",
4688
5112
  props: {
@@ -4821,6 +5245,7 @@ var TLogView = (0, import_vue12.defineComponent)({
4821
5245
  const renderLineCache = /* @__PURE__ */ new Map();
4822
5246
  const wrapLineCache = /* @__PURE__ */ new Map();
4823
5247
  const ansiLineCache = /* @__PURE__ */ new Map();
5248
+ const ansiTextCache = /* @__PURE__ */ new Map();
4824
5249
  const ansiWrapCache = /* @__PURE__ */ new Map();
4825
5250
  const ansiRowCache = /* @__PURE__ */ new Map();
4826
5251
  const linkifyLineCache = /* @__PURE__ */ new Map();
@@ -4942,6 +5367,9 @@ var TLogView = (0, import_vue12.defineComponent)({
4942
5367
  function ansiLineCacheKey(key, baseStyleKey, linkKey) {
4943
5368
  return JSON.stringify(["ansi-line", key, baseStyleKey, linksEnabled() ? 1 : 0, linkKey]);
4944
5369
  }
5370
+ function ansiTextCacheKey(key) {
5371
+ return JSON.stringify(["ansi-text", key]);
5372
+ }
4945
5373
  function linkifyLineCacheKey(key, baseStyleKey, linkKey, optionsKey) {
4946
5374
  return JSON.stringify(["linkify-line", key, baseStyleKey, linkKey, optionsKey]);
4947
5375
  }
@@ -5004,6 +5432,15 @@ var TLogView = (0, import_vue12.defineComponent)({
5004
5432
  ansiLineCache.delete(entry.key);
5005
5433
  }
5006
5434
  }
5435
+ function trimAnsiTextCache() {
5436
+ const max = DEFAULT_LOG_RENDER_CACHE_SIZE;
5437
+ if (ansiTextCache.size <= max)
5438
+ return;
5439
+ const entries = Array.from(ansiTextCache.values()).sort((a, b) => a.touchedAt - b.touchedAt);
5440
+ for (const entry of entries.slice(0, ansiTextCache.size - max)) {
5441
+ ansiTextCache.delete(entry.key);
5442
+ }
5443
+ }
5007
5444
  function trimAnsiWrapCache() {
5008
5445
  const max = DEFAULT_LOG_WRAP_CACHE_SIZE;
5009
5446
  if (ansiWrapCache.size <= max)
@@ -5059,6 +5496,7 @@ var TLogView = (0, import_vue12.defineComponent)({
5059
5496
  renderLineCache.clear();
5060
5497
  wrapLineCache.clear();
5061
5498
  ansiLineCache.clear();
5499
+ ansiTextCache.clear();
5062
5500
  ansiWrapCache.clear();
5063
5501
  ansiRowCache.clear();
5064
5502
  linkifyLineCache.clear();
@@ -5144,6 +5582,24 @@ var TLogView = (0, import_vue12.defineComponent)({
5144
5582
  });
5145
5583
  return segments;
5146
5584
  }
5585
+ function ansiPlainTextForLine(index, count) {
5586
+ if (index < 0 || index >= count)
5587
+ return "";
5588
+ const rawKey = lineKey(index);
5589
+ const key = ansiTextCacheKey(rawKey);
5590
+ const cached = ansiTextCache.get(key);
5591
+ if (cached) {
5592
+ cached.touchedAt = ++cacheClock;
5593
+ return cached.value;
5594
+ }
5595
+ const text = sanitizeAnsiInlineText(props.source.getLine(index));
5596
+ ansiTextCache.set(key, {
5597
+ key,
5598
+ value: text,
5599
+ touchedAt: ++cacheClock
5600
+ });
5601
+ return text;
5602
+ }
5147
5603
  function ansiWrappedRowsForLine(index, count, width, baseStyle, baseStyleKey, linkKey = linkStyleCacheKey()) {
5148
5604
  if (index < 0 || index >= count)
5149
5605
  return [[]];
@@ -5539,10 +5995,9 @@ var TLogView = (0, import_vue12.defineComponent)({
5539
5995
  lineMatches.push(matchIndex);
5540
5996
  invalidateSearchMarkers();
5541
5997
  }
5542
- function searchableTextForLine(index, count, baseStyle, baseStyleKey) {
5543
- if (props.ansi) {
5544
- return ansiSegmentsForLine(index, count, baseStyle, baseStyleKey).map((seg) => seg.text).join("");
5545
- }
5998
+ function searchableTextForLine(index, count) {
5999
+ if (props.ansi)
6000
+ return ansiPlainTextForLine(index, count);
5546
6001
  return sanitizeInlineText(props.source.getLine(index));
5547
6002
  }
5548
6003
  function normalizeSearchResultPreviewWidth(value) {
@@ -5557,8 +6012,8 @@ var TLogView = (0, import_vue12.defineComponent)({
5557
6012
  return DEFAULT_SEARCH_RESULTS_CONTEXT_CELLS;
5558
6013
  return Math.max(0, n);
5559
6014
  }
5560
- function previewForMatch(match, count, baseStyle, baseStyleKey, options) {
5561
- const text = searchableTextForLine(match.index, count, baseStyle, baseStyleKey);
6015
+ function previewForMatch(match, count, options) {
6016
+ const text = searchableTextForLine(match.index, count);
5562
6017
  const totalCells = textCellWidth(text);
5563
6018
  const matchCells = Math.max(1, match.endCell - match.startCell);
5564
6019
  const previewWidth = Math.max(options.previewWidth, matchCells);
@@ -5593,7 +6048,7 @@ var TLogView = (0, import_vue12.defineComponent)({
5593
6048
  matchEndCell
5594
6049
  };
5595
6050
  }
5596
- function cachedLineSearchMatches(index, count, search, baseStyle, baseStyleKey) {
6051
+ function cachedLineSearchMatches(index, count, search) {
5597
6052
  const rawKey = lineKey(index);
5598
6053
  const key = searchLineCacheKey(rawKey, search.key);
5599
6054
  const cached = searchLineCache.get(key);
@@ -5601,9 +6056,7 @@ var TLogView = (0, import_vue12.defineComponent)({
5601
6056
  cached.touchedAt = ++cacheClock;
5602
6057
  return cached.matches;
5603
6058
  }
5604
- const matches = search.findLineMatches(
5605
- searchableTextForLine(index, count, baseStyle, baseStyleKey)
5606
- );
6059
+ const matches = search.findLineMatches(searchableTextForLine(index, count));
5607
6060
  searchLineCache.set(key, {
5608
6061
  key,
5609
6062
  matches,
@@ -5611,10 +6064,10 @@ var TLogView = (0, import_vue12.defineComponent)({
5611
6064
  });
5612
6065
  return matches;
5613
6066
  }
5614
- function scanSearchLine(index, count, search, baseStyle, baseStyleKey, maxMatches, absoluteBase) {
6067
+ function scanSearchLine(index, count, search, maxMatches, absoluteBase) {
5615
6068
  if (searchMatches.length >= maxMatches)
5616
6069
  return;
5617
- const lineMatches = cachedLineSearchMatches(index, count, search, baseStyle, baseStyleKey);
6070
+ const lineMatches = cachedLineSearchMatches(index, count, search);
5618
6071
  for (const match of lineMatches) {
5619
6072
  if (searchMatches.length >= maxMatches)
5620
6073
  return;
@@ -5644,19 +6097,9 @@ var TLogView = (0, import_vue12.defineComponent)({
5644
6097
  const count = lineCount();
5645
6098
  const maxMatches = maxSearchMatches();
5646
6099
  const absoluteBase = firstLineIndex();
5647
- const base = props.style ?? defaultStyle.value;
5648
- const baseStyleKey = styleCacheKey(base);
5649
6100
  let scanned = 0;
5650
6101
  while (searchCursor < count && searchMatches.length < maxMatches && (scanned === 0 || ctx.now() - started < budget)) {
5651
- scanSearchLine(
5652
- searchCursor,
5653
- count,
5654
- compiledSearch,
5655
- base,
5656
- baseStyleKey,
5657
- maxMatches,
5658
- absoluteBase
5659
- );
6102
+ scanSearchLine(searchCursor, count, compiledSearch, maxMatches, absoluteBase);
5660
6103
  searchCursor++;
5661
6104
  scanned++;
5662
6105
  }
@@ -5677,6 +6120,7 @@ var TLogView = (0, import_vue12.defineComponent)({
5677
6120
  emitSearchMarkers(true);
5678
6121
  markViewportDirty();
5679
6122
  ctx.invalidate({ priority: "low", plane: plane.value, reason: "data" });
6123
+ trimAnsiTextCache();
5680
6124
  trimSearchLineCache();
5681
6125
  }
5682
6126
  function requestSearchScan() {
@@ -5948,22 +6392,39 @@ var TLogView = (0, import_vue12.defineComponent)({
5948
6392
  if (count > visualIndexLineCount)
5949
6393
  appendEstimatedVisualLines(visualIndexLineCount, count);
5950
6394
  }
5951
- function measureVisualLine(index) {
6395
+ function measureVisualLine(index, options) {
5952
6396
  ensureVisualIndex();
5953
6397
  if (index < 0 || index >= visualIndexLineCount)
5954
6398
  return 0;
5955
6399
  const key = lineKey(index);
5956
6400
  if (visualKeys[index] === key)
5957
6401
  return 0;
5958
- const base = props.style ?? defaultStyle.value;
5959
- const rows = props.ansi ? ansiWrappedRowsForLine(
5960
- index,
5961
- visualIndexLineCount,
5962
- visualIndexWidth,
5963
- base,
5964
- styleCacheKey(base)
5965
- ) : wrappedRowsForLine(index, visualIndexLineCount, visualIndexWidth);
5966
- const nextCount = Math.max(1, rows.length);
6402
+ let nextCount;
6403
+ if (options?.countOnly === true) {
6404
+ if (props.ansi) {
6405
+ const raw = props.source.getLine(index);
6406
+ const asciiCells = countAsciiAnsiTextCells(raw);
6407
+ nextCount = asciiCells == null ? countWrappedTextRows(
6408
+ ansiPlainTextForLine(index, visualIndexLineCount),
6409
+ visualIndexWidth
6410
+ ) : Math.max(1, Math.ceil(asciiCells / visualIndexWidth));
6411
+ } else {
6412
+ nextCount = countWrappedPlainTextRows(
6413
+ sanitizeInlineText(props.source.getLine(index)),
6414
+ visualIndexWidth
6415
+ );
6416
+ }
6417
+ } else {
6418
+ const base = props.style ?? defaultStyle.value;
6419
+ const rows = props.ansi ? ansiWrappedRowsForLine(
6420
+ index,
6421
+ visualIndexLineCount,
6422
+ visualIndexWidth,
6423
+ base,
6424
+ styleCacheKey(base)
6425
+ ) : wrappedRowsForLine(index, visualIndexLineCount, visualIndexWidth);
6426
+ nextCount = Math.max(1, rows.length);
6427
+ }
5967
6428
  const prevCount = visualCounts[index] ?? 1;
5968
6429
  if (nextCount !== prevCount) {
5969
6430
  visualCounts[index] = nextCount;
@@ -6009,6 +6470,7 @@ var TLogView = (0, import_vue12.defineComponent)({
6009
6470
  if (visualMeasureCursor >= target) {
6010
6471
  syncVisualIndexStatus();
6011
6472
  emitVisualIndex();
6473
+ trimAnsiTextCache();
6012
6474
  return;
6013
6475
  }
6014
6476
  const started = ctx.now();
@@ -6020,7 +6482,7 @@ var TLogView = (0, import_vue12.defineComponent)({
6020
6482
  const effectiveTop = currentScrollTop() + topAdjustment;
6021
6483
  const start = visualStartForLine(index);
6022
6484
  const prevCount = visualCounts[index] ?? 1;
6023
- const delta = measureVisualLine(index);
6485
+ const delta = measureVisualLine(index, { countOnly: true });
6024
6486
  if (delta !== 0 && effectiveTop >= start + prevCount)
6025
6487
  topAdjustment += delta;
6026
6488
  visualMeasureCursor++;
@@ -6056,6 +6518,7 @@ var TLogView = (0, import_vue12.defineComponent)({
6056
6518
  }
6057
6519
  syncVisualIndexStatus();
6058
6520
  emitVisualIndex();
6521
+ trimAnsiTextCache();
6059
6522
  }
6060
6523
  function requestVisualIndexMeasurement(restartFrom = measuredLineCount) {
6061
6524
  if (!props.wrap) {
@@ -6069,6 +6532,7 @@ var TLogView = (0, import_vue12.defineComponent)({
6069
6532
  if (measuredLineCount >= target) {
6070
6533
  syncVisualIndexStatus();
6071
6534
  emitVisualIndex();
6535
+ trimAnsiTextCache();
6072
6536
  return;
6073
6537
  }
6074
6538
  const generation = ++visualMeasureGeneration;
@@ -6661,7 +7125,7 @@ var TLogView = (0, import_vue12.defineComponent)({
6661
7125
  emitScroll(clampedTop);
6662
7126
  return true;
6663
7127
  }
6664
- const scrollPlan = !props.wrap ? prepareUnsafeFullRowScroll({
7128
+ const scrollPlan = prepareUnsafeFullRowScroll({
6665
7129
  render,
6666
7130
  plane: plane.value,
6667
7131
  rect: r,
@@ -6672,7 +7136,7 @@ var TLogView = (0, import_vue12.defineComponent)({
6672
7136
  isClipped: isClipped(),
6673
7137
  hasPendingDirtyRows: Boolean(dirtyRowsHint?.length),
6674
7138
  strategy
6675
- }) : null;
7139
+ });
6676
7140
  if (scrollPlan) {
6677
7141
  const rows = options?.extraDirtyRows?.length ? [...scrollPlan.exposedRows, ...options.extraDirtyRows] : scrollPlan.exposedRows;
6678
7142
  if (markRowsDirty(rows)) {
@@ -7237,14 +7701,12 @@ var TLogView = (0, import_vue12.defineComponent)({
7237
7701
  }));
7238
7702
  }
7239
7703
  const count = lineCount();
7240
- const base = props.style ?? defaultStyle.value;
7241
- const baseStyleKey = styleCacheKey(base);
7242
7704
  const previewWidth = normalizeSearchResultPreviewWidth(options?.previewWidth);
7243
7705
  const contextCells = normalizeSearchResultContextCells(options?.contextCells);
7244
7706
  return searchMatches.slice(offset, offset + limit).map((match, index) => ({
7245
7707
  matchIndex: offset + index,
7246
7708
  match: copySearchMatch(match),
7247
- preview: previewForMatch(match, count, base, baseStyleKey, {
7709
+ preview: previewForMatch(match, count, {
7248
7710
  previewWidth,
7249
7711
  contextCells
7250
7712
  })