@stream-mdx/react 0.4.0 → 1.0.0

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 (106) hide show
  1. package/CHANGELOG.md +385 -4
  2. package/README.md +141 -16
  3. package/dist/blocks-renderer-DAOBFBfi.d.ts +29 -0
  4. package/dist/blocks-renderer-DQEL1kJW.d.cts +29 -0
  5. package/dist/components/bottom-stick-scroll-area.cjs +268 -0
  6. package/dist/components/bottom-stick-scroll-area.d.cts +36 -0
  7. package/dist/components/bottom-stick-scroll-area.d.ts +36 -0
  8. package/dist/components/bottom-stick-scroll-area.mjs +249 -0
  9. package/dist/components/diff-renderer.cjs +86 -0
  10. package/dist/components/diff-renderer.d.cts +11 -0
  11. package/dist/components/diff-renderer.d.ts +11 -0
  12. package/dist/components/diff-renderer.mjs +66 -0
  13. package/dist/components/index.cjs +2879 -1295
  14. package/dist/components/index.d.cts +9 -2
  15. package/dist/components/index.d.ts +9 -2
  16. package/dist/components/index.mjs +2842 -1277
  17. package/dist/components/katex-math.cjs +220 -0
  18. package/dist/components/katex-math.d.cts +10 -0
  19. package/dist/components/katex-math.d.ts +10 -0
  20. package/dist/components/katex-math.mjs +188 -0
  21. package/dist/contexts/math-tracker.cjs +240 -521
  22. package/dist/contexts/math-tracker.mjs +235 -513
  23. package/dist/index-Cmne1yUK.d.cts +723 -0
  24. package/dist/index-D7dyyu3q.d.ts +723 -0
  25. package/dist/index.cjs +13180 -5640
  26. package/dist/index.d.cts +72 -40
  27. package/dist/index.d.ts +72 -40
  28. package/dist/index.mjs +12587 -5038
  29. package/dist/math/display-wrapper.cjs +7 -15
  30. package/dist/math/display-wrapper.d.cts +2 -2
  31. package/dist/math/display-wrapper.d.ts +2 -2
  32. package/dist/math/display-wrapper.mjs +2 -6
  33. package/dist/node-views-Bpc6Gkgl.d.cts +13 -0
  34. package/dist/node-views-DpnYdaAS.d.ts +13 -0
  35. package/dist/renderer/hooks.cjs +35 -20
  36. package/dist/renderer/hooks.d.cts +22 -1
  37. package/dist/renderer/hooks.d.ts +22 -1
  38. package/dist/renderer/hooks.mjs +27 -12
  39. package/dist/renderer/patch-batching.cjs +7 -8
  40. package/dist/renderer/patch-batching.d.cts +1 -1
  41. package/dist/renderer/patch-batching.d.ts +1 -1
  42. package/dist/renderer/patch-batching.mjs +2 -1
  43. package/dist/renderer/patch-coalescing.cjs +5 -8
  44. package/dist/renderer/patch-commit-scheduler.cjs +160 -326
  45. package/dist/renderer/patch-commit-scheduler.d.cts +28 -4
  46. package/dist/renderer/patch-commit-scheduler.d.ts +28 -4
  47. package/dist/renderer/patch-commit-scheduler.mjs +157 -318
  48. package/dist/renderer/store.cjs +3847 -1099
  49. package/dist/renderer/store.d.cts +111 -3
  50. package/dist/renderer/store.d.ts +111 -3
  51. package/dist/renderer/store.mjs +3847 -1085
  52. package/dist/renderer/virtualized-code.cjs +415 -71
  53. package/dist/renderer/virtualized-code.d.cts +24 -23
  54. package/dist/renderer/virtualized-code.d.ts +24 -23
  55. package/dist/renderer/virtualized-code.mjs +409 -62
  56. package/dist/renderer.cjs +10939 -3974
  57. package/dist/renderer.d.cts +100 -29
  58. package/dist/renderer.d.ts +100 -29
  59. package/dist/renderer.mjs +10711 -3697
  60. package/dist/server-regions-CPLHGli0.d.cts +32 -0
  61. package/dist/server-regions-mUhyoKi7.d.ts +32 -0
  62. package/dist/server.cjs +10286 -3365
  63. package/dist/server.d.cts +17 -247
  64. package/dist/server.d.ts +17 -247
  65. package/dist/server.mjs +10565 -3619
  66. package/dist/{streaming-markdown-DOc2uLiP.d.ts → streaming-markdown-yELbJxyH.d.ts} +51 -55
  67. package/dist/{streaming-markdown-D-JcByd9.d.cts → streaming-markdown-zVwFuaJG.d.cts} +51 -55
  68. package/dist/streaming-markdown.cjs +11828 -4879
  69. package/dist/streaming-markdown.d.cts +9 -2
  70. package/dist/streaming-markdown.d.ts +9 -2
  71. package/dist/streaming-markdown.mjs +11820 -4821
  72. package/dist/trusted-mdx/index.cjs +833 -0
  73. package/dist/trusted-mdx/index.d.cts +11 -0
  74. package/dist/trusted-mdx/index.d.ts +11 -0
  75. package/dist/trusted-mdx/index.mjs +807 -0
  76. package/dist/trusted-mdx/native-children-slot.cjs +45 -0
  77. package/dist/trusted-mdx/native-children-slot.d.cts +11 -0
  78. package/dist/trusted-mdx/native-children-slot.d.ts +11 -0
  79. package/dist/trusted-mdx/native-children-slot.mjs +24 -0
  80. package/dist/trusted-mdx/server.cjs +752 -0
  81. package/dist/trusted-mdx/server.d.cts +24 -0
  82. package/dist/trusted-mdx/server.d.ts +24 -0
  83. package/dist/trusted-mdx/server.mjs +724 -0
  84. package/dist/trusted-mdx-contract-ElKpkDI5.d.cts +113 -0
  85. package/dist/trusted-mdx-contract-ElKpkDI5.d.ts +113 -0
  86. package/dist/utils/inline-html.cjs +246 -147
  87. package/dist/utils/inline-html.d.cts +39 -6
  88. package/dist/utils/inline-html.d.ts +39 -6
  89. package/dist/utils/inline-html.mjs +237 -134
  90. package/package.json +52 -18
  91. package/shadcn/README.md +28 -0
  92. package/shadcn/bottom-stick-scroll-area.tsx +358 -0
  93. package/dist/index-D2MYusAM.d.cts +0 -248
  94. package/dist/index-D2MYusAM.d.ts +0 -248
  95. package/dist/mdx-client.cjs +0 -408
  96. package/dist/mdx-client.d.cts +0 -113
  97. package/dist/mdx-client.d.ts +0 -113
  98. package/dist/mdx-client.mjs +0 -369
  99. package/dist/mdx-coordinator.cjs +0 -563
  100. package/dist/mdx-coordinator.d.cts +0 -21
  101. package/dist/mdx-coordinator.d.ts +0 -21
  102. package/dist/mdx-coordinator.mjs +0 -529
  103. package/dist/renderer/node-views.cjs +0 -1642
  104. package/dist/renderer/node-views.d.cts +0 -12
  105. package/dist/renderer/node-views.d.ts +0 -12
  106. package/dist/renderer/node-views.mjs +0 -1616
