@stream-mdx/react 0.5.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 (99) hide show
  1. package/CHANGELOG.md +371 -0
  2. package/README.md +79 -4
  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 +117 -193
  6. package/dist/components/bottom-stick-scroll-area.mjs +112 -183
  7. package/dist/components/diff-renderer.cjs +86 -0
  8. package/dist/components/diff-renderer.d.cts +11 -0
  9. package/dist/components/diff-renderer.d.ts +11 -0
  10. package/dist/components/diff-renderer.mjs +66 -0
  11. package/dist/components/index.cjs +2761 -1508
  12. package/dist/components/index.d.cts +7 -2
  13. package/dist/components/index.d.ts +7 -2
  14. package/dist/components/index.mjs +2730 -1493
  15. package/dist/components/katex-math.cjs +220 -0
  16. package/dist/components/katex-math.d.cts +10 -0
  17. package/dist/components/katex-math.d.ts +10 -0
  18. package/dist/components/katex-math.mjs +188 -0
  19. package/dist/contexts/math-tracker.cjs +240 -521
  20. package/dist/contexts/math-tracker.mjs +235 -513
  21. package/dist/index-Cmne1yUK.d.cts +723 -0
  22. package/dist/index-D7dyyu3q.d.ts +723 -0
  23. package/dist/index.cjs +13029 -6475
  24. package/dist/index.d.cts +69 -39
  25. package/dist/index.d.ts +69 -39
  26. package/dist/index.mjs +13007 -6442
  27. package/dist/math/display-wrapper.cjs +7 -15
  28. package/dist/math/display-wrapper.d.cts +2 -2
  29. package/dist/math/display-wrapper.d.ts +2 -2
  30. package/dist/math/display-wrapper.mjs +2 -6
  31. package/dist/node-views-Bpc6Gkgl.d.cts +13 -0
  32. package/dist/node-views-DpnYdaAS.d.ts +13 -0
  33. package/dist/renderer/hooks.cjs +35 -20
  34. package/dist/renderer/hooks.d.cts +22 -1
  35. package/dist/renderer/hooks.d.ts +22 -1
  36. package/dist/renderer/hooks.mjs +27 -12
  37. package/dist/renderer/patch-batching.cjs +5 -8
  38. package/dist/renderer/patch-coalescing.cjs +5 -8
  39. package/dist/renderer/patch-commit-scheduler.cjs +138 -356
  40. package/dist/renderer/patch-commit-scheduler.d.cts +13 -1
  41. package/dist/renderer/patch-commit-scheduler.d.ts +13 -1
  42. package/dist/renderer/patch-commit-scheduler.mjs +133 -348
  43. package/dist/renderer/store.cjs +3775 -1373
  44. package/dist/renderer/store.d.cts +108 -3
  45. package/dist/renderer/store.d.ts +108 -3
  46. package/dist/renderer/store.mjs +3778 -1362
  47. package/dist/renderer/virtualized-code.cjs +415 -71
  48. package/dist/renderer/virtualized-code.d.cts +24 -23
  49. package/dist/renderer/virtualized-code.d.ts +24 -23
  50. package/dist/renderer/virtualized-code.mjs +409 -62
  51. package/dist/renderer.cjs +10655 -4334
  52. package/dist/renderer.d.cts +98 -29
  53. package/dist/renderer.d.ts +98 -29
  54. package/dist/renderer.mjs +10582 -4212
  55. package/dist/server-regions-CPLHGli0.d.cts +32 -0
  56. package/dist/server-regions-mUhyoKi7.d.ts +32 -0
  57. package/dist/server.cjs +10195 -3872
  58. package/dist/server.d.cts +17 -252
  59. package/dist/server.d.ts +17 -252
  60. package/dist/server.mjs +9997 -3649
  61. package/dist/{streaming-markdown-CEI9eDZm.d.ts → streaming-markdown-yELbJxyH.d.ts} +49 -55
  62. package/dist/{streaming-markdown-DRisClXb.d.cts → streaming-markdown-zVwFuaJG.d.cts} +49 -55
  63. package/dist/streaming-markdown.cjs +11660 -5378
  64. package/dist/streaming-markdown.d.cts +7 -2
  65. package/dist/streaming-markdown.d.ts +7 -2
  66. package/dist/streaming-markdown.mjs +11679 -5347
  67. package/dist/trusted-mdx/index.cjs +833 -0
  68. package/dist/trusted-mdx/index.d.cts +11 -0
  69. package/dist/trusted-mdx/index.d.ts +11 -0
  70. package/dist/trusted-mdx/index.mjs +807 -0
  71. package/dist/trusted-mdx/native-children-slot.cjs +45 -0
  72. package/dist/trusted-mdx/native-children-slot.d.cts +11 -0
  73. package/dist/trusted-mdx/native-children-slot.d.ts +11 -0
  74. package/dist/trusted-mdx/native-children-slot.mjs +24 -0
  75. package/dist/trusted-mdx/server.cjs +752 -0
  76. package/dist/trusted-mdx/server.d.cts +24 -0
  77. package/dist/trusted-mdx/server.d.ts +24 -0
  78. package/dist/trusted-mdx/server.mjs +724 -0
  79. package/dist/trusted-mdx-contract-ElKpkDI5.d.cts +113 -0
  80. package/dist/trusted-mdx-contract-ElKpkDI5.d.ts +113 -0
  81. package/dist/utils/inline-html.cjs +246 -147
  82. package/dist/utils/inline-html.d.cts +37 -6
  83. package/dist/utils/inline-html.d.ts +37 -6
  84. package/dist/utils/inline-html.mjs +237 -134
  85. package/package.json +41 -18
  86. package/dist/index-BVhp_pcF.d.ts +0 -251
  87. package/dist/index-EaeouYp2.d.cts +0 -251
  88. package/dist/mdx-client.cjs +0 -408
  89. package/dist/mdx-client.d.cts +0 -113
  90. package/dist/mdx-client.d.ts +0 -113
  91. package/dist/mdx-client.mjs +0 -369
  92. package/dist/mdx-coordinator.cjs +0 -584
  93. package/dist/mdx-coordinator.d.cts +0 -21
  94. package/dist/mdx-coordinator.d.ts +0 -21
  95. package/dist/mdx-coordinator.mjs +0 -550
  96. package/dist/renderer/node-views.cjs +0 -1887
  97. package/dist/renderer/node-views.d.cts +0 -14
  98. package/dist/renderer/node-views.d.ts +0 -14
  99. package/dist/renderer/node-views.mjs +0 -1861
@@ -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
  };