@simon_he/vue-tui 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/agent.cjs CHANGED
@@ -6169,6 +6169,49 @@ var TSelect = (0, import_vue12.defineComponent)({
6169
6169
  const typeaheadQuery = (0, import_vue12.ref)("");
6170
6170
  const query = (0, import_vue12.computed)(() => props.query ?? innerQuery.value);
6171
6171
  const options = (0, import_vue12.computed)(() => providerOptions.value ?? props.options);
6172
+ function getOptionLabel(opt) {
6173
+ return isOptionObject(opt) ? opt.label : opt;
6174
+ }
6175
+ function getOptionKind(opt) {
6176
+ return isOptionObject(opt) ? opt.kind : void 0;
6177
+ }
6178
+ function getOptionValue(opt, index) {
6179
+ if (props.valueMode === "index")
6180
+ return index;
6181
+ if (props.valueMode === "option")
6182
+ return opt;
6183
+ return isOptionObject(opt) && "value" in opt ? opt.value : getOptionLabel(opt);
6184
+ }
6185
+ const optionValueIndex = (0, import_vue12.computed)(() => {
6186
+ if (props.valueMode === "index")
6187
+ return null;
6188
+ const indexed = /* @__PURE__ */ new Map();
6189
+ const opts = options.value;
6190
+ for (let index = 0; index < opts.length; index++) {
6191
+ const value = getOptionValue(opts[index], index);
6192
+ if (typeof value === "number" && value === 0)
6193
+ continue;
6194
+ if (!indexed.has(value))
6195
+ indexed.set(value, index);
6196
+ }
6197
+ return indexed;
6198
+ });
6199
+ function valuesEqual(a, b) {
6200
+ return Object.is(a, b);
6201
+ }
6202
+ function modelIndex(value, useValueIndex = props.multiple) {
6203
+ if (props.valueMode === "index")
6204
+ return normalizeModelIndex(value);
6205
+ if (useValueIndex) {
6206
+ const indexed = optionValueIndex.value;
6207
+ const cached = indexed?.get(value);
6208
+ if (cached != null)
6209
+ return cached;
6210
+ }
6211
+ return options.value.findIndex(
6212
+ (opt, optIndex) => valuesEqual(getOptionValue(opt, optIndex), value)
6213
+ );
6214
+ }
6172
6215
  const initialActive = (() => {
6173
6216
  const max = Math.max(0, options.value.length - 1);
6174
6217
  if (!props.multiple) {
@@ -6226,29 +6269,6 @@ var TSelect = (0, import_vue12.defineComponent)({
6226
6269
  }
6227
6270
  innerActive.value = clamp5(active.value, 0, max);
6228
6271
  });
6229
- function getOptionLabel(opt) {
6230
- return isOptionObject(opt) ? opt.label : opt;
6231
- }
6232
- function getOptionKind(opt) {
6233
- return isOptionObject(opt) ? opt.kind : void 0;
6234
- }
6235
- function getOptionValue(opt, index) {
6236
- if (props.valueMode === "index")
6237
- return index;
6238
- if (props.valueMode === "option")
6239
- return opt;
6240
- return isOptionObject(opt) && "value" in opt ? opt.value : getOptionLabel(opt);
6241
- }
6242
- function valuesEqual(a, b) {
6243
- return Object.is(a, b);
6244
- }
6245
- function modelIndex(value) {
6246
- if (props.valueMode === "index")
6247
- return normalizeModelIndex(value);
6248
- return options.value.findIndex(
6249
- (opt, optIndex) => valuesEqual(getOptionValue(opt, optIndex), value)
6250
- );
6251
- }
6252
6272
  function isOptionInteractive(opt) {
6253
6273
  if (!opt)
6254
6274
  return false;
@@ -7596,22 +7616,40 @@ function normalizeVisibleLimit2(value) {
7596
7616
  function isCommandPaletteRowSelectable(item) {
7597
7617
  return Boolean(item && item.kind !== "separator" && item.kind !== "group" && !item.disabled);
7598
7618
  }
7599
- function substringMatcher(item, query) {
7600
- const q = query.trim().toLowerCase();
7601
- if (!q)
7602
- return { score: 0 };
7603
- const labelRanges = computeCommandPaletteMatchRanges(item.label, q);
7604
- const detailRanges = item.detail ? computeCommandPaletteMatchRanges(item.detail, q) : [];
7605
- const keywordMatch = (item.keywords ?? []).some(
7606
- (keyword) => String(keyword ?? "").toLowerCase().includes(q)
7607
- );
7608
- if (!labelRanges.length && !detailRanges.length && !keywordMatch)
7609
- return null;
7610
- return {
7611
- score: labelRanges.length ? 100 : detailRanges.length ? 50 : 10,
7612
- labelRanges,
7613
- detailRanges
7614
- };
7619
+ function defaultSubstringEntries(sourceItems, query) {
7620
+ const q = query.trim();
7621
+ if (!q) {
7622
+ return sourceItems.map((item, sourceIndex) => ({
7623
+ item,
7624
+ sourceIndex,
7625
+ match: { score: 0 }
7626
+ }));
7627
+ }
7628
+ const needle = q.toLowerCase();
7629
+ const labelMatches = [];
7630
+ const detailMatches = [];
7631
+ const keywordMatches = [];
7632
+ for (let sourceIndex = 0; sourceIndex < sourceItems.length; sourceIndex++) {
7633
+ const item = sourceItems[sourceIndex];
7634
+ if (item.kind === "separator" || item.kind === "group")
7635
+ continue;
7636
+ const label = String(item.label ?? "");
7637
+ const detail = item.detail == null ? "" : String(item.detail);
7638
+ if (label.toLowerCase().includes(needle)) {
7639
+ labelMatches.push({ item, sourceIndex, match: { score: 100 } });
7640
+ continue;
7641
+ }
7642
+ if (detail.toLowerCase().includes(needle)) {
7643
+ detailMatches.push({ item, sourceIndex, match: { score: 50 } });
7644
+ continue;
7645
+ }
7646
+ if ((item.keywords ?? []).some(
7647
+ (keyword) => String(keyword ?? "").toLowerCase().includes(needle)
7648
+ )) {
7649
+ keywordMatches.push({ item, sourceIndex, match: { score: 10 } });
7650
+ }
7651
+ }
7652
+ return labelMatches.concat(detailMatches, keywordMatches);
7615
7653
  }
7616
7654
  function fuzzyMatcher(item, query) {
7617
7655
  const q = query.trim().toLowerCase();
@@ -7779,14 +7817,22 @@ var TCommandPalette = (0, import_vue16.defineComponent)({
7779
7817
  const filteredEntries = (0, import_vue16.computed)(() => {
7780
7818
  const q = query.value.trim();
7781
7819
  const sourceItems = props.itemsProvider ? providerItems.value ?? [] : props.items;
7782
- const matcher = props.matcher ?? (props.filterStrategy === "fuzzy" ? fuzzyMatcher : substringMatcher);
7783
- const entries = sourceItems.flatMap((item, sourceIndex) => {
7820
+ if (!props.matcher && props.filterStrategy === "substring") {
7821
+ return defaultSubstringEntries(sourceItems, q);
7822
+ }
7823
+ const matcher = props.matcher ?? fuzzyMatcher;
7824
+ const entries = [];
7825
+ for (let sourceIndex = 0; sourceIndex < sourceItems.length; sourceIndex++) {
7826
+ const item = sourceItems[sourceIndex];
7784
7827
  if (item.kind === "separator" || item.kind === "group") {
7785
- return q ? [] : [{ item, sourceIndex, match: { score: 0 } }];
7828
+ if (!q)
7829
+ entries.push({ item, sourceIndex, match: { score: 0 } });
7830
+ continue;
7786
7831
  }
7787
7832
  const match2 = matcher(item, q);
7788
- return match2 ? [{ item, sourceIndex, match: match2 }] : [];
7789
- });
7833
+ if (match2)
7834
+ entries.push({ item, sourceIndex, match: match2 });
7835
+ }
7790
7836
  entries.sort((a, b) => b.match.score - a.match.score || a.sourceIndex - b.sourceIndex);
7791
7837
  return entries;
7792
7838
  });
@@ -9879,6 +9925,13 @@ function normalizeInt(value) {
9879
9925
  const n = Math.floor(Number(value));
9880
9926
  return Number.isFinite(n) ? n : 0;
9881
9927
  }
9928
+ function normalizeChangedRange(range, count) {
9929
+ if (!range)
9930
+ return null;
9931
+ const start = clamp8(normalizeInt(range.start), 0, count);
9932
+ const end = clamp8(normalizeInt(range.end), start, count);
9933
+ return { start, end };
9934
+ }
9882
9935
  function getWheelScrollInput2(e) {
9883
9936
  const deltaY = Number(e.deltaY ?? 0);
9884
9937
  const deltaMode = typeof e.deltaMode === "number" ? e.deltaMode : void 0;
@@ -9903,6 +9956,10 @@ var TVirtualRows = (0, import_vue20.defineComponent)({
9903
9956
  zIndex: { type: Number, default: 0 },
9904
9957
  itemCount: { type: Number, required: true },
9905
9958
  itemVersion: { type: Number, required: true },
9959
+ itemChangedRange: {
9960
+ type: Function,
9961
+ default: void 0
9962
+ },
9906
9963
  getItem: { type: Function, required: true },
9907
9964
  paintItem: {
9908
9965
  type: Function,
@@ -10102,6 +10159,23 @@ var TVirtualRows = (0, import_vue20.defineComponent)({
10102
10159
  function invalidateSelf(priority = "normal") {
10103
10160
  scheduler.invalidate({ priority, plane: plane.value, reason: "scroll" });
10104
10161
  }
10162
+ function markScrollDamage(prevTop, nextTop, strategy = "auto") {
10163
+ const r = normalizedRect();
10164
+ const h24 = r.h;
10165
+ const delta = nextTop - prevTop;
10166
+ if (h24 <= 0 || !delta)
10167
+ return;
10168
+ const size = terminal.size();
10169
+ const ownsFullRows = Math.floor(r.x) === 0 && Math.floor(r.w) >= size.cols;
10170
+ const withinTerminalRows = r.y >= 0 && r.y + h24 <= size.rows;
10171
+ const canUseScrollPlane = strategy === "auto" && props.rowScrollMode === "unsafe-full-row" && rendererCapabilities.value.scrollOperations && ownsFullRows && withinTerminalRows && !isClipped() && Math.abs(delta) < h24 && !dirtyRowsHint?.length;
10172
+ if (canUseScrollPlane) {
10173
+ render.unsafeScrollPlaneRows(plane.value, r.y, r.y + h24, delta);
10174
+ markRowsDirty(exposedRowsForDelta2(r.y, h24, delta));
10175
+ } else {
10176
+ markViewportDirty();
10177
+ }
10178
+ }
10105
10179
  function applyScrollTop(nextTop, options) {
10106
10180
  const r = normalizedRect();
10107
10181
  const h24 = r.h;
@@ -10122,16 +10196,7 @@ var TVirtualRows = (0, import_vue20.defineComponent)({
10122
10196
  setCurrentScrollTop(clampedTop);
10123
10197
  if (options?.emitUpdate !== false)
10124
10198
  emit("update:scrollTop", clampedTop);
10125
- const size = terminal.size();
10126
- const ownsFullRows = Math.floor(r.x) === 0 && Math.floor(r.w) >= size.cols;
10127
- const withinTerminalRows = r.y >= 0 && r.y + h24 <= size.rows;
10128
- const canUseScrollPlane = (options?.strategy ?? "auto") === "auto" && props.rowScrollMode === "unsafe-full-row" && rendererCapabilities.value.scrollOperations && ownsFullRows && withinTerminalRows && !isClipped() && Math.abs(delta) < h24 && !dirtyRowsHint?.length;
10129
- if (canUseScrollPlane) {
10130
- render.unsafeScrollPlaneRows(plane.value, r.y, r.y + h24, delta);
10131
- markRowsDirty(exposedRowsForDelta2(r.y, h24, delta));
10132
- } else {
10133
- markViewportDirty();
10134
- }
10199
+ markScrollDamage(prevTop, clampedTop, options?.strategy);
10135
10200
  if (options?.emitScroll)
10136
10201
  emitScroll(clampedTop);
10137
10202
  return true;
@@ -10198,8 +10263,11 @@ var TVirtualRows = (0, import_vue20.defineComponent)({
10198
10263
  invalidateSelf("normal");
10199
10264
  }
10200
10265
  function invalidateRange(start, end) {
10266
+ markItemRangeDirty(start, end);
10267
+ }
10268
+ function markItemRangeDirty(start, end) {
10201
10269
  if (!hasPaintableViewport())
10202
- return;
10270
+ return false;
10203
10271
  const r = normalizedRect();
10204
10272
  const { y: clipY } = clipOffsets();
10205
10273
  const top = currentScrollTop() + clipY;
@@ -10212,9 +10280,10 @@ var TVirtualRows = (0, import_vue20.defineComponent)({
10212
10280
  rows.push(y);
10213
10281
  }
10214
10282
  if (!rows.length)
10215
- return;
10283
+ return true;
10216
10284
  markRowsDirty(rows);
10217
10285
  invalidateSelf("normal");
10286
+ return true;
10218
10287
  }
10219
10288
  function invalidateIndex(index) {
10220
10289
  const n = normalizeInt(index);
@@ -10454,8 +10523,9 @@ var TVirtualRows = (0, import_vue20.defineComponent)({
10454
10523
  return;
10455
10524
  }
10456
10525
  cancelWheelScrollFrame();
10526
+ const prevTop = controlledScrollTop.value;
10457
10527
  controlledScrollTop.value = nextTop;
10458
- markViewportDirty();
10528
+ markScrollDamage(prevTop, nextTop);
10459
10529
  selection.refresh(pendingSelectionScrollFocusRemap ? { remapFocus: true } : void 0);
10460
10530
  pendingSelectionScrollFocusRemap = false;
10461
10531
  invalidateSelf("high");
@@ -10479,7 +10549,7 @@ var TVirtualRows = (0, import_vue20.defineComponent)({
10479
10549
  () => absRect.value.w,
10480
10550
  () => absRect.value.h
10481
10551
  ],
10482
- () => {
10552
+ (next, prev) => {
10483
10553
  resetWheelScrollState(wheelState);
10484
10554
  const nextTop = clampScrollTop(currentScrollTop());
10485
10555
  if (isScrollControlled()) {
@@ -10490,6 +10560,16 @@ var TVirtualRows = (0, import_vue20.defineComponent)({
10490
10560
  } else {
10491
10561
  setCurrentScrollTop(nextTop);
10492
10562
  }
10563
+ const onlyItemVersionChanged = Boolean(
10564
+ 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]
10565
+ );
10566
+ if (onlyItemVersionChanged) {
10567
+ const changedRange = normalizeChangedRange(props.itemChangedRange?.(), itemCount.value);
10568
+ if (changedRange) {
10569
+ markItemRangeDirty(changedRange.start, changedRange.end);
10570
+ return;
10571
+ }
10572
+ }
10493
10573
  markViewportDirty();
10494
10574
  invalidateSelf("normal");
10495
10575
  },
@@ -10517,7 +10597,6 @@ var TVirtualRows = (0, import_vue20.defineComponent)({
10517
10597
  absRect.value,
10518
10598
  fullRect.value,
10519
10599
  itemCount.value,
10520
- props.itemVersion,
10521
10600
  props.getItem,
10522
10601
  props.paintItem,
10523
10602
  props.selectionSpanText,
@@ -10606,8 +10685,11 @@ var EMPTY_LAYOUT = Object.freeze({
10606
10685
  visualRows: Object.freeze([]),
10607
10686
  rowStarts: /* @__PURE__ */ new Map(),
10608
10687
  rowEnds: /* @__PURE__ */ new Map(),
10609
- regions: Object.freeze([])
10688
+ regions: Object.freeze([]),
10689
+ fixedRows: false,
10690
+ rowCount: 0
10610
10691
  });
10692
+ var MAX_KEYED_ROW_LAYOUT_CACHE_ENTRIES = 2048;
10611
10693
  function clamp9(n, min, max) {
10612
10694
  return Math.max(min, Math.min(max, n));
10613
10695
  }
@@ -10615,6 +10697,13 @@ function normalizeInt2(value) {
10615
10697
  const n = Math.floor(Number(value));
10616
10698
  return Number.isFinite(n) ? n : 0;
10617
10699
  }
10700
+ function normalizedChangedRange(range, count) {
10701
+ if (!range)
10702
+ return null;
10703
+ const start = clamp9(normalizeInt2(range.start), 0, count);
10704
+ const end = clamp9(normalizeInt2(range.end), start, count);
10705
+ return { start, end };
10706
+ }
10618
10707
  function sourceSegmentsForRegion(row) {
10619
10708
  if (row.kind === "message")
10620
10709
  return row.segments;
@@ -10651,8 +10740,27 @@ function rowHasRegionId(row, rowKey, id) {
10651
10740
  }
10652
10741
  return false;
10653
10742
  }
10654
- function sameCachedLayout(entry, next) {
10655
- 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;
10743
+ function cachedLayoutHasRegionId(entry, id) {
10744
+ if (!entry || !id)
10745
+ return false;
10746
+ for (const visualRow of entry.visualRows) {
10747
+ if (visualRow.hitRegions.some((region) => region.id === id))
10748
+ return true;
10749
+ }
10750
+ return false;
10751
+ }
10752
+ function rowLayoutIdentityKey(rowKey, rowVersion) {
10753
+ return JSON.stringify([rowKey, rowVersion]);
10754
+ }
10755
+ function reindexVisualRows(rows, rowIndex) {
10756
+ return rows.map((visualRow) => ({
10757
+ ...visualRow,
10758
+ rowIndex,
10759
+ hitRegions: visualRow.hitRegions.map((region) => ({ ...region, rowIndex }))
10760
+ }));
10761
+ }
10762
+ function sameCachedLayout(entry, next, matchRowIndex = true) {
10763
+ 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;
10656
10764
  }
10657
10765
  var TTranscriptView = (0, import_vue21.defineComponent)({
10658
10766
  name: "TTranscriptView",
@@ -10702,6 +10810,10 @@ var TTranscriptView = (0, import_vue21.defineComponent)({
10702
10810
  const hoveredRegion = (0, import_vue21.ref)(null);
10703
10811
  const focusedRegion = (0, import_vue21.ref)(null);
10704
10812
  const rowLayoutCache = /* @__PURE__ */ new Map();
10813
+ const keyedRowLayoutCache = /* @__PURE__ */ new Map();
10814
+ let previousWrappedLayout = null;
10815
+ let previousWrappedDeps = null;
10816
+ let canUseWrappedChangedRangeRepaint = false;
10705
10817
  let warnedLargeFlattenedRows = false;
10706
10818
  function isScrollControlled() {
10707
10819
  return Object.prototype.hasOwnProperty.call(instance?.vnode.props ?? {}, "scrollTop");
@@ -10709,14 +10821,181 @@ var TTranscriptView = (0, import_vue21.defineComponent)({
10709
10821
  function currentScrollTop() {
10710
10822
  return normalizeInt2(isScrollControlled() ? props.scrollTop : innerScrollTop.value);
10711
10823
  }
10824
+ function currentScrollTopForRowCount(count) {
10825
+ const viewportRows = Math.max(0, normalizeInt2(props.h));
10826
+ const maxTop = Math.max(0, count - viewportRows);
10827
+ return clamp9(currentScrollTop(), 0, maxTop);
10828
+ }
10829
+ function rememberKeyedRowLayout(entry) {
10830
+ if (!entry.usesSourceRowVersion)
10831
+ return;
10832
+ keyedRowLayoutCache.set(rowLayoutIdentityKey(entry.rowKey, entry.rowVersion), entry);
10833
+ if (keyedRowLayoutCache.size <= MAX_KEYED_ROW_LAYOUT_CACHE_ENTRIES)
10834
+ return;
10835
+ const first = keyedRowLayoutCache.keys().next().value;
10836
+ if (first != null)
10837
+ keyedRowLayoutCache.delete(first);
10838
+ }
10839
+ function layoutSourceRow(rowIndex, options) {
10840
+ const sourceRowVersion = props.source.getRowVersion?.(rowIndex);
10841
+ let cached = rowLayoutCache.get(rowIndex);
10842
+ const usesSourceRowVersion = sourceRowVersion != null;
10843
+ let row = cached?.row;
10844
+ let rowKey = cached?.rowKey;
10845
+ let canUseCachedRow = false;
10846
+ if (usesSourceRowVersion && cached?.usesSourceRowVersion && cached.rowVersion === sourceRowVersion) {
10847
+ if (props.source.getRowKey) {
10848
+ rowKey = props.source.getRowKey(rowIndex);
10849
+ canUseCachedRow = rowKey === cached.rowKey;
10850
+ } else {
10851
+ rowKey = cached.rowKey;
10852
+ canUseCachedRow = true;
10853
+ }
10854
+ }
10855
+ if (!canUseCachedRow && usesSourceRowVersion && props.source.getRowKey) {
10856
+ rowKey = props.source.getRowKey(rowIndex);
10857
+ cached = keyedRowLayoutCache.get(rowLayoutIdentityKey(rowKey, sourceRowVersion));
10858
+ if (cached) {
10859
+ row = cached.row;
10860
+ canUseCachedRow = true;
10861
+ }
10862
+ }
10863
+ if (!canUseCachedRow || row == null || rowKey == null) {
10864
+ row = props.source.getRow(rowIndex);
10865
+ rowKey = props.source.getRowKey?.(rowIndex) ?? row.key;
10866
+ canUseCachedRow = false;
10867
+ }
10868
+ const nextHoverRegionId = canUseCachedRow ? cachedLayoutHasRegionId(cached, options.hoverRegionId) ? options.hoverRegionId : null : rowHasRegionId(row, rowKey, options.hoverRegionId) ? options.hoverRegionId : null;
10869
+ const nextFocusedRegionId = canUseCachedRow ? cachedLayoutHasRegionId(cached, options.focusedRegionId) ? options.focusedRegionId : null : rowHasRegionId(row, rowKey, options.focusedRegionId) ? options.focusedRegionId : null;
10870
+ const cacheKey = {
10871
+ version: props.version,
10872
+ rowVersion: sourceRowVersion ?? props.version,
10873
+ usesSourceRowVersion,
10874
+ row,
10875
+ rowIndex,
10876
+ rowKey,
10877
+ width: options.width,
10878
+ wrap: props.wrap,
10879
+ baseStyle: options.baseStyle,
10880
+ hoverStyle: props.hoverStyle,
10881
+ focusStyle: props.focusStyle,
10882
+ hoverRegionId: nextHoverRegionId,
10883
+ focusedRegionId: nextFocusedRegionId
10884
+ };
10885
+ const cacheMatches = sameCachedLayout(cached, cacheKey);
10886
+ const reindexableCacheMatches = !cacheMatches && canUseCachedRow && sameCachedLayout(cached, cacheKey, false);
10887
+ const rows = cacheMatches ? cached.visualRows : reindexableCacheMatches ? reindexVisualRows(cached.visualRows, rowIndex) : layoutTranscriptRow({
10888
+ row,
10889
+ rowIndex,
10890
+ rowKey,
10891
+ width: options.width,
10892
+ baseStyle: options.baseStyle,
10893
+ hoverRegionId: nextHoverRegionId,
10894
+ focusedRegionId: nextFocusedRegionId,
10895
+ hoverStyle: props.hoverStyle,
10896
+ focusStyle: props.focusStyle,
10897
+ wrap: props.wrap
10898
+ });
10899
+ if (rows !== cached?.visualRows) {
10900
+ const entry = { ...cacheKey, visualRows: rows };
10901
+ rowLayoutCache.set(rowIndex, entry);
10902
+ rememberKeyedRowLayout(entry);
10903
+ }
10904
+ return { rows, cacheHit: cacheMatches || reindexableCacheMatches };
10905
+ }
10906
+ function pruneRowLayoutCache(count) {
10907
+ for (const rowIndex of rowLayoutCache.keys()) {
10908
+ if (rowIndex >= count)
10909
+ rowLayoutCache.delete(rowIndex);
10910
+ }
10911
+ }
10912
+ function sameWrappedDeps(a, b) {
10913
+ 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;
10914
+ }
10915
+ function visibleRegionsForVisualRows(visualRows) {
10916
+ const top = clamp9(currentScrollTop(), 0, visualRows.length);
10917
+ const bottom = Math.min(visualRows.length, top + Math.max(0, normalizeInt2(props.h)));
10918
+ const regions = [];
10919
+ for (let index = top; index < bottom; index++) {
10920
+ regions.push(...visualRows[index]?.hitRegions ?? []);
10921
+ }
10922
+ return regions;
10923
+ }
10924
+ function tryUpdateWrappedLayoutFromChangedRange(count, changedRange, deps) {
10925
+ const previous = previousWrappedLayout;
10926
+ if (!previous || previous.fixedRows || previous.rowCount !== count)
10927
+ return null;
10928
+ if (!changedRange || !sameWrappedDeps(previousWrappedDeps, deps))
10929
+ return null;
10930
+ const changedCount = changedRange.end - changedRange.start;
10931
+ if (changedCount <= 0) {
10932
+ canUseWrappedChangedRangeRepaint = true;
10933
+ return {
10934
+ state: { ...previous, regions: visibleRegionsForVisualRows(previous.visualRows) },
10935
+ cacheHit: count,
10936
+ cacheMiss: 0
10937
+ };
10938
+ }
10939
+ const oldVisualStart = previous.rowStarts.get(changedRange.start);
10940
+ const oldVisualEnd = previous.rowEnds.get(changedRange.end - 1);
10941
+ if (oldVisualStart == null || oldVisualEnd == null)
10942
+ return null;
10943
+ const nextRows = [];
10944
+ const changedRowHeights = [];
10945
+ let cacheHit = count - changedCount;
10946
+ let cacheMiss = 0;
10947
+ for (let rowIndex = changedRange.start; rowIndex < changedRange.end; rowIndex++) {
10948
+ const result = layoutSourceRow(rowIndex, {
10949
+ width: deps.width,
10950
+ baseStyle: deps.baseStyle,
10951
+ hoverRegionId: deps.hoverRegionId,
10952
+ focusedRegionId: deps.focusedRegionId
10953
+ });
10954
+ if (result.cacheHit)
10955
+ cacheHit++;
10956
+ else
10957
+ cacheMiss++;
10958
+ changedRowHeights.push(result.rows.length);
10959
+ nextRows.push(...result.rows);
10960
+ }
10961
+ if (nextRows.length !== oldVisualEnd - oldVisualStart)
10962
+ return null;
10963
+ canUseWrappedChangedRangeRepaint = true;
10964
+ const visualRows = previous.visualRows;
10965
+ for (let index = 0; index < nextRows.length; index++) {
10966
+ visualRows[oldVisualStart + index] = nextRows[index];
10967
+ }
10968
+ const rowStarts = previous.rowStarts;
10969
+ const rowEnds = previous.rowEnds;
10970
+ let visualCursor = oldVisualStart;
10971
+ for (let rowIndex = changedRange.start, offset = 0; rowIndex < changedRange.end; rowIndex++, offset++) {
10972
+ rowStarts.set(rowIndex, visualCursor);
10973
+ visualCursor += changedRowHeights[offset] ?? 0;
10974
+ rowEnds.set(rowIndex, visualCursor);
10975
+ }
10976
+ const state = {
10977
+ visualRows,
10978
+ rowStarts,
10979
+ rowEnds,
10980
+ regions: visibleRegionsForVisualRows(visualRows),
10981
+ fixedRows: false,
10982
+ rowCount: count
10983
+ };
10984
+ previousWrappedLayout = state;
10985
+ return { state, cacheHit, cacheMiss };
10986
+ }
10712
10987
  const layoutState = (0, import_vue21.computed)(() => {
10713
10988
  void props.version;
10989
+ canUseWrappedChangedRangeRepaint = false;
10714
10990
  const perfEnabled = observability.framePerf.enabled.value;
10715
10991
  const perfStartedAt = perfEnabled ? framePerfNow() : 0;
10716
10992
  const width = Math.max(1, Math.floor(props.w));
10717
10993
  const count = Math.max(0, normalizeInt2(props.source.rowCount()));
10718
10994
  if (!count) {
10719
10995
  rowLayoutCache.clear();
10996
+ keyedRowLayoutCache.clear();
10997
+ previousWrappedLayout = null;
10998
+ previousWrappedDeps = null;
10720
10999
  if (perfEnabled) {
10721
11000
  observability.framePerf.recordComponent({
10722
11001
  name: "TTranscriptView",
@@ -10733,7 +11012,10 @@ var TTranscriptView = (0, import_vue21.defineComponent)({
10733
11012
  }
10734
11013
  return EMPTY_LAYOUT;
10735
11014
  }
11015
+ const fixedRows = props.wrap === false;
10736
11016
  const visualRows = [];
11017
+ if (fixedRows)
11018
+ visualRows.length = count;
10737
11019
  const rowStarts = /* @__PURE__ */ new Map();
10738
11020
  const rowEnds = /* @__PURE__ */ new Map();
10739
11021
  const regions = [];
@@ -10742,59 +11024,81 @@ var TTranscriptView = (0, import_vue21.defineComponent)({
10742
11024
  const focusedRegionId = focusedRegion.value?.id ?? null;
10743
11025
  let cacheHit = 0;
10744
11026
  let cacheMiss = 0;
10745
- for (let rowIndex = 0; rowIndex < count; rowIndex++) {
10746
- const row = props.source.getRow(rowIndex);
10747
- const rowKey = props.source.getRowKey?.(rowIndex) ?? row.key;
10748
- const sourceRowVersion = props.source.getRowVersion?.(rowIndex);
10749
- const localHoverRegionId = rowHasRegionId(row, rowKey, hoverRegionId) ? hoverRegionId : null;
10750
- const localFocusedRegionId = rowHasRegionId(row, rowKey, focusedRegionId) ? focusedRegionId : null;
10751
- const cacheKey = {
10752
- version: props.version,
10753
- rowVersion: sourceRowVersion ?? props.version,
10754
- usesSourceRowVersion: sourceRowVersion != null,
10755
- row,
10756
- rowIndex,
10757
- rowKey,
10758
- width,
10759
- wrap: props.wrap,
10760
- baseStyle,
10761
- hoverStyle: props.hoverStyle,
10762
- focusStyle: props.focusStyle,
10763
- hoverRegionId: localHoverRegionId,
10764
- focusedRegionId: localFocusedRegionId
10765
- };
10766
- rowStarts.set(rowIndex, visualRows.length);
10767
- const cached = rowLayoutCache.get(rowIndex);
10768
- const cacheMatches = sameCachedLayout(cached, cacheKey);
10769
- if (cacheMatches)
10770
- cacheHit++;
10771
- else
10772
- cacheMiss++;
10773
- const rows = cacheMatches ? cached.visualRows : layoutTranscriptRow({
10774
- row,
10775
- rowIndex,
10776
- rowKey,
10777
- width,
10778
- baseStyle,
10779
- hoverRegionId: localHoverRegionId,
10780
- focusedRegionId: localFocusedRegionId,
10781
- hoverStyle: props.hoverStyle,
10782
- focusStyle: props.focusStyle,
10783
- wrap: props.wrap
10784
- });
10785
- if (rows !== cached?.visualRows)
10786
- rowLayoutCache.set(rowIndex, { ...cacheKey, visualRows: rows });
10787
- for (const visualRow of rows) {
10788
- visualRows.push(visualRow);
10789
- regions.push(...visualRow.hitRegions);
11027
+ const sourceChangedRange = normalizedChangedRange(props.source.getChangedRange?.(), count);
11028
+ const wrappedDeps = {
11029
+ width,
11030
+ baseStyle,
11031
+ hoverStyle: props.hoverStyle,
11032
+ focusStyle: props.focusStyle,
11033
+ hoverRegionId,
11034
+ focusedRegionId
11035
+ };
11036
+ if (fixedRows) {
11037
+ previousWrappedLayout = null;
11038
+ previousWrappedDeps = null;
11039
+ const top = currentScrollTopForRowCount(count);
11040
+ const bottom = Math.min(count, top + Math.max(0, normalizeInt2(props.h)));
11041
+ for (let rowIndex = top; rowIndex < bottom; rowIndex++) {
11042
+ rowStarts.set(rowIndex, rowIndex);
11043
+ rowEnds.set(rowIndex, rowIndex + 1);
11044
+ const result = layoutSourceRow(rowIndex, {
11045
+ width,
11046
+ baseStyle,
11047
+ hoverRegionId,
11048
+ focusedRegionId
11049
+ });
11050
+ if (result.cacheHit)
11051
+ cacheHit++;
11052
+ else
11053
+ cacheMiss++;
11054
+ const row = result.rows[0];
11055
+ visualRows[rowIndex] = row;
11056
+ regions.push(...row.hitRegions);
11057
+ }
11058
+ } else {
11059
+ const incremental = tryUpdateWrappedLayoutFromChangedRange(
11060
+ count,
11061
+ sourceChangedRange,
11062
+ wrappedDeps
11063
+ );
11064
+ if (incremental) {
11065
+ if (perfEnabled) {
11066
+ observability.framePerf.recordComponent({
11067
+ name: "TTranscriptView",
11068
+ id: instance?.uid == null ? void 0 : String(instance.uid),
11069
+ phase: "layout",
11070
+ durationMs: framePerfNow() - perfStartedAt,
11071
+ itemCount: count,
11072
+ renderedCount: incremental.state.visualRows.length,
11073
+ cacheHit: incremental.cacheHit,
11074
+ cacheMiss: incremental.cacheMiss,
11075
+ width,
11076
+ version: props.version
11077
+ });
11078
+ }
11079
+ return incremental.state;
11080
+ }
11081
+ for (let rowIndex = 0; rowIndex < count; rowIndex++) {
11082
+ rowStarts.set(rowIndex, visualRows.length);
11083
+ const result = layoutSourceRow(rowIndex, {
11084
+ width,
11085
+ baseStyle,
11086
+ hoverRegionId,
11087
+ focusedRegionId
11088
+ });
11089
+ if (result.cacheHit)
11090
+ cacheHit++;
11091
+ else
11092
+ cacheMiss++;
11093
+ for (const visualRow of result.rows) {
11094
+ visualRows.push(visualRow);
11095
+ regions.push(...visualRow.hitRegions);
11096
+ }
11097
+ rowEnds.set(rowIndex, visualRows.length);
10790
11098
  }
10791
- rowEnds.set(rowIndex, visualRows.length);
10792
- }
10793
- for (const rowIndex of rowLayoutCache.keys()) {
10794
- if (rowIndex >= count)
10795
- rowLayoutCache.delete(rowIndex);
10796
11099
  }
10797
- if (!warnedLargeFlattenedRows && globalThis.__VT_DEBUG_PERF__ && visualRows.length > 5e3) {
11100
+ pruneRowLayoutCache(count);
11101
+ if (!warnedLargeFlattenedRows && !fixedRows && globalThis.__VT_DEBUG_PERF__ && visualRows.length > 5e3) {
10798
11102
  warnedLargeFlattenedRows = true;
10799
11103
  console.warn(
10800
11104
  "[vue-tui] TTranscriptView flattens all transcript rows; use TLogView or windowed source for large retained output."
@@ -10807,14 +11111,22 @@ var TTranscriptView = (0, import_vue21.defineComponent)({
10807
11111
  phase: "layout",
10808
11112
  durationMs: framePerfNow() - perfStartedAt,
10809
11113
  itemCount: count,
10810
- renderedCount: visualRows.length,
11114
+ renderedCount: fixedRows ? cacheHit + cacheMiss : visualRows.length,
10811
11115
  cacheHit,
10812
11116
  cacheMiss,
10813
11117
  width,
10814
11118
  version: props.version
10815
11119
  });
10816
11120
  }
10817
- return { visualRows, rowStarts, rowEnds, regions };
11121
+ const state = { visualRows, rowStarts, rowEnds, regions, fixedRows, rowCount: count };
11122
+ if (fixedRows) {
11123
+ previousWrappedLayout = null;
11124
+ previousWrappedDeps = null;
11125
+ } else {
11126
+ previousWrappedLayout = state;
11127
+ previousWrappedDeps = wrappedDeps;
11128
+ }
11129
+ return state;
10818
11130
  });
10819
11131
  const maxScrollTop = (0, import_vue21.computed)(
10820
11132
  () => Math.max(0, layoutState.value.visualRows.length - Math.max(0, normalizeInt2(props.h)))
@@ -10863,8 +11175,11 @@ var TTranscriptView = (0, import_vue21.defineComponent)({
10863
11175
  return [];
10864
11176
  const rows = layoutState.value.visualRows;
10865
11177
  const indexes = [];
10866
- for (let i = 0; i < rows.length; i++) {
10867
- if (rows[i].hitRegions.some((candidate) => candidate.id === region.id))
11178
+ const top = layoutState.value.fixedRows ? clamp9(currentScrollTop(), 0, rows.length) : 0;
11179
+ const bottom = layoutState.value.fixedRows ? Math.min(rows.length, top + Math.max(0, normalizeInt2(props.h))) : rows.length;
11180
+ for (let i = top; i < bottom; i++) {
11181
+ const row = getVisualRow(i);
11182
+ if (row?.hitRegions.some((candidate) => candidate.id === region.id))
10868
11183
  indexes.push(i);
10869
11184
  }
10870
11185
  return indexes;
@@ -10897,7 +11212,7 @@ var TTranscriptView = (0, import_vue21.defineComponent)({
10897
11212
  const seen = /* @__PURE__ */ new Set();
10898
11213
  const regions = [];
10899
11214
  for (let i = top; i < bottom; i++) {
10900
- for (const region of rows[i]?.hitRegions ?? []) {
11215
+ for (const region of getVisualRow(i)?.hitRegions ?? []) {
10901
11216
  if (seen.has(region.id))
10902
11217
  continue;
10903
11218
  seen.add(region.id);
@@ -10911,7 +11226,7 @@ var TTranscriptView = (0, import_vue21.defineComponent)({
10911
11226
  const top = clamp9(currentScrollTop(), 0, rows.length);
10912
11227
  const bottom = Math.min(rows.length, top + Math.max(0, normalizeInt2(props.h)));
10913
11228
  for (let i = top; i < bottom; i++) {
10914
- if (rows[i]?.hitRegions.some((region) => region.id === id))
11229
+ if (getVisualRow(i)?.hitRegions.some((region) => region.id === id))
10915
11230
  return true;
10916
11231
  }
10917
11232
  return false;
@@ -10978,8 +11293,8 @@ var TTranscriptView = (0, import_vue21.defineComponent)({
10978
11293
  }
10979
11294
  function scrollToRow(index, options) {
10980
11295
  const rowIndex = clamp9(normalizeInt2(index), 0, Math.max(0, props.source.rowCount() - 1));
10981
- const visualIndex = layoutState.value.rowStarts.get(rowIndex) ?? 0;
10982
- const rowEnd = layoutState.value.rowEnds.get(rowIndex) ?? visualIndex + 1;
11296
+ const visualIndex = layoutState.value.fixedRows ? rowIndex : layoutState.value.rowStarts.get(rowIndex) ?? 0;
11297
+ const rowEnd = layoutState.value.fixedRows ? rowIndex + 1 : layoutState.value.rowEnds.get(rowIndex) ?? visualIndex + 1;
10983
11298
  const viewportRows = Math.max(1, normalizeInt2(props.h));
10984
11299
  const rowHeight = Math.max(1, rowEnd - visualIndex);
10985
11300
  let top = visualIndex;
@@ -10992,8 +11307,8 @@ var TTranscriptView = (0, import_vue21.defineComponent)({
10992
11307
  setScrollTop(top);
10993
11308
  }
10994
11309
  function invalidateRow(index) {
10995
- const start = layoutState.value.rowStarts.get(index);
10996
- const end = layoutState.value.rowEnds.get(index);
11310
+ const start = layoutState.value.fixedRows ? index : layoutState.value.rowStarts.get(index);
11311
+ const end = layoutState.value.fixedRows ? index + 1 : layoutState.value.rowEnds.get(index);
10997
11312
  if (start == null || end == null)
10998
11313
  return;
10999
11314
  rowsRef.value?.invalidateRange(start, end);
@@ -11001,12 +11316,27 @@ var TTranscriptView = (0, import_vue21.defineComponent)({
11001
11316
  function invalidateRange(start, end) {
11002
11317
  const first = clamp9(normalizeInt2(start), 0, props.source.rowCount());
11003
11318
  const last = clamp9(normalizeInt2(end), first, props.source.rowCount());
11004
- const visualStart = layoutState.value.rowStarts.get(first);
11005
- const visualEnd = layoutState.value.rowEnds.get(last - 1);
11319
+ const visualStart = layoutState.value.fixedRows ? first : layoutState.value.rowStarts.get(first);
11320
+ const visualEnd = layoutState.value.fixedRows ? last : layoutState.value.rowEnds.get(last - 1);
11006
11321
  if (visualStart == null || visualEnd == null)
11007
11322
  return;
11008
11323
  rowsRef.value?.invalidateRange(visualStart, visualEnd);
11009
11324
  }
11325
+ function virtualRowsChangedRange() {
11326
+ const state = layoutState.value;
11327
+ const changedRange = normalizedChangedRange(props.source.getChangedRange?.(), state.rowCount);
11328
+ if (!changedRange)
11329
+ return null;
11330
+ if (state.fixedRows || changedRange.end <= changedRange.start)
11331
+ return changedRange;
11332
+ if (!canUseWrappedChangedRangeRepaint)
11333
+ return null;
11334
+ const visualStart = state.rowStarts.get(changedRange.start);
11335
+ const visualEnd = state.rowEnds.get(changedRange.end - 1);
11336
+ if (visualStart == null || visualEnd == null)
11337
+ return null;
11338
+ return { start: visualStart, end: visualEnd };
11339
+ }
11010
11340
  expose({
11011
11341
  scrollToBottom,
11012
11342
  scrollToTop,
@@ -11110,7 +11440,22 @@ var TTranscriptView = (0, import_vue21.defineComponent)({
11110
11440
  return text2;
11111
11441
  }
11112
11442
  function getVisualRow(index) {
11113
- return layoutState.value.visualRows[index];
11443
+ const state = layoutState.value;
11444
+ const cached = state.visualRows[index];
11445
+ if (cached || !state.fixedRows)
11446
+ return cached;
11447
+ if (index < 0 || index >= state.visualRows.length)
11448
+ return void 0;
11449
+ const result = layoutSourceRow(index, {
11450
+ width: Math.max(1, Math.floor(props.w)),
11451
+ baseStyle: props.style ?? defaultStyle.value,
11452
+ hoverRegionId: hoveredRegion.value?.id ?? null,
11453
+ focusedRegionId: focusedRegion.value?.id ?? null
11454
+ });
11455
+ const row = result.rows[0];
11456
+ if (row)
11457
+ state.visualRows[index] = row;
11458
+ return row;
11114
11459
  }
11115
11460
  function selectionTextForVisualRow(visualRow) {
11116
11461
  if (!visualRow)
@@ -11138,6 +11483,7 @@ var TTranscriptView = (0, import_vue21.defineComponent)({
11138
11483
  zIndex: props.zIndex,
11139
11484
  itemCount: layoutState.value.visualRows.length,
11140
11485
  itemVersion: props.version,
11486
+ itemChangedRange: virtualRowsChangedRange,
11141
11487
  getItem: getVisualRow,
11142
11488
  paintItem: paintVisualRow,
11143
11489
  renderItemNodes: renderVisualRowNodes,
@@ -11786,6 +12132,20 @@ function wrapStyledSegmentsByCells(segments, width) {
11786
12132
  rowCells += cells;
11787
12133
  };
11788
12134
  for (const seg of segments) {
12135
+ const cells = textCellWidth(seg.text);
12136
+ if (cells === seg.text.length) {
12137
+ for (let i = 0; i < seg.text.length; ) {
12138
+ if (rowCells >= width)
12139
+ openRow();
12140
+ const take = Math.min(width - rowCells, seg.text.length - i);
12141
+ if (take <= 0)
12142
+ break;
12143
+ row.push({ text: seg.text.slice(i, i + take), cells: take, style: seg.style });
12144
+ rowCells += take;
12145
+ i += take;
12146
+ }
12147
+ continue;
12148
+ }
11789
12149
  forEachTextCellSegment(seg.text, (piece) => {
11790
12150
  if (!piece.text || piece.cells <= 0)
11791
12151
  return;
@@ -11794,6 +12154,116 @@ function wrapStyledSegmentsByCells(segments, width) {
11794
12154
  }
11795
12155
  return rows;
11796
12156
  }
12157
+ function countWrappedTextRows(text2, width) {
12158
+ width = Math.max(1, Math.floor(width));
12159
+ if (!text2)
12160
+ return 1;
12161
+ const cells = textCellWidth(text2);
12162
+ if (cells <= 0)
12163
+ return 1;
12164
+ if (cells === text2.length)
12165
+ return Math.max(1, Math.ceil(cells / width));
12166
+ let rows = 1;
12167
+ let rowCells = 0;
12168
+ forEachTextCellSegment(text2, (piece) => {
12169
+ if (!piece.text || piece.cells <= 0)
12170
+ return;
12171
+ const pieceCells = piece.cells;
12172
+ if (rowCells > 0 && rowCells + pieceCells > width) {
12173
+ rows++;
12174
+ rowCells = 0;
12175
+ }
12176
+ if (pieceCells > width) {
12177
+ let remaining = pieceCells;
12178
+ while (remaining > 0) {
12179
+ if (rowCells >= width) {
12180
+ rows++;
12181
+ rowCells = 0;
12182
+ }
12183
+ const take = Math.min(width - rowCells, remaining);
12184
+ rowCells += take;
12185
+ remaining -= take;
12186
+ }
12187
+ return;
12188
+ }
12189
+ if (rowCells >= width) {
12190
+ rows++;
12191
+ rowCells = 0;
12192
+ }
12193
+ rowCells += pieceCells;
12194
+ });
12195
+ return rows;
12196
+ }
12197
+ function countWrappedPlainTextRows(text2, width) {
12198
+ width = Math.max(1, Math.floor(width));
12199
+ if (!text2)
12200
+ return 1;
12201
+ let rows = 0;
12202
+ let rowCells = 0;
12203
+ let sawText = false;
12204
+ forEachTextCellSegment(text2, (piece) => {
12205
+ if (!piece.text || piece.cells <= 0)
12206
+ return;
12207
+ sawText = true;
12208
+ const cells = piece.cells;
12209
+ if (rowCells > 0 && rowCells + cells > width) {
12210
+ rows++;
12211
+ rowCells = 0;
12212
+ }
12213
+ rowCells += cells;
12214
+ if (rowCells >= width) {
12215
+ rows++;
12216
+ rowCells = 0;
12217
+ }
12218
+ });
12219
+ return Math.max(1, rows + (rowCells > 0 || !sawText ? 1 : 0));
12220
+ }
12221
+ function countAsciiAnsiTextCells(text2) {
12222
+ let cells = 0;
12223
+ for (let i = 0; i < text2.length; i++) {
12224
+ const code2 = text2.charCodeAt(i);
12225
+ if (code2 > 127)
12226
+ return null;
12227
+ if (code2 === 27) {
12228
+ const next = text2[i + 1];
12229
+ if (next === "[") {
12230
+ let j = i + 2;
12231
+ while (j < text2.length) {
12232
+ const c = text2.charCodeAt(j);
12233
+ if (c >= 64 && c <= 126)
12234
+ break;
12235
+ j++;
12236
+ }
12237
+ if (j >= text2.length)
12238
+ return null;
12239
+ i = j;
12240
+ continue;
12241
+ }
12242
+ if (next === "]") {
12243
+ let j = i + 2;
12244
+ while (j < text2.length) {
12245
+ const c = text2.charCodeAt(j);
12246
+ if (c === 7)
12247
+ break;
12248
+ if (c === 27 && text2[j + 1] === "\\") {
12249
+ j++;
12250
+ break;
12251
+ }
12252
+ j++;
12253
+ }
12254
+ if (j >= text2.length)
12255
+ return null;
12256
+ i = j;
12257
+ continue;
12258
+ }
12259
+ return null;
12260
+ }
12261
+ if (code2 <= 31 || code2 === 127)
12262
+ return null;
12263
+ cells++;
12264
+ }
12265
+ return cells;
12266
+ }
11797
12267
  var TLogView = (0, import_vue22.defineComponent)({
11798
12268
  name: "TLogView",
11799
12269
  props: {
@@ -11932,6 +12402,7 @@ var TLogView = (0, import_vue22.defineComponent)({
11932
12402
  const renderLineCache = /* @__PURE__ */ new Map();
11933
12403
  const wrapLineCache = /* @__PURE__ */ new Map();
11934
12404
  const ansiLineCache = /* @__PURE__ */ new Map();
12405
+ const ansiTextCache = /* @__PURE__ */ new Map();
11935
12406
  const ansiWrapCache = /* @__PURE__ */ new Map();
11936
12407
  const ansiRowCache = /* @__PURE__ */ new Map();
11937
12408
  const linkifyLineCache = /* @__PURE__ */ new Map();
@@ -12053,6 +12524,9 @@ var TLogView = (0, import_vue22.defineComponent)({
12053
12524
  function ansiLineCacheKey(key, baseStyleKey, linkKey) {
12054
12525
  return JSON.stringify(["ansi-line", key, baseStyleKey, linksEnabled() ? 1 : 0, linkKey]);
12055
12526
  }
12527
+ function ansiTextCacheKey(key) {
12528
+ return JSON.stringify(["ansi-text", key]);
12529
+ }
12056
12530
  function linkifyLineCacheKey(key, baseStyleKey, linkKey, optionsKey) {
12057
12531
  return JSON.stringify(["linkify-line", key, baseStyleKey, linkKey, optionsKey]);
12058
12532
  }
@@ -12115,6 +12589,15 @@ var TLogView = (0, import_vue22.defineComponent)({
12115
12589
  ansiLineCache.delete(entry.key);
12116
12590
  }
12117
12591
  }
12592
+ function trimAnsiTextCache() {
12593
+ const max = DEFAULT_LOG_RENDER_CACHE_SIZE;
12594
+ if (ansiTextCache.size <= max)
12595
+ return;
12596
+ const entries = Array.from(ansiTextCache.values()).sort((a, b) => a.touchedAt - b.touchedAt);
12597
+ for (const entry of entries.slice(0, ansiTextCache.size - max)) {
12598
+ ansiTextCache.delete(entry.key);
12599
+ }
12600
+ }
12118
12601
  function trimAnsiWrapCache() {
12119
12602
  const max = DEFAULT_LOG_WRAP_CACHE_SIZE;
12120
12603
  if (ansiWrapCache.size <= max)
@@ -12170,6 +12653,7 @@ var TLogView = (0, import_vue22.defineComponent)({
12170
12653
  renderLineCache.clear();
12171
12654
  wrapLineCache.clear();
12172
12655
  ansiLineCache.clear();
12656
+ ansiTextCache.clear();
12173
12657
  ansiWrapCache.clear();
12174
12658
  ansiRowCache.clear();
12175
12659
  linkifyLineCache.clear();
@@ -12255,6 +12739,24 @@ var TLogView = (0, import_vue22.defineComponent)({
12255
12739
  });
12256
12740
  return segments;
12257
12741
  }
12742
+ function ansiPlainTextForLine(index, count) {
12743
+ if (index < 0 || index >= count)
12744
+ return "";
12745
+ const rawKey = lineKey(index);
12746
+ const key = ansiTextCacheKey(rawKey);
12747
+ const cached = ansiTextCache.get(key);
12748
+ if (cached) {
12749
+ cached.touchedAt = ++cacheClock;
12750
+ return cached.value;
12751
+ }
12752
+ const text2 = sanitizeAnsiInlineText(props.source.getLine(index));
12753
+ ansiTextCache.set(key, {
12754
+ key,
12755
+ value: text2,
12756
+ touchedAt: ++cacheClock
12757
+ });
12758
+ return text2;
12759
+ }
12258
12760
  function ansiWrappedRowsForLine(index, count, width, baseStyle, baseStyleKey, linkKey = linkStyleCacheKey()) {
12259
12761
  if (index < 0 || index >= count)
12260
12762
  return [[]];
@@ -12650,10 +13152,9 @@ var TLogView = (0, import_vue22.defineComponent)({
12650
13152
  lineMatches.push(matchIndex);
12651
13153
  invalidateSearchMarkers();
12652
13154
  }
12653
- function searchableTextForLine(index, count, baseStyle, baseStyleKey) {
12654
- if (props.ansi) {
12655
- return ansiSegmentsForLine(index, count, baseStyle, baseStyleKey).map((seg) => seg.text).join("");
12656
- }
13155
+ function searchableTextForLine(index, count) {
13156
+ if (props.ansi)
13157
+ return ansiPlainTextForLine(index, count);
12657
13158
  return sanitizeInlineText(props.source.getLine(index));
12658
13159
  }
12659
13160
  function normalizeSearchResultPreviewWidth(value) {
@@ -12668,8 +13169,8 @@ var TLogView = (0, import_vue22.defineComponent)({
12668
13169
  return DEFAULT_SEARCH_RESULTS_CONTEXT_CELLS;
12669
13170
  return Math.max(0, n);
12670
13171
  }
12671
- function previewForMatch(match2, count, baseStyle, baseStyleKey, options) {
12672
- const text2 = searchableTextForLine(match2.index, count, baseStyle, baseStyleKey);
13172
+ function previewForMatch(match2, count, options) {
13173
+ const text2 = searchableTextForLine(match2.index, count);
12673
13174
  const totalCells = textCellWidth(text2);
12674
13175
  const matchCells = Math.max(1, match2.endCell - match2.startCell);
12675
13176
  const previewWidth = Math.max(options.previewWidth, matchCells);
@@ -12704,7 +13205,7 @@ var TLogView = (0, import_vue22.defineComponent)({
12704
13205
  matchEndCell
12705
13206
  };
12706
13207
  }
12707
- function cachedLineSearchMatches(index, count, search, baseStyle, baseStyleKey) {
13208
+ function cachedLineSearchMatches(index, count, search) {
12708
13209
  const rawKey = lineKey(index);
12709
13210
  const key = searchLineCacheKey(rawKey, search.key);
12710
13211
  const cached = searchLineCache.get(key);
@@ -12712,9 +13213,7 @@ var TLogView = (0, import_vue22.defineComponent)({
12712
13213
  cached.touchedAt = ++cacheClock;
12713
13214
  return cached.matches;
12714
13215
  }
12715
- const matches = search.findLineMatches(
12716
- searchableTextForLine(index, count, baseStyle, baseStyleKey)
12717
- );
13216
+ const matches = search.findLineMatches(searchableTextForLine(index, count));
12718
13217
  searchLineCache.set(key, {
12719
13218
  key,
12720
13219
  matches,
@@ -12722,10 +13221,10 @@ var TLogView = (0, import_vue22.defineComponent)({
12722
13221
  });
12723
13222
  return matches;
12724
13223
  }
12725
- function scanSearchLine(index, count, search, baseStyle, baseStyleKey, maxMatches, absoluteBase) {
13224
+ function scanSearchLine(index, count, search, maxMatches, absoluteBase) {
12726
13225
  if (searchMatches.length >= maxMatches)
12727
13226
  return;
12728
- const lineMatches = cachedLineSearchMatches(index, count, search, baseStyle, baseStyleKey);
13227
+ const lineMatches = cachedLineSearchMatches(index, count, search);
12729
13228
  for (const match2 of lineMatches) {
12730
13229
  if (searchMatches.length >= maxMatches)
12731
13230
  return;
@@ -12755,19 +13254,9 @@ var TLogView = (0, import_vue22.defineComponent)({
12755
13254
  const count = lineCount();
12756
13255
  const maxMatches = maxSearchMatches();
12757
13256
  const absoluteBase = firstLineIndex();
12758
- const base = props.style ?? defaultStyle.value;
12759
- const baseStyleKey = styleCacheKey(base);
12760
13257
  let scanned = 0;
12761
13258
  while (searchCursor < count && searchMatches.length < maxMatches && (scanned === 0 || ctx.now() - started < budget)) {
12762
- scanSearchLine(
12763
- searchCursor,
12764
- count,
12765
- compiledSearch,
12766
- base,
12767
- baseStyleKey,
12768
- maxMatches,
12769
- absoluteBase
12770
- );
13259
+ scanSearchLine(searchCursor, count, compiledSearch, maxMatches, absoluteBase);
12771
13260
  searchCursor++;
12772
13261
  scanned++;
12773
13262
  }
@@ -12788,6 +13277,7 @@ var TLogView = (0, import_vue22.defineComponent)({
12788
13277
  emitSearchMarkers(true);
12789
13278
  markViewportDirty();
12790
13279
  ctx.invalidate({ priority: "low", plane: plane.value, reason: "data" });
13280
+ trimAnsiTextCache();
12791
13281
  trimSearchLineCache();
12792
13282
  }
12793
13283
  function requestSearchScan() {
@@ -13059,22 +13549,39 @@ var TLogView = (0, import_vue22.defineComponent)({
13059
13549
  if (count > visualIndexLineCount)
13060
13550
  appendEstimatedVisualLines(visualIndexLineCount, count);
13061
13551
  }
13062
- function measureVisualLine(index) {
13552
+ function measureVisualLine(index, options) {
13063
13553
  ensureVisualIndex();
13064
13554
  if (index < 0 || index >= visualIndexLineCount)
13065
13555
  return 0;
13066
13556
  const key = lineKey(index);
13067
13557
  if (visualKeys[index] === key)
13068
13558
  return 0;
13069
- const base = props.style ?? defaultStyle.value;
13070
- const rows = props.ansi ? ansiWrappedRowsForLine(
13071
- index,
13072
- visualIndexLineCount,
13073
- visualIndexWidth,
13074
- base,
13075
- styleCacheKey(base)
13076
- ) : wrappedRowsForLine(index, visualIndexLineCount, visualIndexWidth);
13077
- const nextCount = Math.max(1, rows.length);
13559
+ let nextCount;
13560
+ if (options?.countOnly === true) {
13561
+ if (props.ansi) {
13562
+ const raw = props.source.getLine(index);
13563
+ const asciiCells = countAsciiAnsiTextCells(raw);
13564
+ nextCount = asciiCells == null ? countWrappedTextRows(
13565
+ ansiPlainTextForLine(index, visualIndexLineCount),
13566
+ visualIndexWidth
13567
+ ) : Math.max(1, Math.ceil(asciiCells / visualIndexWidth));
13568
+ } else {
13569
+ nextCount = countWrappedPlainTextRows(
13570
+ sanitizeInlineText(props.source.getLine(index)),
13571
+ visualIndexWidth
13572
+ );
13573
+ }
13574
+ } else {
13575
+ const base = props.style ?? defaultStyle.value;
13576
+ const rows = props.ansi ? ansiWrappedRowsForLine(
13577
+ index,
13578
+ visualIndexLineCount,
13579
+ visualIndexWidth,
13580
+ base,
13581
+ styleCacheKey(base)
13582
+ ) : wrappedRowsForLine(index, visualIndexLineCount, visualIndexWidth);
13583
+ nextCount = Math.max(1, rows.length);
13584
+ }
13078
13585
  const prevCount = visualCounts[index] ?? 1;
13079
13586
  if (nextCount !== prevCount) {
13080
13587
  visualCounts[index] = nextCount;
@@ -13120,6 +13627,7 @@ var TLogView = (0, import_vue22.defineComponent)({
13120
13627
  if (visualMeasureCursor >= target) {
13121
13628
  syncVisualIndexStatus();
13122
13629
  emitVisualIndex();
13630
+ trimAnsiTextCache();
13123
13631
  return;
13124
13632
  }
13125
13633
  const started = ctx.now();
@@ -13131,7 +13639,7 @@ var TLogView = (0, import_vue22.defineComponent)({
13131
13639
  const effectiveTop = currentScrollTop() + topAdjustment;
13132
13640
  const start = visualStartForLine(index);
13133
13641
  const prevCount = visualCounts[index] ?? 1;
13134
- const delta = measureVisualLine(index);
13642
+ const delta = measureVisualLine(index, { countOnly: true });
13135
13643
  if (delta !== 0 && effectiveTop >= start + prevCount)
13136
13644
  topAdjustment += delta;
13137
13645
  visualMeasureCursor++;
@@ -13167,6 +13675,7 @@ var TLogView = (0, import_vue22.defineComponent)({
13167
13675
  }
13168
13676
  syncVisualIndexStatus();
13169
13677
  emitVisualIndex();
13678
+ trimAnsiTextCache();
13170
13679
  }
13171
13680
  function requestVisualIndexMeasurement(restartFrom = measuredLineCount) {
13172
13681
  if (!props.wrap) {
@@ -13180,6 +13689,7 @@ var TLogView = (0, import_vue22.defineComponent)({
13180
13689
  if (measuredLineCount >= target) {
13181
13690
  syncVisualIndexStatus();
13182
13691
  emitVisualIndex();
13692
+ trimAnsiTextCache();
13183
13693
  return;
13184
13694
  }
13185
13695
  const generation = ++visualMeasureGeneration;
@@ -13772,7 +14282,7 @@ var TLogView = (0, import_vue22.defineComponent)({
13772
14282
  emitScroll(clampedTop);
13773
14283
  return true;
13774
14284
  }
13775
- const scrollPlan = !props.wrap ? prepareUnsafeFullRowScroll({
14285
+ const scrollPlan = prepareUnsafeFullRowScroll({
13776
14286
  render,
13777
14287
  plane: plane.value,
13778
14288
  rect: r,
@@ -13783,7 +14293,7 @@ var TLogView = (0, import_vue22.defineComponent)({
13783
14293
  isClipped: isClipped(),
13784
14294
  hasPendingDirtyRows: Boolean(dirtyRowsHint?.length),
13785
14295
  strategy
13786
- }) : null;
14296
+ });
13787
14297
  if (scrollPlan) {
13788
14298
  const rows = options?.extraDirtyRows?.length ? [...scrollPlan.exposedRows, ...options.extraDirtyRows] : scrollPlan.exposedRows;
13789
14299
  if (markRowsDirty(rows)) {
@@ -14348,14 +14858,12 @@ var TLogView = (0, import_vue22.defineComponent)({
14348
14858
  }));
14349
14859
  }
14350
14860
  const count = lineCount();
14351
- const base = props.style ?? defaultStyle.value;
14352
- const baseStyleKey = styleCacheKey(base);
14353
14861
  const previewWidth = normalizeSearchResultPreviewWidth(options?.previewWidth);
14354
14862
  const contextCells = normalizeSearchResultContextCells(options?.contextCells);
14355
14863
  return searchMatches.slice(offset, offset + limit).map((match2, index) => ({
14356
14864
  matchIndex: offset + index,
14357
14865
  match: copySearchMatch(match2),
14358
- preview: previewForMatch(match2, count, base, baseStyleKey, {
14866
+ preview: previewForMatch(match2, count, {
14359
14867
  previewWidth,
14360
14868
  contextCells
14361
14869
  })