@simon_he/vue-tui 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +1 -0
  2. package/dist/TAgentTerminalGraphic-B8-YZfuP.js +1198 -0
  3. package/dist/{TSelect-CMBOOFwK.js → TSelect-CCsjjCOr.js} +41 -12
  4. package/dist/{TTree-kk8ntrpg.js → TTree-3W5A2_EH.js} +4 -3
  5. package/dist/TVideo-D9AOiyq2.js +948 -0
  6. package/dist/agent.cjs +5204 -4312
  7. package/dist/agent.js +11 -9
  8. package/dist/{block-source-DhzajB9H.js → block-source-BhvdZomx.js} +246 -53
  9. package/dist/cli.cjs +41 -26
  10. package/dist/cli.js +68 -18
  11. package/dist/core.js +2 -2
  12. package/dist/experimental/browser.cjs +3705 -0
  13. package/dist/experimental/browser.d.cts +3 -0
  14. package/dist/experimental/browser.d.ts +3 -0
  15. package/dist/experimental/browser.js +413 -0
  16. package/dist/experimental.cjs +2 -1
  17. package/dist/experimental.js +9 -947
  18. package/dist/{linkify-CL9wwnZN.js → frame-mailbox-DaTrfyaF.js} +2 -297
  19. package/dist/index.cjs +25 -8
  20. package/dist/index.js +6 -5
  21. package/dist/linkify-CklfA1vp.js +298 -0
  22. package/dist/markdown.cjs +5196 -4322
  23. package/dist/markdown.js +4 -4
  24. package/dist/{mermaid-N3kGTHFy.js → mermaid-D0ov4_A1.js} +93 -9
  25. package/dist/mermaid.cjs +78 -7
  26. package/dist/mermaid.js +4 -4
  27. package/dist/{path-suggest-Dndu4dTi.js → path-suggest-fPzRsgPv.js} +1 -1
  28. package/dist/renderer/terminal-graphics.d.ts +1 -1
  29. package/dist/{terminal-graphics-2FTRg9x3.js → terminal-graphics-DpSL29OQ.js} +3 -3
  30. package/dist/{tlog-theme-B91Wv7Zj.js → tlog-theme-BnILsD3n.js} +10 -1203
  31. package/dist/vue/browser/types.d.ts +52 -0
  32. package/dist/vue/components/TAgentTerminalGraphic.d.ts +3 -0
  33. package/dist/vue/components/TBrowser.d.ts +231 -0
  34. package/dist/vue/components/TVirtualMarkdown.d.ts +10 -0
  35. package/dist/vue/markdown/render.d.ts +18 -1
  36. package/dist/vue.cjs +25 -8
  37. package/dist/vue.js +8 -7
  38. package/package.json +27 -2
  39. /package/dist/{TMermaidText-wLnMMvVS.js → TMermaidText-C1akgJPU.js} +0 -0
  40. /package/dist/{math-image-BZhmfUeu.js → math-image-CcUpBnW0.js} +0 -0
  41. /package/dist/{path-mnSgEpCm.js → path-BI9W15Zt.js} +0 -0
  42. /package/dist/{wheel-scroll-DASkEZr-.js → wheel-scroll-CQ22Hcyj.js} +0 -0
package/dist/agent.js CHANGED
@@ -1,19 +1,21 @@
1
1
  import "./create-terminal-Db_-UGVQ.js";
2
2
  import "./hyperlink-clbb_ha_.js";
3
3
  import { forEachTextCellSegment, recordTerminalGraphicTrace, sanitizeInlineText, sanitizeTextBlock, sliceByCells, spaces, textCellWidth$1 as textCellWidth, useTerminal, withTextWidthProvider } from "./rect-JzWMJxxc.js";
4
- import { TText, TView } from "./linkify-CL9wwnZN.js";
4
+ import "./linkify-CklfA1vp.js";
5
5
  import "./frame-perf-CHpWhbC4.js";
6
6
  import "./env-Cl8qmNFN.js";
7
7
  import "./terminal-selection-4W1SJj4c.js";
8
8
  import "./debug-logger-CmsGI4of.js";
