@simon_he/vue-tui 1.1.14 → 1.1.15

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
@@ -9826,7 +9826,7 @@ var EMPTY_LAYOUT = Object.freeze({
9826
9826
  regions: Object.freeze([]),
9827
9827
  fixedRows: !1,
9828
9828
  rowCount: 0
9829
- }), MAX_KEYED_ROW_LAYOUT_CACHE_ENTRIES = 2048;
9829
+ }), MAX_KEYED_ROW_LAYOUT_CACHE_ENTRIES = 2048, RETAINED_ROW_LAYOUT_MARGIN_ROWS = 512;
9830
9830
  function clamp9(n, min, max) {
9831
9831
  return Math.max(min, Math.min(max, n));
9832
9832
  }
@@ -9930,7 +9930,7 @@ var TTranscriptView = (0, import_vue23.defineComponent)({
9930
9930
  "hoverRegion"
9931
9931
  ],
9932
9932
  setup(props, { emit, expose }) {
9933
- let { defaultStyle, observability } = useTerminal(), instance = (0, import_vue23.getCurrentInstance)(), rowsRef = (0, import_vue23.ref)(null), innerScrollTop = (0, import_vue23.ref)(normalizeInt2(props.defaultScrollTop)), hoveredRegion = (0, import_vue23.ref)(null), focusedRegion = (0, import_vue23.ref)(null), rowLayoutCache = /* @__PURE__ */ new Map(), keyedRowLayoutCache = /* @__PURE__ */ new Map(), previousWrappedLayout = null, previousWrappedDeps = null, canUseWrappedChangedRangeRepaint = !1, warnedLargeFlattenedRows = !1;
9933
+ let { defaultStyle, observability } = useTerminal(), instance = (0, import_vue23.getCurrentInstance)(), rowsRef = (0, import_vue23.ref)(null), innerScrollTop = (0, import_vue23.ref)(normalizeInt2(props.defaultScrollTop)), hoveredRegion = (0, import_vue23.ref)(null), focusedRegion = (0, import_vue23.ref)(null), rowLayoutCache = /* @__PURE__ */ new Map(), keyedRowLayoutCache = /* @__PURE__ */ new Map(), retainedWindow = null, previousWrappedLayout = null, previousWrappedDeps = null, canUseWrappedChangedRangeRepaint = !1, warnedLargeFlattenedRows = !1;
9934
9934
  function isScrollControlled() {
9935
9935
  return Object.prototype.hasOwnProperty.call(instance?.vnode.props ?? {}, "scrollTop");
9936
9936
  }
@@ -9986,6 +9986,18 @@ var TTranscriptView = (0, import_vue23.defineComponent)({
9986
9986
  for (let rowIndex of rowLayoutCache.keys())
9987
9987
  rowIndex >= count && rowLayoutCache.delete(rowIndex);
9988
9988
  }
9989
+ function pruneRowLayoutCacheOutsideWindow(count, top, bottom) {
9990
+ let start = Math.max(0, top - RETAINED_ROW_LAYOUT_MARGIN_ROWS), end = Math.min(count, bottom + RETAINED_ROW_LAYOUT_MARGIN_ROWS);
9991
+ if (!(retainedWindow && retainedWindow.start === start && retainedWindow.end === end)) {
9992
+ retainedWindow = { start, end };
9993
+ for (let [rowIndex, entry] of rowLayoutCache) {
9994
+ if (rowIndex >= start && rowIndex < end)
9995
+ continue;
9996
+ let identity = rowLayoutIdentityKey(entry.rowKey, entry.rowVersion);
9997
+ keyedRowLayoutCache.get(identity) === entry && keyedRowLayoutCache.delete(identity), rowLayoutCache.delete(rowIndex);
9998
+ }
9999
+ }
10000
+ }
9989
10001
  function sameWrappedDeps(a, b) {
9990
10002
  return !!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;
9991
10003
  }
@@ -10042,7 +10054,7 @@ var TTranscriptView = (0, import_vue23.defineComponent)({
10042
10054
  props.version, canUseWrappedChangedRangeRepaint = !1;
10043
10055
  let perfEnabled = observability.framePerf.enabled.value, perfStartedAt = perfEnabled ? framePerfNow() : 0, width = Math.max(1, Math.floor(props.w)), count = Math.max(0, normalizeInt2(props.source.rowCount()));
10044
10056
  if (!count)
10045
- return rowLayoutCache.clear(), keyedRowLayoutCache.clear(), previousWrappedLayout = null, previousWrappedDeps = null, perfEnabled && observability.framePerf.recordComponent({
10057
+ return rowLayoutCache.clear(), keyedRowLayoutCache.clear(), retainedWindow = null, previousWrappedLayout = null, previousWrappedDeps = null, perfEnabled && observability.framePerf.recordComponent({
10046
10058
  name: "TTranscriptView",
10047
10059
  id: instance?.uid == null ? void 0 : String(instance.uid),
10048
10060
  phase: "layout",
@@ -10079,6 +10091,7 @@ var TTranscriptView = (0, import_vue23.defineComponent)({
10079
10091
  let row = result.rows[0];
10080
10092
  visualRows[rowIndex] = row, regions.push(...row.hitRegions);
10081
10093
  }
10094
+ pruneRowLayoutCacheOutsideWindow(count, top, bottom);
10082
10095
  } else {
10083
10096
  let incremental = tryUpdateWrappedLayoutFromChangedRange(
10084
10097
  count,
package/dist/agent.js CHANGED
@@ -14,7 +14,7 @@ import { TMermaid$1 as TMermaid, TMermaidText$1 as TMermaidText, isSimpleMermaid
14
14
  import { createIterm2InlineImageSequence, createKittyDeleteGraphicsSequence, createKittyGraphicsSequence, createKittyPlacementSequence, createTerminalGraphicPngSequence, detectTerminalGraphicsCapabilities, getTerminalGraphicsOutput, getTerminalGraphicsOutputVersion, hashTerminalGraphicsString, isSafeTerminalGraphicsSequence, normalizeTerminalGraphicSize, sanitizeTerminalFallbackText, subscribeTerminalGraphicsOutput } from "./terminal-graphics-DpSL29OQ.js";
15
15
  import { TVirtualMarkdown, createMarkdownBlockSource } from "./block-source-BGTRldY7.js";
16
16
  import "./math-image-CcUpBnW0.js";
17
- import { TLogLinksPanel, TLogMinimap, TLogScrollbar, TLogSearchBar, TLogSearchPager, TLogSearchResults, TLogView, TLogVirtualLinksPanel, TLogVirtualSearchResults, TTranscriptView, TVirtualList, createAppendOnlyLogStore, createTLogDensityBucketsFromMarkers, createTLogLevelPlugin, createTLogLineMatcherPlugin, createTLogLinkActionPlugin, createTLogOsc8LinkPlugin, createTLogUrlPlugin, detectTLogUrls, dispatchTLogPluginLinkAction, getTLogPluginMetadata, parseTLogAnnotatedText, resolveTLogLinksPanelTheme, resolveTLogMinimapTheme, resolveTLogScrollbarTheme, resolveTLogSearchBarTheme, resolveTLogSearchPagerTheme, resolveTLogSearchResultsTheme, resolveTLogViewTheme, stripTLogAnsiText, tlogDarkPreset, tlogDarkTheme, tlogDefaultPreset, tlogDefaultTheme, tlogHighContrastPreset, tlogHighContrastTheme, toTLogExternalLinkFromVisibleLink, useTLogLinkController, useTLogRetainedIndex, useTLogSearchController, useTLogSearchResultsPage, useTLogVirtualSearchResults } from "./tlog-theme-DKq1YOQF.js";
17
+ import { TLogLinksPanel, TLogMinimap, TLogScrollbar, TLogSearchBar, TLogSearchPager, TLogSearchResults, TLogView, TLogVirtualLinksPanel, TLogVirtualSearchResults, TTranscriptView, TVirtualList, createAppendOnlyLogStore, createTLogDensityBucketsFromMarkers, createTLogLevelPlugin, createTLogLineMatcherPlugin, createTLogLinkActionPlugin, createTLogOsc8LinkPlugin, createTLogUrlPlugin, detectTLogUrls, dispatchTLogPluginLinkAction, getTLogPluginMetadata, parseTLogAnnotatedText, resolveTLogLinksPanelTheme, resolveTLogMinimapTheme, resolveTLogScrollbarTheme, resolveTLogSearchBarTheme, resolveTLogSearchPagerTheme, resolveTLogSearchResultsTheme, resolveTLogViewTheme, stripTLogAnsiText, tlogDarkPreset, tlogDarkTheme, tlogDefaultPreset, tlogDefaultTheme, tlogHighContrastPreset, tlogHighContrastTheme, toTLogExternalLinkFromVisibleLink, useTLogLinkController, useTLogRetainedIndex, useTLogSearchController, useTLogSearchResultsPage, useTLogVirtualSearchResults } from "./tlog-theme-zAInqC2W.js";
18
18
  import { TAgentTerminalGraphic } from "./TAgentTerminalGraphic-BKDAoGoE.js";
19
19
  import { computed, defineComponent, h } from "vue";
20
20
 
@@ -6540,7 +6540,7 @@ var EMPTY_LAYOUT = Object.freeze({
6540
6540
  regions: Object.freeze([]),
6541
6541
  fixedRows: !1,
6542
6542
  rowCount: 0
6543
- }), MAX_KEYED_ROW_LAYOUT_CACHE_ENTRIES = 2048;
6543
+ }), MAX_KEYED_ROW_LAYOUT_CACHE_ENTRIES = 2048, RETAINED_ROW_LAYOUT_MARGIN_ROWS = 512;
6544
6544
  function clamp4(n, min, max) {
6545
6545
  return Math.max(min, Math.min(max, n));
6546
6546
  }
@@ -6644,7 +6644,7 @@ var TTranscriptView = (0, import_vue17.defineComponent)({
6644
6644
  "hoverRegion"
6645
6645
  ],
6646
6646
  setup(props, { emit, expose }) {
6647
- let { defaultStyle, observability } = useTerminal(), instance = (0, import_vue17.getCurrentInstance)(), rowsRef = (0, import_vue17.ref)(null), innerScrollTop = (0, import_vue17.ref)(normalizeInt2(props.defaultScrollTop)), hoveredRegion = (0, import_vue17.ref)(null), focusedRegion = (0, import_vue17.ref)(null), rowLayoutCache = /* @__PURE__ */ new Map(), keyedRowLayoutCache = /* @__PURE__ */ new Map(), previousWrappedLayout = null, previousWrappedDeps = null, canUseWrappedChangedRangeRepaint = !1, warnedLargeFlattenedRows = !1;
6647
+ let { defaultStyle, observability } = useTerminal(), instance = (0, import_vue17.getCurrentInstance)(), rowsRef = (0, import_vue17.ref)(null), innerScrollTop = (0, import_vue17.ref)(normalizeInt2(props.defaultScrollTop)), hoveredRegion = (0, import_vue17.ref)(null), focusedRegion = (0, import_vue17.ref)(null), rowLayoutCache = /* @__PURE__ */ new Map(), keyedRowLayoutCache = /* @__PURE__ */ new Map(), retainedWindow = null, previousWrappedLayout = null, previousWrappedDeps = null, canUseWrappedChangedRangeRepaint = !1, warnedLargeFlattenedRows = !1;
6648
6648
  function isScrollControlled() {
6649
6649
  return Object.prototype.hasOwnProperty.call(instance?.vnode.props ?? {}, "scrollTop");
6650
6650
  }
@@ -6700,6 +6700,18 @@ var TTranscriptView = (0, import_vue17.defineComponent)({
6700
6700
  for (let rowIndex of rowLayoutCache.keys())
6701
6701
  rowIndex >= count && rowLayoutCache.delete(rowIndex);
6702
6702
  }
6703
+ function pruneRowLayoutCacheOutsideWindow(count, top, bottom) {
6704
+ let start = Math.max(0, top - RETAINED_ROW_LAYOUT_MARGIN_ROWS), end = Math.min(count, bottom + RETAINED_ROW_LAYOUT_MARGIN_ROWS);
6705
+ if (!(retainedWindow && retainedWindow.start === start && retainedWindow.end === end)) {
6706
+ retainedWindow = { start, end };
6707
+ for (let [rowIndex, entry] of rowLayoutCache) {
6708
+ if (rowIndex >= start && rowIndex < end)
6709
+ continue;
6710
+ let identity = rowLayoutIdentityKey(entry.rowKey, entry.rowVersion);
6711
+ keyedRowLayoutCache.get(identity) === entry && keyedRowLayoutCache.delete(identity), rowLayoutCache.delete(rowIndex);
6712
+ }
6713
+ }
6714
+ }
6703
6715
  function sameWrappedDeps(a, b) {
6704
6716
  return !!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;
6705
6717
  }
@@ -6756,7 +6768,7 @@ var TTranscriptView = (0, import_vue17.defineComponent)({
6756
6768
  props.version, canUseWrappedChangedRangeRepaint = !1;
6757
6769
  let perfEnabled = observability.framePerf.enabled.value, perfStartedAt = perfEnabled ? framePerfNow() : 0, width = Math.max(1, Math.floor(props.w)), count = Math.max(0, normalizeInt2(props.source.rowCount()));
6758
6770
  if (!count)
6759
- return rowLayoutCache.clear(), keyedRowLayoutCache.clear(), previousWrappedLayout = null, previousWrappedDeps = null, perfEnabled && observability.framePerf.recordComponent({
6771
+ return rowLayoutCache.clear(), keyedRowLayoutCache.clear(), retainedWindow = null, previousWrappedLayout = null, previousWrappedDeps = null, perfEnabled && observability.framePerf.recordComponent({
6760
6772
  name: "TTranscriptView",
6761
6773
  id: instance?.uid == null ? void 0 : String(instance.uid),
6762
6774
  phase: "layout",
@@ -6793,6 +6805,7 @@ var TTranscriptView = (0, import_vue17.defineComponent)({
6793
6805
  let row = result.rows[0];
6794
6806
  visualRows[rowIndex] = row, regions.push(...row.hitRegions);
6795
6807
  }
6808
+ pruneRowLayoutCacheOutsideWindow(count, top, bottom);
6796
6809
  } else {
6797
6810
  let incremental = tryUpdateWrappedLayoutFromChangedRange(
6798
6811
  count,
@@ -7,7 +7,7 @@ import "./terminal-selection-4W1SJj4c.js";
7
7
  import { TView } from "./frame-mailbox-6XHw-P8Q.js";
8
8
  import "./wheel-scroll-CQ22Hcyj.js";
9
9
  import "./terminal-graphics-DpSL29OQ.js";
10
- import { TLogLinksPanel, TLogMinimap, TLogScrollbar, TLogSearchBar, TLogSearchPager, TLogSearchResults, TLogView, TLogVirtualLinksPanel, TLogVirtualSearchResults, TTranscriptView, TVirtualList, createAppendOnlyLogStore, createTLogDensityBucketsFromMarkers, createTLogLevelPlugin, createTLogLineMatcherPlugin, createTLogLinkActionPlugin, createTLogOsc8LinkPlugin, createTLogUrlPlugin, detectTLogUrls, dispatchTLogPluginLinkAction, getTLogPluginMetadata, handleTLogKeymapEvent, matchesTLogKeyBinding, parseTLogAnnotatedText, resolveTLogLinksPanelTheme, resolveTLogMinimapTheme, resolveTLogScrollbarTheme, resolveTLogSearchBarTheme, resolveTLogSearchPagerTheme, resolveTLogSearchResultsTheme, resolveTLogViewTheme, stripTLogAnsiText, tlogDarkPreset, tlogDarkTheme, tlogDefaultKeymap, tlogDefaultPreset, tlogDefaultTheme, tlogHighContrastKeymap, tlogHighContrastPreset, tlogHighContrastTheme, toTLogExternalLinkFromVisibleLink, useTLogLinkController, useTLogRetainedIndex, useTLogSearchController, useTLogSearchResultsPage, useTLogVirtualSearchResults } from "./tlog-theme-DKq1YOQF.js";
10
+ import { TLogLinksPanel, TLogMinimap, TLogScrollbar, TLogSearchBar, TLogSearchPager, TLogSearchResults, TLogView, TLogVirtualLinksPanel, TLogVirtualSearchResults, TTranscriptView, TVirtualList, createAppendOnlyLogStore, createTLogDensityBucketsFromMarkers, createTLogLevelPlugin, createTLogLineMatcherPlugin, createTLogLinkActionPlugin, createTLogOsc8LinkPlugin, createTLogUrlPlugin, detectTLogUrls, dispatchTLogPluginLinkAction, getTLogPluginMetadata, handleTLogKeymapEvent, matchesTLogKeyBinding, parseTLogAnnotatedText, resolveTLogLinksPanelTheme, resolveTLogMinimapTheme, resolveTLogScrollbarTheme, resolveTLogSearchBarTheme, resolveTLogSearchPagerTheme, resolveTLogSearchResultsTheme, resolveTLogViewTheme, stripTLogAnsiText, tlogDarkPreset, tlogDarkTheme, tlogDefaultKeymap, tlogDefaultPreset, tlogDefaultTheme, tlogHighContrastKeymap, tlogHighContrastPreset, tlogHighContrastTheme, toTLogExternalLinkFromVisibleLink, useTLogLinkController, useTLogRetainedIndex, useTLogSearchController, useTLogSearchResultsPage, useTLogVirtualSearchResults } from "./tlog-theme-zAInqC2W.js";
11
11
  import "./TAgentTerminalGraphic-BKDAoGoE.js";
12
12
  import { TVideo } from "./TVideo-C3gnTlFD.js";
13
13
  import { computed, defineComponent, h, inject, onBeforeUnmount, onUnmounted, ref, watch } from "vue";
@@ -1787,6 +1787,20 @@ const EMPTY_LAYOUT = Object.freeze({
1787
1787
  rowCount: 0
1788
1788
  });
1789
1789
  const MAX_KEYED_ROW_LAYOUT_CACHE_ENTRIES = 2048;
1790
+ /**
1791
+ * How far outside the drawn window a fixed-row transcript keeps a row's layout.
1792
+ *
1793
+ * The fixed-row path lays a frame's rows out on demand, so its cache is the one structure that
1794
+ * grows with a transcript nobody re-reads: a reply appended while the view follows the bottom draws
1795
+ * every one of its new rows exactly once, and without a retention margin every one of them stayed
1796
+ * for the life of the component (measured in an agent TUI: ~2 MB of retained layout per turn of
1797
+ * 152 KB of appended code). The margin is what makes releasing them safe to do: scrolling back
1798
+ * within it still finds its layouts, and a row that returns after it is laid out again on demand —
1799
+ * the same work its first draw did — while the retained row space stays bounded at the drawn window
1800
+ * plus this margin on each side. The wrapped path is untouched: it keeps its layouts for the whole
1801
+ * row space in `previousWrappedLayout`, which its incremental repaint reads.
1802
+ */
1803
+ const RETAINED_ROW_LAYOUT_MARGIN_ROWS = 512;
1790
1804
  function clamp$9(n, min, max) {
1791
1805
  return Math.max(min, Math.min(max, n));
1792
1806
  }
@@ -1957,6 +1971,8 @@ const TTranscriptView = defineComponent({
1957
1971
  const focusedRegion = ref(null);
1958
1972
  const rowLayoutCache = new Map();
1959
1973
  const keyedRowLayoutCache = new Map();
1974
+ /** The retained row window the last release ran for (see `pruneRowLayoutCacheOutsideWindow`). */
1975
+ let retainedWindow = null;
1960
1976
  let previousWrappedLayout = null;
1961
1977
  let previousWrappedDeps = null;
1962
1978
  let canUseWrappedChangedRangeRepaint = false;
@@ -2053,6 +2069,34 @@ const TTranscriptView = defineComponent({
2053
2069
  function pruneRowLayoutCache(count) {
2054
2070
  for (const rowIndex of rowLayoutCache.keys()) if (rowIndex >= count) rowLayoutCache.delete(rowIndex);
2055
2071
  }
2072
+ /**
2073
+ * Drops the layouts of rows that scrolled out of the drawn window's retention margin.
2074
+ *
2075
+ * The alternative — letting the cache keep every row it ever laid out — makes a followed
2076
+ * transcript's retention follow its row space, without bound, so the release has to happen here,
2077
+ * where rows are laid out per frame on demand. A released row is laid out again when it is drawn
2078
+ * again, and its identity entry goes with it: that map holds the same entry object, so leaving it
2079
+ * behind would keep the row retained through the keyed cache instead of releasing it.
2080
+ *
2081
+ * The scan runs when the retained window changes, which is exactly when rows can fall out of it
2082
+ * (a jump, a scroll, a grown or shrunk row space); a repaint of the same window finds nothing to
2083
+ * release, so it does not pay for the walk.
2084
+ */
2085
+ function pruneRowLayoutCacheOutsideWindow(count, top, bottom) {
2086
+ const start = Math.max(0, top - RETAINED_ROW_LAYOUT_MARGIN_ROWS);
2087
+ const end = Math.min(count, bottom + RETAINED_ROW_LAYOUT_MARGIN_ROWS);
2088
+ if (retainedWindow && retainedWindow.start === start && retainedWindow.end === end) return;
2089
+ retainedWindow = {
2090
+ start,
2091
+ end
2092
+ };
2093
+ for (const [rowIndex, entry] of rowLayoutCache) {
2094
+ if (rowIndex >= start && rowIndex < end) continue;
2095
+ const identity = rowLayoutIdentityKey(entry.rowKey, entry.rowVersion);
2096
+ if (keyedRowLayoutCache.get(identity) === entry) keyedRowLayoutCache.delete(identity);
2097
+ rowLayoutCache.delete(rowIndex);
2098
+ }
2099
+ }
2056
2100
  function sameWrappedDeps(a, b) {
2057
2101
  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;
2058
2102
  }
@@ -2135,6 +2179,7 @@ const TTranscriptView = defineComponent({
2135
2179
  if (!count) {
2136
2180
  rowLayoutCache.clear();
2137
2181
  keyedRowLayoutCache.clear();
2182
+ retainedWindow = null;
2138
2183
  previousWrappedLayout = null;
2139
2184
  previousWrappedDeps = null;
2140
2185
  if (perfEnabled) observability.framePerf.recordComponent({
@@ -2191,6 +2236,7 @@ const TTranscriptView = defineComponent({
2191
2236
  visualRows[rowIndex] = row;
2192
2237
  regions.push(...row.hitRegions);
2193
2238
  }
2239
+ pruneRowLayoutCacheOutsideWindow(count, top, bottom);
2194
2240
  } else {
2195
2241
  const incremental = tryUpdateWrappedLayoutFromChangedRange(count, sourceChangedRange, wrappedDeps);
2196
2242
  if (incremental) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simon_he/vue-tui",
3
- "version": "1.1.14",
3
+ "version": "1.1.15",
4
4
  "description": "Vue 3 terminal UI components and renderers for browser DOM, CLI stdout, logs, markdown transcripts, and agent consoles",
5
5
  "keywords": [
6
6
  "agent-console",