@@ -1,43 +1,44 @@
1
- import React from 'react';
2
- import { TokenLineV1 } from '@stream-mdx/core';
1
+ import React__default from 'react';
3
2
 
4
3
  interface VirtualizedCodeConfig {
5
4
  enabled: boolean;
6
5
  windowSize: number;
7
6
  bufferSize: number;
8
7
  virtualizeThreshold: number;
8
+ followTail?: boolean;
9
9
  }
10
10
  declare const DEFAULT_VIRTUALIZED_CODE_CONFIG: VirtualizedCodeConfig;
11
- interface VirtualizedLine {
12
- id: string;
13
- index: number;
14
- text: string;
15
- html?: string | null;
16
- tokens?: TokenLineV1 | null;
17
- }
18
- interface VirtualizedCodeWindow {
11
+ declare function resolveVirtualizedCodeConfig(options?: Partial<VirtualizedCodeConfig>): VirtualizedCodeConfig;
12
+ interface VirtualizedCodeRange {
19
13
  startIndex: number;
20
14
  endIndex: number;
21
15
  visibleStart: number;
22
16
  visibleEnd: number;
23
- visibleLines: VirtualizedLine[];
24
17
  totalLines: number;
25
18
  mountedLines: number;
26
19
  }
27
- /**
28
- * Calculate which lines should be rendered based on scroll position
29
- */
30
- declare function calculateCodeWindow(lines: ReadonlyArray<VirtualizedLine>, scrollTop: number, containerHeight: number, lineHeight: number, config: VirtualizedCodeConfig): VirtualizedCodeWindow;
31
- /**
32
- * Hook for virtualized code block scrolling
33
- */
34
- declare function useVirtualizedCode(lines: ReadonlyArray<VirtualizedLine>, config?: VirtualizedCodeConfig): {
35
- containerRef: React.MutableRefObject<HTMLDivElement | null>;
36
- window: VirtualizedCodeWindow;
37
- handleScroll: (e: React.UIEvent<HTMLDivElement>) => void;
20
+ declare function calculateCodeWindow(totalLines: number, scrollTop: number, containerHeight: number, lineHeight: number, config: VirtualizedCodeConfig): VirtualizedCodeRange;
21
+ interface CodeViewportRenderRange {
22
+ startIndex: number;
23
+ endIndex: number;
24
+ visibleStart: number;
25
+ visibleEnd: number;
26
+ spacerBeforeHeight: number;
27
+ }
28
+ /** Owns viewport geometry and scroll position only; record ranges subscribe separately. */
29
+ declare function useVirtualizedCode(totalLines: number, generation: number, config?: VirtualizedCodeConfig, ownerId?: string, configurationRevision?: number, ownerIdentity?: object): {
30
+ containerRef: React__default.MutableRefObject<HTMLDivElement | null>;
31
+ isPrinting: boolean;
32
+ selectionLineIndices: readonly number[];
33
+ window: VirtualizedCodeRange;
34
+ renderRanges: readonly CodeViewportRenderRange[];
35
+ mountedLineCount: number;
36
+ spacerTopHeight: number;
37
+ spacerBottomHeight: number;
38
+ handleScroll: (event: React__default.UIEvent<HTMLDivElement>) => void;
38
39
  scrollToLine: (lineIndex: number) => void;
39
40
  lineHeight: number;
40
41
  config: VirtualizedCodeConfig;
41
42
  };
42
43
 
43
- export { DEFAULT_VIRTUALIZED_CODE_CONFIG, type VirtualizedCodeConfig, type VirtualizedCodeWindow, type VirtualizedLine, calculateCodeWindow, useVirtualizedCode };
44
+ export { DEFAULT_VIRTUALIZED_CODE_CONFIG, type VirtualizedCodeConfig, type VirtualizedCodeRange, calculateCodeWindow, resolveVirtualizedCodeConfig, useVirtualizedCode };
@@ -2,98 +2,445 @@
2
2
 
3
3
  // src/renderer/virtualized-code.tsx
4
4
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
5
+ import { flushSync } from "react-dom";
6
+
7
+ // src/renderer/viewport-height-index.ts
8
+ var ViewportHeightIndex = class {
9
+ constructor() {
10
+ this.count = 0;
11
+ this.estimate = 20;
12
+ this.measuredHeights = /* @__PURE__ */ new Map();
13
+ this.heightDeltas = /* @__PURE__ */ new Map();
14
+ }
15
+ reset(count, estimate) {
16
+ this.count = Number.isFinite(count) ? Math.max(0, Math.floor(count)) : 0, this.estimate = Number.isFinite(estimate) && estimate > 0 ? estimate : 20, this.measuredHeights.clear(), this.heightDeltas.clear();
17
+ }
18
+ resize(count) {
19
+ let nextCount = Number.isFinite(count) ? Math.max(0, Math.floor(count)) : 0;
20
+ if (nextCount !== this.count) {
21
+ if (nextCount < this.count) {
22
+ for (let index = nextCount; index < this.count; index += 1)
23
+ this.measuredHeights.delete(index), this.heightDeltas.delete(index + 1);
24
+ this.count = nextCount;
25
+ return;
26
+ }
27
+ for (let index = this.count + 1; index <= nextCount; index += 1) {
28
+ let start = index - (index & -index), delta = this.prefix(index - 1) - this.prefix(start);
29
+ delta !== 0 && this.heightDeltas.set(index, delta);
30
+ }
31
+ this.count = nextCount;
32
+ }
33
+ }
34
+ setHeight(index, height) {
35
+ if (!Number.isSafeInteger(index) || index < 0 || index >= this.count || !Number.isFinite(height) || height <= 0)
36
+ return !1;
37
+ let previous = this.measuredHeights.get(index) ?? this.estimate;
38
+ return Math.abs(previous - height) <= 0.5 ? !1 : (this.measuredHeights.set(index, height), this.add(index + 1, height - previous), !0);
39
+ }
40
+ offsetAt(index) {
41
+ let end = Number.isFinite(index) ? Math.max(0, Math.min(this.count, Math.floor(index))) : 0;
42
+ return end * this.estimate + this.prefix(end);
43
+ }
44
+ indexAt(offset) {
45
+ if (this.count === 0) return 0;
46
+ let target = Number.isNaN(offset) ? 0 : Math.max(0, Math.min(offset, this.offsetAt(this.count))), index = 0, delta = 0, bit = 1;
47
+ for (; bit <= Math.floor(this.count / 2); ) bit *= 2;
48
+ for (; bit > 0; ) {
49
+ let next = index + bit;
50
+ if (next <= this.count) {
51
+ let nextDelta = delta + (this.heightDeltas.get(next) ?? 0);
52
+ next * this.estimate + nextDelta <= target && (index = next, delta = nextDelta);
53
+ }
54
+ bit = Math.floor(bit / 2);
55
+ }
56
+ return index;
57
+ }
58
+ prefix(count) {
59
+ let total = 0;
60
+ for (let index = count; index > 0; index -= index & -index) total += this.heightDeltas.get(index) ?? 0;
61
+ return total;
62
+ }
63
+ add(index, delta) {
64
+ for (let cursor = index; cursor <= this.count; cursor += cursor & -cursor) {
65
+ let next = (this.heightDeltas.get(cursor) ?? 0) + delta;
66
+ next === 0 ? this.heightDeltas.delete(cursor) : this.heightDeltas.set(cursor, next);
67
+ }
68
+ }
69
+ };
70
+
71
+ // src/renderer/virtualized-code.tsx
5
72
  var DEFAULT_VIRTUALIZED_CODE_CONFIG = {
6
- enabled: true,
73
+ enabled: !0,
7
74
  windowSize: 100,
8
- // Render 100 lines at a time
9
75
  bufferSize: 50,
10
- // +50 lines buffer above/below
11
- virtualizeThreshold: 200
12
- // Only virtualize if 200+ lines
76
+ virtualizeThreshold: 200,
77
+ followTail: !1
13
78
  };
14
- function calculateCodeWindow(lines, scrollTop, containerHeight, lineHeight, config) {
15
- const totalLines = lines.length;
16
- if (!config.enabled || totalLines < config.virtualizeThreshold) {
79
+ function resolveVirtualizedCodeConfig(options) {
80
+ return {
81
+ ...DEFAULT_VIRTUALIZED_CODE_CONFIG,
82
+ ...options,
83
+ followTail: options?.followTail ?? DEFAULT_VIRTUALIZED_CODE_CONFIG.followTail
84
+ };
85
+ }
86
+ var DEFAULT_CODE_LINE_HEIGHT = 20, CODE_FOLLOW_TAIL_EPS_BOTTOM_PX = 2, CODE_FOLLOW_TAIL_DETACH_SCROLL_DELTA_PX = 1, DEFAULT_CODE_VIEWPORT_OWNER = {};
87
+ function calculateCodeWindow(totalLines, scrollTop, containerHeight, lineHeight, config) {
88
+ let lineCount = Math.max(0, totalLines);
89
+ if (!config.enabled || lineCount < config.virtualizeThreshold)
17
90
  return {
18
91
  startIndex: 0,
19
- endIndex: totalLines,
92
+ endIndex: lineCount,
20
93
  visibleStart: 0,
21
- visibleEnd: totalLines,
22
- visibleLines: lines,
23
- totalLines,
24
- mountedLines: totalLines
94
+ visibleEnd: lineCount,
95
+ totalLines: lineCount,
96
+ mountedLines: lineCount
25
97
  };
26
- }
27
- const firstVisibleLine = Math.floor(scrollTop / lineHeight);
28
- const lastVisibleLine = Math.ceil((scrollTop + containerHeight) / lineHeight);
29
- const visibleStart = Math.max(0, firstVisibleLine);
30
- const visibleEnd = Math.min(totalLines, Math.max(visibleStart, lastVisibleLine));
31
- const startIndex = Math.max(0, visibleStart - config.bufferSize);
32
- const endIndex = Math.min(totalLines, visibleEnd + config.bufferSize);
98
+ let measuredLineHeight = lineHeight > 0 ? lineHeight : 20, firstVisibleLine = Math.floor(Math.max(0, scrollTop) / measuredLineHeight), visibleLineCount = containerHeight > 0 ? Math.max(1, Math.ceil(containerHeight / measuredLineHeight)) : Math.max(1, config.windowSize), visibleStart = Math.min(lineCount, firstVisibleLine), visibleEnd = Math.min(lineCount, visibleStart + visibleLineCount), bufferSize = Math.max(0, config.bufferSize), startIndex = Math.max(0, visibleStart - bufferSize), endIndex = Math.min(lineCount, visibleEnd + bufferSize);
33
99
  return {
34
100
  startIndex,
35
101
  endIndex,
36
102
  visibleStart,
37
103
  visibleEnd,
38
- visibleLines: lines.slice(startIndex, endIndex),
39
- totalLines,
104
+ totalLines: lineCount,
40
105
  mountedLines: endIndex - startIndex
41
106
  };
42
107
  }
43
- function useVirtualizedCode(lines, config = DEFAULT_VIRTUALIZED_CODE_CONFIG) {
44
- const containerRef = useRef(null);
45
- const [scrollTop, setScrollTop] = useState(0);
46
- const [containerHeight, setContainerHeight] = useState(0);
47
- const lineHeightRef = useRef(20);
108
+ var EMPTY_SELECTION_LINE_INDICES = [];
109
+ function calculateMeasuredCodeWindow(totalLines, scrollTop, containerHeight, heights, config) {
110
+ let lineCount = Math.max(0, totalLines);
111
+ if (!config.enabled || lineCount < config.virtualizeThreshold)
112
+ return {
113
+ window: calculateCodeWindow(lineCount, scrollTop, containerHeight, DEFAULT_CODE_LINE_HEIGHT, config),
114
+ spacerTopHeight: 0,
115
+ spacerBottomHeight: 0
116
+ };
117
+ let totalHeight = heights.offsetAt(lineCount), viewportHeight = containerHeight > 0 ? containerHeight : Math.max(1, config.windowSize) * DEFAULT_CODE_LINE_HEIGHT, top = Math.min(
118
+ Math.max(0, scrollTop),
119
+ Math.max(0, totalHeight - viewportHeight)
120
+ ), bottom = top + viewportHeight, visibleStart = Math.min(lineCount, heights.indexAt(top)), visibleEnd = heights.indexAt(bottom);
121
+ visibleEnd < lineCount && heights.offsetAt(visibleEnd) < bottom && (visibleEnd += 1), visibleEnd = Math.min(lineCount, Math.max(visibleStart, visibleEnd));
122
+ let bufferSize = Math.max(0, config.bufferSize), startIndex = Math.max(0, visibleStart - bufferSize), endIndex = Math.min(lineCount, visibleEnd + bufferSize);
123
+ return {
124
+ window: {
125
+ startIndex,
126
+ endIndex,
127
+ visibleStart,
128
+ visibleEnd,
129
+ totalLines: lineCount,
130
+ mountedLines: endIndex - startIndex
131
+ },
132
+ spacerTopHeight: heights.offsetAt(startIndex),
133
+ spacerBottomHeight: Math.max(0, totalHeight - heights.offsetAt(endIndex))
134
+ };
135
+ }
136
+ function useVirtualizedCode(totalLines, generation, config = DEFAULT_VIRTUALIZED_CODE_CONFIG, ownerId = "", configurationRevision = 0, ownerIdentity = DEFAULT_CODE_VIEWPORT_OWNER) {
137
+ let containerRef = useRef(null), [scrollTop, setScrollTop] = useState(0), [containerHeight, setContainerHeight] = useState(0), [containerWidth, setContainerWidth] = useState(0), [measureVersion, setMeasureVersion] = useState(0), [selectionState, setSelectionState] = useState(() => ({
138
+ ownerId,
139
+ generation,
140
+ lineIndices: EMPTY_SELECTION_LINE_INDICES
141
+ })), heightIndexRef = useRef(null), heightIndex = heightIndexRef.current;
142
+ heightIndex === null && (heightIndex = new ViewportHeightIndex(), heightIndex.reset(totalLines, DEFAULT_CODE_LINE_HEIGHT), heightIndexRef.current = heightIndex);
143
+ let indexedCountRef = useRef(totalLines), totalLinesRef = useRef(totalLines);
144
+ totalLinesRef.current = totalLines;
145
+ let measuredWidthRef = useRef(null), measurementGenerationRef = useRef(0), previousScrollTopRef = useRef(null), followTailRef = useRef(!1), followTailOptInRef = useRef(config.followTail === !0);
146
+ followTailOptInRef.current = config.followTail === !0, followTailOptInRef.current || (followTailRef.current = !1);
147
+ let writeScrollTop = useCallback((container, nextTop) => {
148
+ container.scrollTop = nextTop, previousScrollTopRef.current = container.scrollTop;
149
+ }, []), viewportAnchorRef = useRef(null), geometryIdentityRef = useRef(null), printOwnerGenerationRef = useRef(0), [printOwner, setPrintOwner] = useState(null), isPrintingRef = useRef(!1), printGeometrySuspendedRef = useRef(!1), restoringPrintScrollRef = useRef(!1), printSnapshotRef = useRef(null), rowResizeObserverRef = useRef(null), printRowsRef = useRef(/* @__PURE__ */ new Set()), printEligible = config.enabled && totalLines >= config.virtualizeThreshold, isPrinting = printOwner?.identity === ownerIdentity && printOwner.generation === printOwnerGenerationRef.current, previousIdentity = geometryIdentityRef.current;
150
+ previousIdentity === null && (previousIdentity = {
151
+ ownerId,
152
+ ownerIdentity,
153
+ generation,
154
+ configurationRevision,
155
+ enabled: config.enabled,
156
+ windowSize: config.windowSize,
157
+ bufferSize: config.bufferSize,
158
+ virtualizeThreshold: config.virtualizeThreshold
159
+ }, geometryIdentityRef.current = previousIdentity);
160
+ let geometryReady = previousIdentity.ownerId === ownerId && previousIdentity.ownerIdentity === ownerIdentity && previousIdentity.generation === generation && previousIdentity.configurationRevision === configurationRevision && previousIdentity.enabled === config.enabled && previousIdentity.windowSize === config.windowSize && previousIdentity.bufferSize === config.bufferSize && previousIdentity.virtualizeThreshold === config.virtualizeThreshold && indexedCountRef.current === totalLines, selectionLineIndices = selectionState.ownerId === ownerId && selectionState.generation === generation ? selectionState.lineIndices : EMPTY_SELECTION_LINE_INDICES, selectionTrackingEnabled = config.enabled && totalLines >= config.virtualizeThreshold, refreshSelection = useCallback(() => {
161
+ let container = containerRef.current;
162
+ if (!container || !selectionTrackingEnabled) return;
163
+ let selection = container.ownerDocument.getSelection(), nextLineIndices = [], retainEndpoint = (node) => {
164
+ if (!node) return;
165
+ let row = (node.nodeType === 1 ? node : node.parentElement)?.closest(".line[data-line]");
166
+ if (!row || !container.contains(row)) return;
167
+ let lineNumber = Number(row.getAttribute("data-line")), lineIndex = lineNumber - 1;
168
+ !Number.isSafeInteger(lineNumber) || lineNumber <= 0 || lineIndex >= totalLinesRef.current || nextLineIndices.includes(lineIndex) || nextLineIndices.push(lineIndex);
169
+ };
170
+ selection && selection.rangeCount > 0 && (retainEndpoint(selection.anchorNode), retainEndpoint(selection.focusNode)), nextLineIndices.sort((left, right) => left - right), setSelectionState((previous) => previous.ownerId === ownerId && previous.generation === generation && previous.lineIndices.length === nextLineIndices.length && previous.lineIndices.every((lineIndex, index) => lineIndex === nextLineIndices[index]) ? previous : { ownerId, generation, lineIndices: nextLineIndices });
171
+ }, [generation, ownerId, selectionTrackingEnabled]), layout = useMemo(() => {
172
+ if (geometryReady)
173
+ return calculateMeasuredCodeWindow(totalLines, scrollTop, containerHeight, heightIndex, config);
174
+ let window = calculateCodeWindow(
175
+ totalLines,
176
+ scrollTop,
177
+ containerHeight,
178
+ DEFAULT_CODE_LINE_HEIGHT,
179
+ config
180
+ ), virtualized = config.enabled && totalLines >= config.virtualizeThreshold;
181
+ return {
182
+ window,
183
+ spacerTopHeight: virtualized ? window.startIndex * DEFAULT_CODE_LINE_HEIGHT : 0,
184
+ spacerBottomHeight: virtualized ? (window.totalLines - window.endIndex) * DEFAULT_CODE_LINE_HEIGHT : 0
185
+ };
186
+ }, [config, containerHeight, geometryReady, heightIndex, measureVersion, scrollTop, totalLines]), renderLayout = useMemo(() => {
187
+ let intervals = [], primary = layout.window;
188
+ primary.endIndex > primary.startIndex && intervals.push({
189
+ startIndex: primary.startIndex,
190
+ endIndex: primary.endIndex,
191
+ visibleStart: primary.visibleStart,
192
+ visibleEnd: primary.visibleEnd
193
+ });
194
+ for (let lineIndex of selectionLineIndices) {
195
+ if (!Number.isSafeInteger(lineIndex) || lineIndex < 0 || lineIndex >= totalLines || lineIndex >= primary.startIndex && lineIndex < primary.endIndex)
196
+ continue;
197
+ let previous = intervals[intervals.length - 1];
198
+ previous && (previous.startIndex !== primary.startIndex || previous.endIndex !== primary.endIndex) && previous.endIndex === lineIndex ? (previous.endIndex += 1, previous.visibleEnd = lineIndex + 1) : intervals.push({
199
+ startIndex: lineIndex,
200
+ endIndex: lineIndex + 1,
201
+ visibleStart: lineIndex,
202
+ visibleEnd: lineIndex + 1
203
+ });
204
+ }
205
+ intervals.sort((left, right) => left.startIndex - right.startIndex);
206
+ let offsetAt = geometryReady ? (index) => heightIndex.offsetAt(index) : (index) => index * DEFAULT_CODE_LINE_HEIGHT, previousEnd = 0, mountedLines = 0, ranges = intervals.map((interval, index) => {
207
+ let spacerBeforeHeight = index === 0 ? 0 : Math.max(0, offsetAt(interval.startIndex) - offsetAt(previousEnd));
208
+ return previousEnd = interval.endIndex, mountedLines += interval.endIndex - interval.startIndex, { ...interval, spacerBeforeHeight };
209
+ });
210
+ return {
211
+ ranges,
212
+ spacerTopHeight: ranges.length > 0 ? offsetAt(ranges[0].startIndex) : 0,
213
+ spacerBottomHeight: Math.max(0, offsetAt(totalLines) - offsetAt(previousEnd)),
214
+ mountedLines
215
+ };
216
+ }, [geometryReady, heightIndex, layout.window, selectionLineIndices, totalLines]);
48
217
  useEffect(() => {
49
- if (!containerRef.current || lines.length === 0) return;
50
- const timeoutId = setTimeout(() => {
51
- if (!containerRef.current) return;
52
- const firstLine = containerRef.current.querySelector(".line");
53
- if (firstLine) {
54
- const measuredHeight = firstLine.offsetHeight || 20;
55
- if (measuredHeight !== lineHeightRef.current) {
56
- lineHeightRef.current = measuredHeight;
57
- }
218
+ let container = containerRef.current;
219
+ if (!container || !selectionTrackingEnabled) return;
220
+ let ownerDocument = container.ownerDocument;
221
+ return ownerDocument.addEventListener("selectionchange", refreshSelection), refreshSelection(), () => ownerDocument.removeEventListener("selectionchange", refreshSelection);
222
+ }, [refreshSelection, selectionTrackingEnabled]), useEffect(() => {
223
+ let container = containerRef.current;
224
+ if (!container || config.followTail !== !0) return;
225
+ let scrollTop2 = container.scrollTop, distanceToBottom = container.scrollHeight - container.clientHeight - scrollTop2;
226
+ followTailRef.current = distanceToBottom <= CODE_FOLLOW_TAIL_EPS_BOTTOM_PX, previousScrollTopRef.current = scrollTop2;
227
+ }, [config.followTail]), useEffect(() => {
228
+ if (!printEligible) return;
229
+ let container = containerRef.current;
230
+ if (!container) return;
231
+ let ownerWindow = container.ownerDocument.defaultView;
232
+ if (!ownerWindow) return;
233
+ let owner = ++printOwnerGenerationRef.current, active = !0, isCurrentOwner = () => active && printOwnerGenerationRef.current === owner && containerRef.current === container, onBeforePrint = () => {
234
+ if (!isCurrentOwner() || !container.isConnected || isPrintingRef.current) return;
235
+ isPrintingRef.current = !0, printGeometrySuspendedRef.current = !0, printSnapshotRef.current = {
236
+ identity: ownerIdentity,
237
+ owner,
238
+ scrollTop: container.scrollTop,
239
+ followTail: followTailRef.current,
240
+ anchor: viewportAnchorRef.current
241
+ }, flushSync(() => {
242
+ setPrintOwner({ identity: ownerIdentity, generation: owner });
243
+ });
244
+ let observer = rowResizeObserverRef.current;
245
+ if (observer)
246
+ for (let row of container.querySelectorAll(".line[data-line]"))
247
+ observer.observe(row), printRowsRef.current.add(row);
248
+ }, onAfterPrint = () => {
249
+ if (!isCurrentOwner() || !isPrintingRef.current) return;
250
+ let snapshot = printSnapshotRef.current;
251
+ if (!snapshot || snapshot.owner !== owner) return;
252
+ let restoreReader = snapshot.identity === ownerIdentity && container.isConnected;
253
+ restoreReader && (restoringPrintScrollRef.current = !0), isPrintingRef.current = !1, printSnapshotRef.current = null, flushSync(() => {
254
+ setPrintOwner((current) => current?.generation === owner ? null : current);
255
+ }), restoreReader && (followTailRef.current = snapshot.followTail && followTailOptInRef.current, viewportAnchorRef.current = snapshot.anchor, flushSync(() => setScrollTop(container.scrollTop)));
256
+ let observer = rowResizeObserverRef.current;
257
+ for (let row of printRowsRef.current)
258
+ container.contains(row) || observer?.unobserve(row);
259
+ printRowsRef.current.clear(), ownerWindow.requestAnimationFrame(() => {
260
+ !isCurrentOwner() || isPrintingRef.current || (restoreReader && (restoringPrintScrollRef.current = !1), printGeometrySuspendedRef.current = !1);
261
+ });
262
+ };
263
+ return ownerWindow.addEventListener("beforeprint", onBeforePrint), ownerWindow.addEventListener("afterprint", onAfterPrint), () => {
264
+ if (active = !1, ownerWindow.removeEventListener("beforeprint", onBeforePrint), ownerWindow.removeEventListener("afterprint", onAfterPrint), printOwnerGenerationRef.current !== owner) return;
265
+ printOwnerGenerationRef.current++, restoringPrintScrollRef.current = !1, printGeometrySuspendedRef.current = !1, isPrintingRef.current = !1, printSnapshotRef.current = null;
266
+ let observer = rowResizeObserverRef.current;
267
+ for (let row of printRowsRef.current)
268
+ container.contains(row) || observer?.unobserve(row);
269
+ printRowsRef.current.clear();
270
+ };
271
+ }, [ownerIdentity, printEligible]), useEffect(() => {
272
+ let previous = geometryIdentityRef.current;
273
+ if (!previous) return;
274
+ let ownerChanged = previous.ownerId !== ownerId || previous.ownerIdentity !== ownerIdentity || previous.generation !== generation, sourceConfigurationChanged = previous.configurationRevision !== configurationRevision, presentationOptionsChanged = previous.enabled !== config.enabled || previous.windowSize !== config.windowSize || previous.bufferSize !== config.bufferSize || previous.virtualizeThreshold !== config.virtualizeThreshold, lineCountChanged = indexedCountRef.current !== totalLines;
275
+ if (ownerChanged || sourceConfigurationChanged)
276
+ heightIndex.reset(totalLines, DEFAULT_CODE_LINE_HEIGHT), indexedCountRef.current = totalLines, viewportAnchorRef.current = null;
277
+ else if (lineCountChanged) {
278
+ let previousCount = indexedCountRef.current;
279
+ if (heightIndex.resize(totalLines), indexedCountRef.current = totalLines, totalLines < previousCount) {
280
+ let anchor = viewportAnchorRef.current;
281
+ anchor && anchor.lineIndex >= totalLines && (viewportAnchorRef.current = null);
282
+ }
283
+ let container = containerRef.current;
284
+ if (totalLines > previousCount && container && followTailRef.current && !isPrintingRef.current && !printGeometrySuspendedRef.current && !restoringPrintScrollRef.current) {
285
+ let nextScrollTop = Math.max(0, container.scrollHeight - container.clientHeight);
286
+ container.scrollTop !== nextScrollTop && (writeScrollTop(container, nextScrollTop), setScrollTop(nextScrollTop));
58
287
  }
59
- }, 0);
60
- return () => clearTimeout(timeoutId);
61
- }, [lines.length]);
62
- const handleScroll = useCallback((e) => {
63
- const target = e.currentTarget;
64
- setScrollTop(target.scrollTop);
65
- setContainerHeight(target.clientHeight);
66
- }, []);
288
+ }
289
+ if (ownerChanged) {
290
+ followTailRef.current = !1;
291
+ let container = containerRef.current;
292
+ container && (container.scrollTop !== 0 ? writeScrollTop(container, 0) : previousScrollTopRef.current = 0), setScrollTop(0);
293
+ }
294
+ (ownerChanged || sourceConfigurationChanged || presentationOptionsChanged || lineCountChanged) && ((ownerChanged || sourceConfigurationChanged || presentationOptionsChanged) && (measurementGenerationRef.current += 1), setMeasureVersion((version) => version + 1)), geometryIdentityRef.current = {
295
+ ownerId,
296
+ ownerIdentity,
297
+ generation,
298
+ configurationRevision,
299
+ enabled: config.enabled,
300
+ windowSize: config.windowSize,
301
+ bufferSize: config.bufferSize,
302
+ virtualizeThreshold: config.virtualizeThreshold
303
+ };
304
+ }, [
305
+ config.bufferSize,
306
+ config.enabled,
307
+ config.virtualizeThreshold,
308
+ config.windowSize,
309
+ configurationRevision,
310
+ generation,
311
+ heightIndex,
312
+ ownerId,
313
+ ownerIdentity,
314
+ totalLines,
315
+ writeScrollTop
316
+ ]), useEffect(() => {
317
+ let container = containerRef.current;
318
+ if (!container || typeof ResizeObserver > "u") return;
319
+ let resizeObserver = new ResizeObserver((entries) => {
320
+ if (!(isPrintingRef.current || printGeometrySuspendedRef.current || restoringPrintScrollRef.current))
321
+ for (let entry of entries) {
322
+ if (entry.target !== container) continue;
323
+ let nextHeight = Math.ceil(entry.contentRect.height || container.clientHeight);
324
+ setContainerHeight((current) => current === nextHeight ? current : nextHeight);
325
+ let nextWidth = Math.ceil(container.clientWidth || entry.contentRect.width), previousWidth = measuredWidthRef.current;
326
+ if (previousWidth === null)
327
+ measuredWidthRef.current = nextWidth;
328
+ else if (previousWidth !== nextWidth) {
329
+ measuredWidthRef.current = nextWidth;
330
+ let lineCount = totalLinesRef.current;
331
+ heightIndex.reset(lineCount, DEFAULT_CODE_LINE_HEIGHT), indexedCountRef.current = lineCount, measurementGenerationRef.current += 1, viewportAnchorRef.current = null, setMeasureVersion((version) => version + 1);
332
+ }
333
+ setContainerWidth((current) => current === nextWidth ? current : nextWidth);
334
+ }
335
+ });
336
+ return resizeObserver.observe(container), () => resizeObserver.disconnect();
337
+ }, [heightIndex]);
338
+ let handleScroll = useCallback((event) => {
339
+ let target = event.currentTarget;
340
+ if (target !== containerRef.current || isPrintingRef.current || restoringPrintScrollRef.current) return;
341
+ refreshSelection();
342
+ let nextScrollTop = target.scrollTop, previousScrollTop = previousScrollTopRef.current, scrolledUp = previousScrollTop !== null && previousScrollTop - nextScrollTop > CODE_FOLLOW_TAIL_DETACH_SCROLL_DELTA_PX;
343
+ previousScrollTopRef.current = nextScrollTop, setScrollTop(nextScrollTop), setContainerHeight(target.clientHeight);
344
+ let atBottom = target.scrollHeight - target.clientHeight - nextScrollTop <= CODE_FOLLOW_TAIL_EPS_BOTTOM_PX;
345
+ if (config.followTail !== !0 || scrolledUp ? followTailRef.current = !1 : atBottom && (followTailRef.current = !0), atBottom && !scrolledUp) {
346
+ viewportAnchorRef.current = null;
347
+ return;
348
+ }
349
+ let lineIndex = Math.min(
350
+ Math.max(0, totalLines - 1),
351
+ geometryReady ? heightIndex.indexAt(nextScrollTop) : Math.floor(Math.max(0, nextScrollTop) / DEFAULT_CODE_LINE_HEIGHT)
352
+ ), lineTop = geometryReady ? heightIndex.offsetAt(lineIndex) : lineIndex * DEFAULT_CODE_LINE_HEIGHT;
353
+ viewportAnchorRef.current = {
354
+ lineIndex,
355
+ offset: lineTop - nextScrollTop,
356
+ ownerId,
357
+ ownerIdentity,
358
+ generation,
359
+ configurationRevision
360
+ };
361
+ }, [config.followTail, configurationRevision, generation, geometryReady, heightIndex, ownerId, ownerIdentity, refreshSelection, totalLines]);
67
362
  useEffect(() => {
68
- if (!containerRef.current) return;
69
- const resizeObserver = new ResizeObserver((entries) => {
70
- for (const entry of entries) {
71
- setContainerHeight(entry.contentRect.height);
363
+ let container = containerRef.current;
364
+ if (!container || !geometryReady || !config.enabled || totalLines < config.virtualizeThreshold)
365
+ return;
366
+ let measurementGeneration = measurementGenerationRef.current, active = !0, preserveViewport = () => {
367
+ if (!active || measurementGenerationRef.current !== measurementGeneration || isPrintingRef.current || printGeometrySuspendedRef.current || restoringPrintScrollRef.current) return;
368
+ if (followTailRef.current) {
369
+ let nextScrollTop2 = Math.max(0, container.scrollHeight - container.clientHeight);
370
+ container.scrollTop !== nextScrollTop2 && (writeScrollTop(container, nextScrollTop2), setScrollTop(nextScrollTop2));
371
+ return;
72
372
  }
373
+ let anchor = viewportAnchorRef.current;
374
+ if (!anchor || anchor.ownerId !== ownerId || anchor.ownerIdentity !== ownerIdentity || anchor.generation !== generation || anchor.configurationRevision !== configurationRevision)
375
+ return;
376
+ let lineIndex = Math.min(Math.max(0, totalLines - 1), anchor.lineIndex), nextScrollTop = Math.max(0, heightIndex.offsetAt(lineIndex) - anchor.offset);
377
+ Math.abs(container.scrollTop - nextScrollTop) > 0.5 && (writeScrollTop(container, nextScrollTop), setScrollTop(nextScrollTop));
378
+ }, measureRow = (row) => {
379
+ if (isPrintingRef.current || printGeometrySuspendedRef.current || restoringPrintScrollRef.current || !row.matches(".line[data-line]") || !container.contains(row)) return !1;
380
+ let lineIndex = Number(row.getAttribute("data-line")) - 1;
381
+ if (!Number.isSafeInteger(lineIndex) || lineIndex < 0 || lineIndex >= totalLines) return !1;
382
+ let element = row, height = element.getBoundingClientRect().height || element.offsetHeight;
383
+ return height > 0 && heightIndex.setHeight(lineIndex, height);
384
+ }, observer = typeof ResizeObserver > "u" ? null : new ResizeObserver((entries) => {
385
+ if (!active || measurementGenerationRef.current !== measurementGeneration || isPrintingRef.current || printGeometrySuspendedRef.current || restoringPrintScrollRef.current) return;
386
+ let changed2 = !1;
387
+ for (let entry of entries) changed2 = measureRow(entry.target) || changed2;
388
+ changed2 && (preserveViewport(), setMeasureVersion((version) => version + 1));
73
389
  });
74
- resizeObserver.observe(containerRef.current);
75
- return () => resizeObserver.disconnect();
76
- }, []);
77
- const window = useMemo(() => {
78
- return calculateCodeWindow(lines, scrollTop, containerHeight, lineHeightRef.current, config);
79
- }, [lines, scrollTop, containerHeight, config]);
80
- const scrollToLine = useCallback((lineIndex) => {
81
- if (!containerRef.current) return;
82
- const targetScrollTop = lineIndex * lineHeightRef.current;
83
- containerRef.current.scrollTop = targetScrollTop;
84
- setScrollTop(targetScrollTop);
85
- }, []);
390
+ rowResizeObserverRef.current = observer;
391
+ let changed = !1, rows = container.querySelectorAll(".line[data-line]");
392
+ for (let row of rows)
393
+ observer?.observe(row), isPrintingRef.current && printRowsRef.current.add(row), changed = measureRow(row) || changed;
394
+ return changed && (preserveViewport(), setMeasureVersion((version) => version + 1)), () => {
395
+ rowResizeObserverRef.current === observer && (rowResizeObserverRef.current = null), observer?.disconnect(), printRowsRef.current.clear();
396
+ };
397
+ }, [
398
+ config.enabled,
399
+ config.virtualizeThreshold,
400
+ configurationRevision,
401
+ generation,
402
+ geometryReady,
403
+ heightIndex,
404
+ layout.window.endIndex,
405
+ layout.window.startIndex,
406
+ ownerIdentity,
407
+ ownerId,
408
+ totalLines,
409
+ containerWidth,
410
+ selectionLineIndices,
411
+ writeScrollTop
412
+ ]);
413
+ let scrollToLine = useCallback((lineIndex) => {
414
+ let container = containerRef.current;
415
+ if (!container || isPrintingRef.current || printGeometrySuspendedRef.current || restoringPrintScrollRef.current) return;
416
+ let targetIndex = Math.max(0, Math.min(Math.floor(lineIndex), totalLines - 1)), targetScrollTop = geometryReady ? heightIndex.offsetAt(targetIndex) : targetIndex * DEFAULT_CODE_LINE_HEIGHT;
417
+ writeScrollTop(container, targetScrollTop), setScrollTop(targetScrollTop), followTailRef.current = !1, viewportAnchorRef.current = {
418
+ lineIndex: targetIndex,
419
+ offset: 0,
420
+ ownerId,
421
+ ownerIdentity,
422
+ generation,
423
+ configurationRevision
424
+ };
425
+ }, [configurationRevision, generation, geometryReady, heightIndex, ownerId, ownerIdentity, totalLines, writeScrollTop]), lineHeight = heightIndex.offsetAt(1) - heightIndex.offsetAt(0) || DEFAULT_CODE_LINE_HEIGHT;
86
426
  return {
87
427
  containerRef,
88
- window,
428
+ isPrinting,
429
+ selectionLineIndices,
430
+ window: layout.window,
431
+ renderRanges: renderLayout.ranges,
432
+ mountedLineCount: renderLayout.mountedLines,
433
+ spacerTopHeight: renderLayout.spacerTopHeight,
434
+ spacerBottomHeight: renderLayout.spacerBottomHeight,
89
435
  handleScroll,
90
436
  scrollToLine,
91
- lineHeight: lineHeightRef.current,
437
+ lineHeight,
92
438
  config
93
439
  };
94
440
  }
95
441
  export {
96
442
  DEFAULT_VIRTUALIZED_CODE_CONFIG,
97
443
  calculateCodeWindow,
444
+ resolveVirtualizedCodeConfig,
98
445
  useVirtualizedCode
99
446
  };