9
- import { TBox, TCommandPalette, TDialog, TInput, TRenderPlane, TSelect, computeCommandPaletteMatchRanges } from "./TSelect-CMBOOFwK.js";
10
- import "./path-mnSgEpCm.js";
11
- import "./wheel-scroll-DASkEZr-.js";
12
- import { TMermaid$1 as TMermaid, TMermaidText$1 as TMermaidText, isSimpleMermaidFlowchartSource, markMermaidRenderErrorFatal } from "./TMermaidText-wLnMMvVS.js";
13
- import { createIterm2InlineImageSequence, createKittyDeleteGraphicsSequence, createKittyGraphicsSequence, createKittyPlacementSequence, createTerminalGraphicPngSequence, detectTerminalGraphicsCapabilities, getTerminalGraphicsOutput, getTerminalGraphicsOutputVersion, hashTerminalGraphicsString, isSafeTerminalGraphicsSequence, normalizeTerminalGraphicSize, sanitizeTerminalFallbackText, subscribeTerminalGraphicsOutput } from "./terminal-graphics-2FTRg9x3.js";
14
- import { TVirtualMarkdown, createMarkdownBlockSource } from "./block-source-DhzajB9H.js";
15
- import "./math-image-BZhmfUeu.js";
16
- import { TAgentTerminalGraphic, 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-B91Wv7Zj.js";
9
+ import { TBox, TCommandPalette, TDialog, TInput, TRenderPlane, TSelect, computeCommandPaletteMatchRanges } from "./TSelect-CCsjjCOr.js";
10
+ import { TText, TView } from "./frame-mailbox-DaTrfyaF.js";
11
+ import "./path-BI9W15Zt.js";
12
+ import "./wheel-scroll-CQ22Hcyj.js";
13
+ import { TMermaid$1 as TMermaid, TMermaidText$1 as TMermaidText, isSimpleMermaidFlowchartSource, markMermaidRenderErrorFatal } from "./TMermaidText-C1akgJPU.js";
14
+ import { createIterm2InlineImageSequence, createKittyDeleteGraphicsSequence, createKittyGraphicsSequence, createKittyPlacementSequence, createTerminalGraphicPngSequence, detectTerminalGraphicsCapabilities, getTerminalGraphicsOutput, getTerminalGraphicsOutputVersion, hashTerminalGraphicsString, isSafeTerminalGraphicsSequence, normalizeTerminalGraphicSize, sanitizeTerminalFallbackText, subscribeTerminalGraphicsOutput } from "./terminal-graphics-DpSL29OQ.js";
15
+ import { TVirtualMarkdown, createMarkdownBlockSource } from "./block-source-BhvdZomx.js";
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-BnILsD3n.js";
18
+ import { TAgentTerminalGraphic } from "./TAgentTerminalGraphic-B8-YZfuP.js";
17
19
  import { computed, defineComponent, h } from "vue";
18
20
 
19
21
  //#region src/vue/agent/view-models.ts
@@ -1,10 +1,10 @@
1
1
  import { hasEncodedControl, sanitizeDomHref } from "./hyperlink-clbb_ha_.js";
2
- import { EventZIndexContextKey, currentTextWidthProvider, forEachTextCellSegment, intersectRect, normalizeCellRect, repeatChar, sanitizeInlineText, sanitizeTextBlock, sliceByCellsRange, spaces, textCellWidth$1 as textCellWidth, translateRect, useLayout, useRenderNode, useTerminal, useTerminalNode, useVisibility, withTextWidthProvider } from "./rect-JzWMJxxc.js";
2
+ import { EventZIndexContextKey, RenderPlaneContextKey, currentTextWidthProvider, forEachTextCellSegment, intersectRect, normalizeCellRect, repeatChar, sanitizeInlineText, sanitizeTextBlock, sliceByCellsRange, spaces, textCellWidth$1 as textCellWidth, translateRect, useLayout, useRenderNode, useTerminal, useTerminalNode, useVisibility, withTextWidthProvider } from "./rect-JzWMJxxc.js";
3
3
  import { terminalSelectionRowSpans, terminalSelectionVisibleRowSpans } from "./terminal-selection-4W1SJj4c.js";
4
4
  import { createDebugLogger, isDebugEnabled } from "./debug-logger-CmsGI4of.js";
5
- import { applyWheelScroll, createWheelScrollState, resetWheelScrollState } from "./wheel-scroll-DASkEZr-.js";
6
- import { createIterm2InlineImageSequence, createKittyDeleteGraphicsSequence, createKittyGraphicsSequence, createKittyPlacementSequence, getTerminalGraphicsOutput, getTerminalGraphicsOutputVersion, isTerminalGraphicsProtocol, stableTerminalGraphicNumericId, subscribeTerminalGraphicsOutput } from "./terminal-graphics-2FTRg9x3.js";
7
- import { enqueueMarkdownMathImages, getCachedMarkdownMathImage, isMarkdownMathImageRendererReady, loadMarkdownMathImageRenderer, resolveMarkdownMathColor, subscribeMarkdownMathImage } from "./math-image-BZhmfUeu.js";
5
+ import { applyWheelScroll, createWheelScrollState, resetWheelScrollState } from "./wheel-scroll-CQ22Hcyj.js";
6
+ import { createIterm2InlineImageSequence, createKittyDeleteGraphicsSequence, createKittyGraphicsSequence, createKittyPlacementSequence, getTerminalGraphicsOutput, getTerminalGraphicsOutputVersion, isTerminalGraphicsProtocol, stableTerminalGraphicNumericId, subscribeTerminalGraphicsOutput } from "./terminal-graphics-DpSL29OQ.js";
7
+ import { enqueueMarkdownMathImages, getCachedMarkdownMathImage, isMarkdownMathImageRendererReady, loadMarkdownMathImageRenderer, resolveMarkdownMathColor, subscribeMarkdownMathImage } from "./math-image-CcUpBnW0.js";
8
8
  import { computed, defineComponent, getCurrentInstance, h, inject, markRaw, onBeforeUnmount, ref, shallowRef, watch, watchEffect } from "vue";
9
9
  import { getMarkdown, parseMarkdownToStructure } from "stream-markdown-parser";
10
10
 
@@ -1696,8 +1696,70 @@ function mergeStyle(base, overlay) {
1696
1696
  return merged;
1697
1697
  }
1698
1698
  const activeMarkdownImageGraphics = new WeakMap();
1699
+ const markdownImageGraphicIdCache = new WeakMap();
1700
+ const preparedMarkdownImageGraphicCache = new WeakMap();
1699
1701
  function markdownImageGraphicId(segment, identity) {
1700
- return `md-image:${stableTerminalGraphicNumericId(`${identity}:${segment.src ?? segment.tex ?? ""}:${segment.displayWidth ?? 0}:${segment.displayHeight ?? 0}:${segment.base64 ?? ""}`)}`;
1702
+ let cachedIds = markdownImageGraphicIdCache.get(segment);
1703
+ if (!cachedIds) {
1704
+ cachedIds = new Map();
1705
+ markdownImageGraphicIdCache.set(segment, cachedIds);
1706
+ }
1707
+ const cached = cachedIds.get(identity);
1708
+ if (cached) return cached;
1709
+ const id = `md-image:${stableTerminalGraphicNumericId(`${identity}:${segment.src ?? segment.tex ?? ""}:${segment.displayWidth ?? 0}:${segment.displayHeight ?? 0}:${segment.base64 ?? ""}`)}`;
1710
+ cachedIds.set(identity, id);
1711
+ return id;
1712
+ }
1713
+ function prepareMarkdownImageGraphic(segment, identity, protocol, placement) {
1714
+ if (!segment.base64) return null;
1715
+ let preparedByVariant = preparedMarkdownImageGraphicCache.get(segment);
1716
+ if (!preparedByVariant) {
1717
+ preparedByVariant = new Map();
1718
+ preparedMarkdownImageGraphicCache.set(segment, preparedByVariant);
1719
+ }
1720
+ const cacheKey = `${identity}\u0000${protocol}\u0000${placement.width}\u0000${placement.height}\u0000${placement.sourceX ?? ""}\u0000${placement.sourceY ?? ""}\u0000${placement.sourceWidth ?? ""}\u0000${placement.sourceHeight ?? ""}`;
1721
+ const cached = preparedByVariant.get(cacheKey);
1722
+ if (cached) return cached;
1723
+ const id = markdownImageGraphicId(segment, identity);
1724
+ const imageId = stableTerminalGraphicNumericId(`image:${id}`);
1725
+ const placementId = stableTerminalGraphicNumericId(`placement:${id}`);
1726
+ const sequence = protocol === "kitty" ? createKittyGraphicsSequence(segment.base64, {
1727
+ imageId,
1728
+ placementId,
1729
+ columns: placement.width,
1730
+ rows: placement.height,
1731
+ sourceX: placement.sourceX,
1732
+ sourceY: placement.sourceY,
1733
+ sourceWidth: placement.sourceWidth,
1734
+ sourceHeight: placement.sourceHeight,
1735
+ zIndex: -1
1736
+ }) : protocol === "iterm2" ? createIterm2InlineImageSequence(segment.base64, {
1737
+ width: placement.width,
1738
+ height: placement.height,
1739
+ preserveAspectRatio: true,
1740
+ doNotMoveCursor: true
1741
+ }) : "";
1742
+ if (!sequence) return null;
1743
+ const prepared = Object.freeze({
1744
+ id,
1745
+ sequence,
1746
+ clearSequence: protocol === "kitty" ? createKittyDeleteGraphicsSequence({
1747
+ imageId,
1748
+ placementId
1749
+ }) : void 0,
1750
+ resizeSequence: protocol === "kitty" ? createKittyPlacementSequence({
1751
+ imageId,
1752
+ placementId,
1753
+ columns: placement.width,
1754
+ rows: placement.height,
1755
+ sourceX: placement.sourceX,
1756
+ sourceY: placement.sourceY,
1757
+ sourceWidth: placement.sourceWidth,
1758
+ sourceHeight: placement.sourceHeight
1759
+ }) : void 0
1760
+ });
1761
+ preparedByVariant.set(cacheKey, prepared);
1762
+ return prepared;
1701
1763
  }
1702
1764
  function rememberMarkdownImageGraphic(terminal, id, rect) {
1703
1765
  let active = activeMarkdownImageGraphics.get(terminal);
@@ -1736,57 +1798,36 @@ function clearMarkdownImageGraphics(terminal, rect) {
1736
1798
  clearTrackedMarkdownImageGraphic(terminal, id);
1737
1799
  }
1738
1800
  }
1739
- function queueMarkdownImageGraphic(terminal, segment, identity, rect) {
1801
+ function queueMarkdownImageGraphic(terminal, segment, identity, rect, placementOverride) {
1740
1802
  if (!segment.base64) return "unavailable";
1741
1803
  const output = getTerminalGraphicsOutput(terminal);
1742
1804
  const protocol = output?.capabilities.preferredProtocol;
1743
1805
  if (!output?.capabilities.supported || !isTerminalGraphicsProtocol(protocol)) return "unavailable";
1744
- const width = Math.max(1, Math.floor(segment.displayWidth ?? rect.w));
1745
- const height = Math.max(1, Math.floor(segment.displayHeight ?? 1));
1746
- const id = markdownImageGraphicId(segment, identity);
1747
- const imageId = stableTerminalGraphicNumericId(`image:${id}`);
1748
- const placementId = stableTerminalGraphicNumericId(`placement:${id}`);
1749
- const sequence = protocol === "kitty" ? createKittyGraphicsSequence(segment.base64, {
1750
- imageId,
1751
- placementId,
1752
- columns: width,
1753
- rows: height,
1754
- zIndex: -1
1755
- }) : protocol === "iterm2" ? createIterm2InlineImageSequence(segment.base64, {
1756
- width,
1757
- height,
1758
- preserveAspectRatio: true,
1759
- doNotMoveCursor: true
1760
- }) : "";
1761
- if (!sequence) return "unavailable";
1762
- const clearSequence = protocol === "kitty" ? createKittyDeleteGraphicsSequence({
1763
- imageId,
1764
- placementId
1765
- }) : void 0;
1766
- const resizeSequence = protocol === "kitty" ? createKittyPlacementSequence({
1767
- imageId,
1768
- placementId,
1769
- columns: width,
1770
- rows: height
1771
- }) : void 0;
1806
+ const placement = placementOverride ?? {
1807
+ width: Math.max(1, Math.floor(segment.displayWidth ?? rect.w)),
1808
+ height: Math.max(1, Math.floor(segment.displayHeight ?? 1))
1809
+ };
1810
+ const prepared = prepareMarkdownImageGraphic(segment, identity, protocol, placement);
1811
+ if (!prepared) return "unavailable";
1772
1812
  const accepted = output.queue({
1773
- id,
1813
+ id: prepared.id,
1774
1814
  x: rect.x,
1775
1815
  y: rect.y,
1776
- w: width,
1777
- h: height,
1816
+ w: placement.width,
1817
+ h: placement.height,
1778
1818
  protocol,
1779
- sequence,
1780
- resizeSequence,
1781
- clearSequence,
1819
+ sequence: prepared.sequence,
1820
+ resizeSequence: prepared.resizeSequence,
1821
+ clearSequence: prepared.clearSequence,
1822
+ placementMoveWithoutClear: protocol === "kitty",
1782
1823
  fallbackText: segment.alt ?? (segment.kind === "math" ? segment.raw ?? segment.tex ?? "math" : "image")
1783
1824
  });
1784
- markdownImageDebug(`queue id=${id}`, `accepted=${accepted ? "true" : "false"}`, `x=${rect.x}`, `y=${rect.y}`, `w=${width}`, `h=${height}`, `visibleW=${rect.w}`, `displayW=${segment.displayWidth ?? ""}`, `displayH=${segment.displayHeight ?? ""}`);
1785
- if (accepted) rememberMarkdownImageGraphic(terminal, id, {
1825
+ markdownImageDebug(`queue id=${prepared.id}`, `accepted=${accepted ? "true" : "false"}`, `x=${rect.x}`, `y=${rect.y}`, `w=${placement.width}`, `h=${placement.height}`, `visibleW=${rect.w}`, `displayW=${segment.displayWidth ?? ""}`, `displayH=${segment.displayHeight ?? ""}`);
1826
+ if (accepted) rememberMarkdownImageGraphic(terminal, prepared.id, {
1786
1827
  x: rect.x,
1787
1828
  y: rect.y,
1788
- w: width,
1789
- h: height
1829
+ w: placement.width,
1830
+ h: placement.height
1790
1831
  });
1791
1832
  return accepted ? "queued" : "rejected";
1792
1833
  }
@@ -1809,7 +1850,6 @@ function collectVisibleMarkdownImageGraphicIds(rows, options) {
1809
1850
  logicalX = segmentEnd;
1810
1851
  if (!segment.graphic) continue;
1811
1852
  if (segmentEnd <= clipStart || segmentStart >= clipEnd) continue;
1812
- if (Math.max(segmentStart, clipStart) !== segmentStart) continue;
1813
1853
  const height = Math.max(1, Math.floor(segment.graphic.displayHeight ?? 1));
1814
1854
  if (rowIndex + height <= firstRow || rowIndex >= lastRow) continue;
1815
1855
  const rect = {
@@ -1824,6 +1864,67 @@ function collectVisibleMarkdownImageGraphicIds(rows, options) {
1824
1864
  }
1825
1865
  return keepIds;
1826
1866
  }
1867
+ function queueClippedVisibleMarkdownImageGraphics(terminal, rows, options) {
1868
+ const queuedIds = new Set();
1869
+ const output = getTerminalGraphicsOutput(terminal);
1870
+ if (options.w <= 0 || options.h <= 0 || !output?.capabilities.supported || output.capabilities.preferredProtocol !== "kitty") return queuedIds;
1871
+ const clipStart = Math.max(0, Math.floor(options.clipStart));
1872
+ const firstRow = Math.min(rows.length, Math.max(0, Math.floor(options.rowOffset)));
1873
+ const lastRow = Math.min(rows.length, firstRow + Math.max(0, Math.floor(options.h)));
1874
+ let scanStart = firstRow;
1875
+ while (scanStart > 0 && (rows[scanStart]?.segments.length ?? 0) === 0) scanStart--;
1876
+ for (let rowIndex = scanStart; rowIndex < lastRow; rowIndex++) {
1877
+ const row = rows[rowIndex];
1878
+ if (!row) continue;
1879
+ let logicalX = 0;
1880
+ for (const [segmentIndex, segment] of row.segments.entries()) {
1881
+ const segmentStart = logicalX;
1882
+ logicalX += segment.cells;
1883
+ if (!segment.graphic?.base64) continue;
1884
+ const fullWidth = Math.max(1, Math.floor(segment.graphic.displayWidth ?? segment.cells));
1885
+ const fullHeight = Math.max(1, Math.floor(segment.graphic.displayHeight ?? 1));
1886
+ const fullRect = {
1887
+ x: options.x + segmentStart - clipStart,
1888
+ y: options.y + rowIndex - firstRow,
1889
+ w: fullWidth,
1890
+ h: fullHeight
1891
+ };
1892
+ const visibleRect = {
1893
+ x: Math.max(options.x, fullRect.x),
1894
+ y: Math.max(options.y, fullRect.y),
1895
+ w: Math.min(options.x + options.w, fullRect.x + fullRect.w) - Math.max(options.x, fullRect.x),
1896
+ h: Math.min(options.y + options.h, fullRect.y + fullRect.h) - Math.max(options.y, fullRect.y)
1897
+ };
1898
+ if (visibleRect.w <= 0 || visibleRect.h <= 0) continue;
1899
+ if (visibleRect.x === fullRect.x && visibleRect.y === fullRect.y && visibleRect.w === fullRect.w && visibleRect.h === fullRect.h) continue;
1900
+ if (options.isGraphicCovered?.(visibleRect)) continue;
1901
+ const naturalWidth = Math.floor(segment.graphic.naturalWidth ?? 0);
1902
+ const naturalHeight = Math.floor(segment.graphic.naturalHeight ?? 0);
1903
+ if (naturalWidth <= 0 || naturalHeight <= 0) continue;
1904
+ const offsetX = visibleRect.x - fullRect.x;
1905
+ const offsetY = visibleRect.y - fullRect.y;
1906
+ const sourceX = Math.floor(offsetX * naturalWidth / fullWidth);
1907
+ const sourceY = Math.floor(offsetY * naturalHeight / fullHeight);
1908
+ const sourceRight = Math.max(sourceX + 1, Math.min(naturalWidth, Math.ceil((offsetX + visibleRect.w) * naturalWidth / fullWidth)));
1909
+ const sourceBottom = Math.max(sourceY + 1, Math.min(naturalHeight, Math.ceil((offsetY + visibleRect.h) * naturalHeight / fullHeight)));
1910
+ const identity = `${row.key}:${segmentIndex}`;
1911
+ const result = queueMarkdownImageGraphic(terminal, segment.graphic, identity, {
1912
+ x: visibleRect.x,
1913
+ y: visibleRect.y,
1914
+ w: visibleRect.w
1915
+ }, {
1916
+ width: visibleRect.w,
1917
+ height: visibleRect.h,
1918
+ sourceX,
1919
+ sourceY,
1920
+ sourceWidth: sourceRight - sourceX,
1921
+ sourceHeight: sourceBottom - sourceY
1922
+ });
1923
+ if (result === "queued") queuedIds.add(markdownImageGraphicId(segment.graphic, identity));
1924
+ }
1925
+ }
1926
+ return queuedIds;
1927
+ }
1827
1928
  function paintMarkdownVisualRow(terminal, row, options) {
1828
1929
  const clipStart = Math.max(0, Math.floor(options.clipStart ?? 0));
1829
1930
  const clipEnd = clipStart + options.w;
@@ -1874,7 +1975,8 @@ function paintMarkdownVisualRow(terminal, row, options) {
1874
1975
  h: Math.max(1, Math.floor(segment.graphic.displayHeight ?? 1))
1875
1976
  };
1876
1977
  const graphicCovered = options.isGraphicCovered?.(graphicRect) === true;
1877
- const queueResult = !graphicCovered && visibleStart === segmentStart && visibleCells > 0 && queueMarkdownImageGraphic(terminal, segment.graphic, `${row.key}:${segmentIndex}`, {
1978
+ const graphicId = markdownImageGraphicId(segment.graphic, `${row.key}:${segmentIndex}`);
1979
+ const queueResult = options.prequeuedGraphicIds?.has(graphicId) === true ? "queued" : !graphicCovered && visibleStart === segmentStart && visibleCells > 0 && queueMarkdownImageGraphic(terminal, segment.graphic, `${row.key}:${segmentIndex}`, {
1878
1980
  x: cellX,
1879
1981
  y: options.y,
1880
1982
  w: visibleCells
@@ -1882,7 +1984,7 @@ function paintMarkdownVisualRow(terminal, row, options) {
1882
1984
  const graphicAvailable = queueResult || canAttemptMarkdownImageGraphic(terminal, segment.graphic);
1883
1985
  const queuedGraphic = queueResult === "queued";
1884
1986
  const suppressFallback = queuedGraphic || queueResult === "rejected" || graphicAvailable === true;
1885
- if (queuedGraphic) queuedGraphics.add(markdownImageGraphicId(segment.graphic, `${row.key}:${segmentIndex}`));
1987
+ if (queuedGraphic) queuedGraphics.add(graphicId);
1886
1988
  terminal.write(suppressFallback ? spaces(visibleCells) : sliceByCellsRange(segment.fallbackText ?? segment.graphic.alt ?? (segment.graphic.kind === "math" ? segment.graphic.raw ?? segment.graphic.tex ?? "math" : "image"), 0, visibleCells) || spaces(visibleCells), {
1887
1989
  x: cellX,
1888
1990
  y: options.y,
@@ -1956,13 +2058,19 @@ function markdownStyleSignature(style) {
1956
2058
  style.href ?? ""
1957
2059
  ].join("");
1958
2060
  }
2061
+ const markdownRowSignatureCache = new WeakMap();
2062
+ const markdownRowGraphicSignatureCache = new WeakMap();
1959
2063
  function markdownRowSignature(row) {
1960
2064
  if (!row) return "";
1961
- return [
2065
+ const cached = markdownRowSignatureCache.get(row);
2066
+ if (cached != null) return cached;
2067
+ const signature = [
1962
2068
  row.key,
1963
2069
  row.plainText,
1964
2070
  row.segments.map((segment) => `${segment.text}\u0001${segment.cells}\u0001${markdownStyleSignature(segment.style)}\u0001${segment.graphic?.src ?? ""}\u0001${segment.graphic?.base64 ?? ""}\u0001${segment.fallbackText ?? ""}`).join("")
1965
2071
  ].join("");
2072
+ markdownRowSignatureCache.set(row, signature);
2073
+ return signature;
1966
2074
  }
1967
2075
  function getWheelScrollInput(e) {
1968
2076
  const deltaY = Number(e.deltaY ?? 0);
@@ -1986,7 +2094,11 @@ function getWheelScrollInput(e) {
1986
2094
  }
1987
2095
  function rowGraphicSignature(row) {
1988
2096
  if (!row) return "";
1989
- return row.segments.map((segment) => segment.graphic ? `${segment.graphic.kind}\u0001${segment.graphic.src}\u0001${segment.graphic.base64 ?? ""}` : "").join("");
2097
+ const cached = markdownRowGraphicSignatureCache.get(row);
2098
+ if (cached != null) return cached;
2099
+ const signature = row.segments.filter((segment) => segment.graphic).map((segment) => `${segment.graphic.kind}\u0001${segment.graphic.src}\u0001${segment.graphic.base64 ?? ""}`).join("");
2100
+ markdownRowGraphicSignatureCache.set(row, signature);
2101
+ return signature;
1990
2102
  }
1991
2103
  const TVirtualMarkdown = defineComponent({
1992
2104
  name: "TVirtualMarkdown",
@@ -2118,6 +2230,10 @@ const TVirtualMarkdown = defineComponent({
2118
2230
  imageOcclusionRects: {
2119
2231
  type: Array,
2120
2232
  default: void 0
2233
+ },
2234
+ rowScrollMode: {
2235
+ type: String,
2236
+ default: "off"
2121
2237
  }
2122
2238
  },
2123
2239
  emits: {
@@ -2132,10 +2248,11 @@ const TVirtualMarkdown = defineComponent({
2132
2248
  },
2133
2249
  setup(props, { emit }) {
2134
2250
  const instance = getCurrentInstance();
2135
- const { terminal, defaultStyle, events, scheduler, selection, widthProvider } = useTerminal();
2251
+ const { terminal, defaultStyle, events, scheduler, selection, widthProvider, render, rendererCapabilities } = useTerminal();
2136
2252
  const layout = useLayout();
2137
2253
  const { visible, rootProps } = useVisibility();
2138
2254
  const parentEventZ = inject(EventZIndexContextKey, computed(() => 0));
2255
+ const plane = inject(RenderPlaneContextKey, ref("default"));
2139
2256
  const eventZ = computed(() => (parentEventZ.value ?? 0) + (props.zIndex ?? 0));
2140
2257
  const internalScrollTop = ref(0);
2141
2258
  const documentVersion = ref(0);
@@ -2146,6 +2263,8 @@ const TVirtualMarkdown = defineComponent({
2146
2263
  let rebuildVersion = 0;
2147
2264
  let layoutCache;
2148
2265
  let alive = true;
2266
+ let dirtyRowsHint;
2267
+ let renderNodeId = null;
2149
2268
  const parser = shallowRef(markRaw(createTuiMarkdownParser({
2150
2269
  streaming: props.streaming,
2151
2270
  customHtmlTags: props.customHtmlTags
@@ -2225,6 +2344,13 @@ const TVirtualMarkdown = defineComponent({
2225
2344
  const nextVisibleGraphics = visibleRowGraphics(nextRows, nextScrollTop);
2226
2345
  const visibleChanged = prevVisibleRows.length !== nextVisibleRows.length || prevVisibleRows.some((row, index) => row !== nextVisibleRows[index]) || prevVisibleGraphics.some((row, index) => row !== nextVisibleGraphics[index]);
2227
2346
  if (!builtOnce || prevScrollTop !== nextScrollTop || visibleChanged) {
2347
+ if (visibleChanged && prevScrollTop === nextScrollTop && prevVisibleGraphics.every((signature) => !signature) && nextVisibleGraphics.every((signature) => !signature)) {
2348
+ const rect = normalizedRect();
2349
+ const changedRows = [];
2350
+ const count = Math.max(prevVisibleRows.length, nextVisibleRows.length);
2351
+ for (let index = 0; index < count; index++) if (prevVisibleRows[index] !== nextVisibleRows[index]) changedRows.push(rect.y + index);
2352
+ markRowsDirty(changedRows);
2353
+ } else markRowsDirty(viewportRows());
2228
2354
  documentVersion.value++;
2229
2355
  selection.refresh();
2230
2356
  }
@@ -2301,6 +2427,52 @@ const TVirtualMarkdown = defineComponent({
2301
2427
  for (let index = start; index < end; index++) out.push(rowGraphicSignature(sourceRows[index]));
2302
2428
  return out;
2303
2429
  }
2430
+ function viewportRows() {
2431
+ const rect = normalizedRect();
2432
+ const result = [];
2433
+ for (let y = rect.y; y < rect.y + rect.h; y++) result.push(y);
2434
+ return result;
2435
+ }
2436
+ function exposedRowsForDelta(y, height, delta) {
2437
+ const result = [];
2438
+ if (delta > 0) for (let offset = height - delta; offset < height; offset++) result.push(y + offset);
2439
+ else for (let offset = 0; offset < -delta; offset++) result.push(y + offset);
2440
+ return result;
2441
+ }
2442
+ function markRowsDirty(nextRows) {
2443
+ const rows$1 = new Set(dirtyRowsHint);
2444
+ for (const y of nextRows) rows$1.add(y);
2445
+ dirtyRowsHint = Array.from(rows$1).sort((a, b) => a - b);
2446
+ if (renderNodeId) render.markDirtyRows(renderNodeId, dirtyRowsHint);
2447
+ }
2448
+ function visibleGraphicsAt(scrollTop) {
2449
+ const rect = normalizedRect();
2450
+ const { x: clipX, y: clipY } = clipOffsets();
2451
+ return collectVisibleMarkdownImageGraphicIds(rows.value, {
2452
+ x: rect.x,
2453
+ y: rect.y,
2454
+ w: rect.w,
2455
+ h: rect.h,
2456
+ rowOffset: scrollTop + clipY,
2457
+ clipStart: clipX
2458
+ }).size > 0;
2459
+ }
2460
+ function isClipped() {
2461
+ const rect = normalizedRect();
2462
+ const full = normalizedFullRect();
2463
+ return rect.x !== full.x || rect.y !== full.y || rect.w !== full.w || rect.h !== full.h;
2464
+ }
2465
+ function markScrollDamage(prevTop, nextTop) {
2466
+ const rect = normalizedRect();
2467
+ const delta = nextTop - prevTop;
2468
+ if (rect.h <= 0 || !delta) return;
2469
+ const size = terminal.size();
2470
+ const canShiftRows = props.rowScrollMode === "unsafe-full-row" && rendererCapabilities.value.scrollOperations && rect.x === 0 && rect.w >= size.cols && rect.y >= 0 && rect.y + rect.h <= size.rows && !isClipped() && Math.abs(delta) < rect.h && !dirtyRowsHint?.length && !visibleGraphicsAt(prevTop);
2471
+ if (canShiftRows) {
2472
+ render.unsafeScrollPlaneRows(plane.value, rect.y, rect.y + rect.h, delta);
2473
+ markRowsDirty(exposedRowsForDelta(rect.y, rect.h, delta));
2474
+ } else markRowsDirty(viewportRows());
2475
+ }
2304
2476
  function hasControlledScrollTop() {
2305
2477
  return Object.prototype.hasOwnProperty.call(instance?.vnode.props ?? {}, "scrollTop");
2306
2478
  }
@@ -2308,7 +2480,10 @@ const TVirtualMarkdown = defineComponent({
2308
2480
  const desired = Math.floor(Number(next) || 0);
2309
2481
  const clamped = clamp(desired, 0, maxScrollTop());
2310
2482
  const changed = internalScrollTop.value !== clamped;
2311
- if (changed) internalScrollTop.value = clamped;
2483
+ if (changed) {
2484
+ markScrollDamage(internalScrollTop.value, clamped);
2485
+ internalScrollTop.value = clamped;
2486
+ }
2312
2487
  if (desired !== clamped) {
2313
2488
  emit("update:scrollTop", clamped);
2314
2489
  emit("scroll", clamped);
@@ -2319,7 +2494,10 @@ const TVirtualMarkdown = defineComponent({
2319
2494
  const desired = Math.floor(Number(next) || 0);
2320
2495
  const clamped = clamp(desired, 0, maxScrollTop());
2321
2496
  const changed = internalScrollTop.value !== clamped;
2322
- if (changed) internalScrollTop.value = clamped;
2497
+ if (changed) {
2498
+ markScrollDamage(internalScrollTop.value, clamped);
2499
+ internalScrollTop.value = clamped;
2500
+ }
2323
2501
  if (emitChange && (changed || refreshOptions?.emitClampEvenIfUnchanged && desired !== clamped)) {
2324
2502
  emit("update:scrollTop", clamped);
2325
2503
  emit("scroll", clamped);
@@ -2572,7 +2750,7 @@ const TVirtualMarkdown = defineComponent({
2572
2750
  unsubscribeMathImage();
2573
2751
  clearMarkdownImageGraphics(terminal, fullRect.value);
2574
2752
  });
2575
- useRenderNode(() => ({
2753
+ const renderNode = useRenderNode(() => ({
2576
2754
  zIndex: props.zIndex,
2577
2755
  rect: visible.value ? normalizedRect() : {
2578
2756
  x: 0,
@@ -2580,6 +2758,7 @@ const TVirtualMarkdown = defineComponent({
2580
2758
  w: 0,
2581
2759
  h: 0
2582
2760
  },
2761
+ dirtyRowsHint,
2583
2762
  deps: [
2584
2763
  visible.value,
2585
2764
  absRect.value,
@@ -2592,6 +2771,7 @@ const TVirtualMarkdown = defineComponent({
2592
2771
  props.imageOcclusionRects
2593
2772
  ],
2594
2773
  paint: (dirtyRows) => {
2774
+ dirtyRowsHint = void 0;
2595
2775
  withTextWidthProvider(widthProvider, () => {
2596
2776
  if (!visible.value) return;
2597
2777
  const r = normalizedRect();
@@ -2601,6 +2781,15 @@ const TVirtualMarkdown = defineComponent({
2601
2781
  const isGraphicCovered = (rect) => {
2602
2782
  return props.imageOcclusionRects?.some((item) => intersectRect(rect, item)) === true;
2603
2783
  };
2784
+ const prequeuedGraphicIds = queueClippedVisibleMarkdownImageGraphics(terminal, rows.value, {
2785
+ x: r.x,
2786
+ y: r.y,
2787
+ w: r.w,
2788
+ h: r.h,
2789
+ rowOffset: internalScrollTop.value + clipY,
2790
+ clipStart: clipX,
2791
+ isGraphicCovered
2792
+ });
2604
2793
  const keepGraphicIds = collectVisibleMarkdownImageGraphicIds(rows.value, {
2605
2794
  x: r.x,
2606
2795
  y: r.y,
@@ -2621,6 +2810,7 @@ const TVirtualMarkdown = defineComponent({
2621
2810
  baseStyle,
2622
2811
  clear: true,
2623
2812
  keepGraphicIds,
2813
+ prequeuedGraphicIds,
2624
2814
  isGraphicCovered
2625
2815
  });
2626
2816
  };
@@ -2632,6 +2822,9 @@ const TVirtualMarkdown = defineComponent({
2632
2822
  });
2633
2823
  }
2634
2824
  }));
2825
+ watchEffect(() => {
2826
+ renderNodeId = renderNode.id.value;
2827
+ });
2635
2828
  return () => h("span", rootProps);
2636
2829
  }
2637
2830
  });