@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
@@ -6,114 +6,457 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
7
  var __export = (target, all) => {
8
8
  for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
9
+ __defProp(target, name, { get: all[name], enumerable: !0 });
10
+ }, __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from == "object" || typeof from == "function")
13
12
  for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
13
+ !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
14
  return to;
18
15
  };
19
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
16
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: !0 }), mod);
20
17
 
21
18
  // src/renderer/virtualized-code.tsx
22
19
  var virtualized_code_exports = {};
23
20
  __export(virtualized_code_exports, {
24
21
  DEFAULT_VIRTUALIZED_CODE_CONFIG: () => DEFAULT_VIRTUALIZED_CODE_CONFIG,
25
22
  calculateCodeWindow: () => calculateCodeWindow,
23
+ resolveVirtualizedCodeConfig: () => resolveVirtualizedCodeConfig,
26
24
  useVirtualizedCode: () => useVirtualizedCode
27
25
  });
28
26
  module.exports = __toCommonJS(virtualized_code_exports);
29
- var import_react = require("react");
27
+ var import_react = require("react"), import_react_dom = require("react-dom");
28
+
29
+ // src/renderer/viewport-height-index.ts
30
+ var ViewportHeightIndex = class {
31
+ constructor() {
32
+ this.count = 0;
33
+ this.estimate = 20;
34
+ this.measuredHeights = /* @__PURE__ */ new Map();
35
+ this.heightDeltas = /* @__PURE__ */ new Map();
36
+ }
37
+ reset(count, estimate) {
38
+ 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();
39
+ }
40
+ resize(count) {
41
+ let nextCount = Number.isFinite(count) ? Math.max(0, Math.floor(count)) : 0;
42
+ if (nextCount !== this.count) {
43
+ if (nextCount < this.count) {
44
+ for (let index = nextCount; index < this.count; index += 1)
45
+ this.measuredHeights.delete(index), this.heightDeltas.delete(index + 1);
46
+ this.count = nextCount;
47
+ return;
48
+ }
49
+ for (let index = this.count + 1; index <= nextCount; index += 1) {
50
+ let start = index - (index & -index), delta = this.prefix(index - 1) - this.prefix(start);
51
+ delta !== 0 && this.heightDeltas.set(index, delta);
52
+ }
53
+ this.count = nextCount;
54
+ }
55
+ }
56
+ setHeight(index, height) {
57
+ if (!Number.isSafeInteger(index) || index < 0 || index >= this.count || !Number.isFinite(height) || height <= 0)
58
+ return !1;
59
+ let previous = this.measuredHeights.get(index) ?? this.estimate;
60
+ return Math.abs(previous - height) <= 0.5 ? !1 : (this.measuredHeights.set(index, height), this.add(index + 1, height - previous), !0);
61
+ }
62
+ offsetAt(index) {
63
+ let end = Number.isFinite(index) ? Math.max(0, Math.min(this.count, Math.floor(index))) : 0;
64
+ return end * this.estimate + this.prefix(end);
65
+ }
66
+ indexAt(offset) {
67
+ if (this.count === 0) return 0;
68
+ let target = Number.isNaN(offset) ? 0 : Math.max(0, Math.min(offset, this.offsetAt(this.count))), index = 0, delta = 0, bit = 1;
69
+ for (; bit <= Math.floor(this.count / 2); ) bit *= 2;
70
+ for (; bit > 0; ) {
71
+ let next = index + bit;
72
+ if (next <= this.count) {
73
+ let nextDelta = delta + (this.heightDeltas.get(next) ?? 0);
74
+ next * this.estimate + nextDelta <= target && (index = next, delta = nextDelta);
75
+ }
76
+ bit = Math.floor(bit / 2);
77
+ }
78
+ return index;
79
+ }
80
+ prefix(count) {
81
+ let total = 0;
82
+ for (let index = count; index > 0; index -= index & -index) total += this.heightDeltas.get(index) ?? 0;
83
+ return total;
84
+ }
85
+ add(index, delta) {
86
+ for (let cursor = index; cursor <= this.count; cursor += cursor & -cursor) {
87
+ let next = (this.heightDeltas.get(cursor) ?? 0) + delta;
88
+ next === 0 ? this.heightDeltas.delete(cursor) : this.heightDeltas.set(cursor, next);
89
+ }
90
+ }
91
+ };
92
+
93
+ // src/renderer/virtualized-code.tsx
30
94
  var DEFAULT_VIRTUALIZED_CODE_CONFIG = {
31
- enabled: true,
95
+ enabled: !0,
32
96
  windowSize: 100,
33
- // Render 100 lines at a time
34
97
  bufferSize: 50,
35
- // +50 lines buffer above/below
36
- virtualizeThreshold: 200
37
- // Only virtualize if 200+ lines
98
+ virtualizeThreshold: 200,
99
+ followTail: !1
38
100
  };
39
- function calculateCodeWindow(lines, scrollTop, containerHeight, lineHeight, config) {
40
- const totalLines = lines.length;
41
- if (!config.enabled || totalLines < config.virtualizeThreshold) {
101
+ function resolveVirtualizedCodeConfig(options) {
102
+ return {
103
+ ...DEFAULT_VIRTUALIZED_CODE_CONFIG,
104
+ ...options,
105
+ followTail: options?.followTail ?? DEFAULT_VIRTUALIZED_CODE_CONFIG.followTail
106
+ };
107
+ }
108
+ 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 = {};
109
+ function calculateCodeWindow(totalLines, scrollTop, containerHeight, lineHeight, config) {
110
+ let lineCount = Math.max(0, totalLines);
111
+ if (!config.enabled || lineCount < config.virtualizeThreshold)
42
112
  return {
43
113
  startIndex: 0,
44
- endIndex: totalLines,
114
+ endIndex: lineCount,
45
115
  visibleStart: 0,
46
- visibleEnd: totalLines,
47
- visibleLines: lines,
48
- totalLines,
49
- mountedLines: totalLines
116
+ visibleEnd: lineCount,
117
+ totalLines: lineCount,
118
+ mountedLines: lineCount
50
119
  };
51
- }
52
- const firstVisibleLine = Math.floor(scrollTop / lineHeight);
53
- const lastVisibleLine = Math.ceil((scrollTop + containerHeight) / lineHeight);
54
- const visibleStart = Math.max(0, firstVisibleLine);
55
- const visibleEnd = Math.min(totalLines, Math.max(visibleStart, lastVisibleLine));
56
- const startIndex = Math.max(0, visibleStart - config.bufferSize);
57
- const endIndex = Math.min(totalLines, visibleEnd + config.bufferSize);
120
+ 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);
58
121
  return {
59
122
  startIndex,
60
123
  endIndex,
61
124
  visibleStart,
62
125
  visibleEnd,
63
- visibleLines: lines.slice(startIndex, endIndex),
64
- totalLines,
126
+ totalLines: lineCount,
65
127
  mountedLines: endIndex - startIndex
66
128
  };
67
129
  }
68
- function useVirtualizedCode(lines, config = DEFAULT_VIRTUALIZED_CODE_CONFIG) {
69
- const containerRef = (0, import_react.useRef)(null);
70
- const [scrollTop, setScrollTop] = (0, import_react.useState)(0);
71
- const [containerHeight, setContainerHeight] = (0, import_react.useState)(0);
72
- const lineHeightRef = (0, import_react.useRef)(20);
130
+ var EMPTY_SELECTION_LINE_INDICES = [];
131
+ function calculateMeasuredCodeWindow(totalLines, scrollTop, containerHeight, heights, config) {
132
+ let lineCount = Math.max(0, totalLines);
133
+ if (!config.enabled || lineCount < config.virtualizeThreshold)
134
+ return {
135
+ window: calculateCodeWindow(lineCount, scrollTop, containerHeight, DEFAULT_CODE_LINE_HEIGHT, config),
136
+ spacerTopHeight: 0,
137
+ spacerBottomHeight: 0
138
+ };
139
+ let totalHeight = heights.offsetAt(lineCount), viewportHeight = containerHeight > 0 ? containerHeight : Math.max(1, config.windowSize) * DEFAULT_CODE_LINE_HEIGHT, top = Math.min(
140
+ Math.max(0, scrollTop),
141
+ Math.max(0, totalHeight - viewportHeight)
142
+ ), bottom = top + viewportHeight, visibleStart = Math.min(lineCount, heights.indexAt(top)), visibleEnd = heights.indexAt(bottom);
143
+ visibleEnd < lineCount && heights.offsetAt(visibleEnd) < bottom && (visibleEnd += 1), visibleEnd = Math.min(lineCount, Math.max(visibleStart, visibleEnd));
144
+ let bufferSize = Math.max(0, config.bufferSize), startIndex = Math.max(0, visibleStart - bufferSize), endIndex = Math.min(lineCount, visibleEnd + bufferSize);
145
+ return {
146
+ window: {
147
+ startIndex,
148
+ endIndex,
149
+ visibleStart,
150
+ visibleEnd,
151
+ totalLines: lineCount,
152
+ mountedLines: endIndex - startIndex
153
+ },
154
+ spacerTopHeight: heights.offsetAt(startIndex),
155
+ spacerBottomHeight: Math.max(0, totalHeight - heights.offsetAt(endIndex))
156
+ };
157
+ }
158
+ function useVirtualizedCode(totalLines, generation, config = DEFAULT_VIRTUALIZED_CODE_CONFIG, ownerId = "", configurationRevision = 0, ownerIdentity = DEFAULT_CODE_VIEWPORT_OWNER) {
159
+ let containerRef = (0, import_react.useRef)(null), [scrollTop, setScrollTop] = (0, import_react.useState)(0), [containerHeight, setContainerHeight] = (0, import_react.useState)(0), [containerWidth, setContainerWidth] = (0, import_react.useState)(0), [measureVersion, setMeasureVersion] = (0, import_react.useState)(0), [selectionState, setSelectionState] = (0, import_react.useState)(() => ({
160
+ ownerId,
161
+ generation,
162
+ lineIndices: EMPTY_SELECTION_LINE_INDICES
163
+ })), heightIndexRef = (0, import_react.useRef)(null), heightIndex = heightIndexRef.current;
164
+ heightIndex === null && (heightIndex = new ViewportHeightIndex(), heightIndex.reset(totalLines, DEFAULT_CODE_LINE_HEIGHT), heightIndexRef.current = heightIndex);
165
+ let indexedCountRef = (0, import_react.useRef)(totalLines), totalLinesRef = (0, import_react.useRef)(totalLines);
166
+ totalLinesRef.current = totalLines;
167
+ let measuredWidthRef = (0, import_react.useRef)(null), measurementGenerationRef = (0, import_react.useRef)(0), previousScrollTopRef = (0, import_react.useRef)(null), followTailRef = (0, import_react.useRef)(!1), followTailOptInRef = (0, import_react.useRef)(config.followTail === !0);
168
+ followTailOptInRef.current = config.followTail === !0, followTailOptInRef.current || (followTailRef.current = !1);
169
+ let writeScrollTop = (0, import_react.useCallback)((container, nextTop) => {
170
+ container.scrollTop = nextTop, previousScrollTopRef.current = container.scrollTop;
171
+ }, []), viewportAnchorRef = (0, import_react.useRef)(null), geometryIdentityRef = (0, import_react.useRef)(null), printOwnerGenerationRef = (0, import_react.useRef)(0), [printOwner, setPrintOwner] = (0, import_react.useState)(null), isPrintingRef = (0, import_react.useRef)(!1), printGeometrySuspendedRef = (0, import_react.useRef)(!1), restoringPrintScrollRef = (0, import_react.useRef)(!1), printSnapshotRef = (0, import_react.useRef)(null), rowResizeObserverRef = (0, import_react.useRef)(null), printRowsRef = (0, import_react.useRef)(/* @__PURE__ */ new Set()), printEligible = config.enabled && totalLines >= config.virtualizeThreshold, isPrinting = printOwner?.identity === ownerIdentity && printOwner.generation === printOwnerGenerationRef.current, previousIdentity = geometryIdentityRef.current;
172
+ previousIdentity === null && (previousIdentity = {
173
+ ownerId,
174
+ ownerIdentity,
175
+ generation,
176
+ configurationRevision,
177
+ enabled: config.enabled,
178
+ windowSize: config.windowSize,
179
+ bufferSize: config.bufferSize,
180
+ virtualizeThreshold: config.virtualizeThreshold
181
+ }, geometryIdentityRef.current = previousIdentity);
182
+ 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 = (0, import_react.useCallback)(() => {
183
+ let container = containerRef.current;
184
+ if (!container || !selectionTrackingEnabled) return;
185
+ let selection = container.ownerDocument.getSelection(), nextLineIndices = [], retainEndpoint = (node) => {
186
+ if (!node) return;
187
+ let row = (node.nodeType === 1 ? node : node.parentElement)?.closest(".line[data-line]");
188
+ if (!row || !container.contains(row)) return;
189
+ let lineNumber = Number(row.getAttribute("data-line")), lineIndex = lineNumber - 1;
190
+ !Number.isSafeInteger(lineNumber) || lineNumber <= 0 || lineIndex >= totalLinesRef.current || nextLineIndices.includes(lineIndex) || nextLineIndices.push(lineIndex);
191
+ };
192
+ 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 });
193
+ }, [generation, ownerId, selectionTrackingEnabled]), layout = (0, import_react.useMemo)(() => {
194
+ if (geometryReady)
195
+ return calculateMeasuredCodeWindow(totalLines, scrollTop, containerHeight, heightIndex, config);
196
+ let window = calculateCodeWindow(
197
+ totalLines,
198
+ scrollTop,
199
+ containerHeight,
200
+ DEFAULT_CODE_LINE_HEIGHT,
201
+ config
202
+ ), virtualized = config.enabled && totalLines >= config.virtualizeThreshold;
203
+ return {
204
+ window,
205
+ spacerTopHeight: virtualized ? window.startIndex * DEFAULT_CODE_LINE_HEIGHT : 0,
206
+ spacerBottomHeight: virtualized ? (window.totalLines - window.endIndex) * DEFAULT_CODE_LINE_HEIGHT : 0
207
+ };
208
+ }, [config, containerHeight, geometryReady, heightIndex, measureVersion, scrollTop, totalLines]), renderLayout = (0, import_react.useMemo)(() => {
209
+ let intervals = [], primary = layout.window;
210
+ primary.endIndex > primary.startIndex && intervals.push({
211
+ startIndex: primary.startIndex,
212
+ endIndex: primary.endIndex,
213
+ visibleStart: primary.visibleStart,
214
+ visibleEnd: primary.visibleEnd
215
+ });
216
+ for (let lineIndex of selectionLineIndices) {
217
+ if (!Number.isSafeInteger(lineIndex) || lineIndex < 0 || lineIndex >= totalLines || lineIndex >= primary.startIndex && lineIndex < primary.endIndex)
218
+ continue;
219
+ let previous = intervals[intervals.length - 1];
220
+ previous && (previous.startIndex !== primary.startIndex || previous.endIndex !== primary.endIndex) && previous.endIndex === lineIndex ? (previous.endIndex += 1, previous.visibleEnd = lineIndex + 1) : intervals.push({
221
+ startIndex: lineIndex,
222
+ endIndex: lineIndex + 1,
223
+ visibleStart: lineIndex,
224
+ visibleEnd: lineIndex + 1
225
+ });
226
+ }
227
+ intervals.sort((left, right) => left.startIndex - right.startIndex);
228
+ let offsetAt = geometryReady ? (index) => heightIndex.offsetAt(index) : (index) => index * DEFAULT_CODE_LINE_HEIGHT, previousEnd = 0, mountedLines = 0, ranges = intervals.map((interval, index) => {
229
+ let spacerBeforeHeight = index === 0 ? 0 : Math.max(0, offsetAt(interval.startIndex) - offsetAt(previousEnd));
230
+ return previousEnd = interval.endIndex, mountedLines += interval.endIndex - interval.startIndex, { ...interval, spacerBeforeHeight };
231
+ });
232
+ return {
233
+ ranges,
234
+ spacerTopHeight: ranges.length > 0 ? offsetAt(ranges[0].startIndex) : 0,
235
+ spacerBottomHeight: Math.max(0, offsetAt(totalLines) - offsetAt(previousEnd)),
236
+ mountedLines
237
+ };
238
+ }, [geometryReady, heightIndex, layout.window, selectionLineIndices, totalLines]);
73
239
  (0, import_react.useEffect)(() => {
74
- if (!containerRef.current || lines.length === 0) return;
75
- const timeoutId = setTimeout(() => {
76
- if (!containerRef.current) return;
77
- const firstLine = containerRef.current.querySelector(".line");
78
- if (firstLine) {
79
- const measuredHeight = firstLine.offsetHeight || 20;
80
- if (measuredHeight !== lineHeightRef.current) {
81
- lineHeightRef.current = measuredHeight;
82
- }
240
+ let container = containerRef.current;
241
+ if (!container || !selectionTrackingEnabled) return;
242
+ let ownerDocument = container.ownerDocument;
243
+ return ownerDocument.addEventListener("selectionchange", refreshSelection), refreshSelection(), () => ownerDocument.removeEventListener("selectionchange", refreshSelection);
244
+ }, [refreshSelection, selectionTrackingEnabled]), (0, import_react.useEffect)(() => {
245
+ let container = containerRef.current;
246
+ if (!container || config.followTail !== !0) return;
247
+ let scrollTop2 = container.scrollTop, distanceToBottom = container.scrollHeight - container.clientHeight - scrollTop2;
248
+ followTailRef.current = distanceToBottom <= CODE_FOLLOW_TAIL_EPS_BOTTOM_PX, previousScrollTopRef.current = scrollTop2;
249
+ }, [config.followTail]), (0, import_react.useEffect)(() => {
250
+ if (!printEligible) return;
251
+ let container = containerRef.current;
252
+ if (!container) return;
253
+ let ownerWindow = container.ownerDocument.defaultView;
254
+ if (!ownerWindow) return;
255
+ let owner = ++printOwnerGenerationRef.current, active = !0, isCurrentOwner = () => active && printOwnerGenerationRef.current === owner && containerRef.current === container, onBeforePrint = () => {
256
+ if (!isCurrentOwner() || !container.isConnected || isPrintingRef.current) return;
257
+ isPrintingRef.current = !0, printGeometrySuspendedRef.current = !0, printSnapshotRef.current = {
258
+ identity: ownerIdentity,
259
+ owner,
260
+ scrollTop: container.scrollTop,
261
+ followTail: followTailRef.current,
262
+ anchor: viewportAnchorRef.current
263
+ }, (0, import_react_dom.flushSync)(() => {
264
+ setPrintOwner({ identity: ownerIdentity, generation: owner });
265
+ });
266
+ let observer = rowResizeObserverRef.current;
267
+ if (observer)
268
+ for (let row of container.querySelectorAll(".line[data-line]"))
269
+ observer.observe(row), printRowsRef.current.add(row);
270
+ }, onAfterPrint = () => {
271
+ if (!isCurrentOwner() || !isPrintingRef.current) return;
272
+ let snapshot = printSnapshotRef.current;
273
+ if (!snapshot || snapshot.owner !== owner) return;
274
+ let restoreReader = snapshot.identity === ownerIdentity && container.isConnected;
275
+ restoreReader && (restoringPrintScrollRef.current = !0), isPrintingRef.current = !1, printSnapshotRef.current = null, (0, import_react_dom.flushSync)(() => {
276
+ setPrintOwner((current) => current?.generation === owner ? null : current);
277
+ }), restoreReader && (followTailRef.current = snapshot.followTail && followTailOptInRef.current, viewportAnchorRef.current = snapshot.anchor, (0, import_react_dom.flushSync)(() => setScrollTop(container.scrollTop)));
278
+ let observer = rowResizeObserverRef.current;
279
+ for (let row of printRowsRef.current)
280
+ container.contains(row) || observer?.unobserve(row);
281
+ printRowsRef.current.clear(), ownerWindow.requestAnimationFrame(() => {
282
+ !isCurrentOwner() || isPrintingRef.current || (restoreReader && (restoringPrintScrollRef.current = !1), printGeometrySuspendedRef.current = !1);
283
+ });
284
+ };
285
+ return ownerWindow.addEventListener("beforeprint", onBeforePrint), ownerWindow.addEventListener("afterprint", onAfterPrint), () => {
286
+ if (active = !1, ownerWindow.removeEventListener("beforeprint", onBeforePrint), ownerWindow.removeEventListener("afterprint", onAfterPrint), printOwnerGenerationRef.current !== owner) return;
287
+ printOwnerGenerationRef.current++, restoringPrintScrollRef.current = !1, printGeometrySuspendedRef.current = !1, isPrintingRef.current = !1, printSnapshotRef.current = null;
288
+ let observer = rowResizeObserverRef.current;
289
+ for (let row of printRowsRef.current)
290
+ container.contains(row) || observer?.unobserve(row);
291
+ printRowsRef.current.clear();
292
+ };
293
+ }, [ownerIdentity, printEligible]), (0, import_react.useEffect)(() => {
294
+ let previous = geometryIdentityRef.current;
295
+ if (!previous) return;
296
+ 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;
297
+ if (ownerChanged || sourceConfigurationChanged)
298
+ heightIndex.reset(totalLines, DEFAULT_CODE_LINE_HEIGHT), indexedCountRef.current = totalLines, viewportAnchorRef.current = null;
299
+ else if (lineCountChanged) {
300
+ let previousCount = indexedCountRef.current;
301
+ if (heightIndex.resize(totalLines), indexedCountRef.current = totalLines, totalLines < previousCount) {
302
+ let anchor = viewportAnchorRef.current;
303
+ anchor && anchor.lineIndex >= totalLines && (viewportAnchorRef.current = null);
83
304
  }
84
- }, 0);
85
- return () => clearTimeout(timeoutId);
86
- }, [lines.length]);
87
- const handleScroll = (0, import_react.useCallback)((e) => {
88
- const target = e.currentTarget;
89
- setScrollTop(target.scrollTop);
90
- setContainerHeight(target.clientHeight);
91
- }, []);
305
+ let container = containerRef.current;
306
+ if (totalLines > previousCount && container && followTailRef.current && !isPrintingRef.current && !printGeometrySuspendedRef.current && !restoringPrintScrollRef.current) {
307
+ let nextScrollTop = Math.max(0, container.scrollHeight - container.clientHeight);
308
+ container.scrollTop !== nextScrollTop && (writeScrollTop(container, nextScrollTop), setScrollTop(nextScrollTop));
309
+ }
310
+ }
311
+ if (ownerChanged) {
312
+ followTailRef.current = !1;
313
+ let container = containerRef.current;
314
+ container && (container.scrollTop !== 0 ? writeScrollTop(container, 0) : previousScrollTopRef.current = 0), setScrollTop(0);
315
+ }
316
+ (ownerChanged || sourceConfigurationChanged || presentationOptionsChanged || lineCountChanged) && ((ownerChanged || sourceConfigurationChanged || presentationOptionsChanged) && (measurementGenerationRef.current += 1), setMeasureVersion((version) => version + 1)), geometryIdentityRef.current = {
317
+ ownerId,
318
+ ownerIdentity,
319
+ generation,
320
+ configurationRevision,
321
+ enabled: config.enabled,
322
+ windowSize: config.windowSize,
323
+ bufferSize: config.bufferSize,
324
+ virtualizeThreshold: config.virtualizeThreshold
325
+ };
326
+ }, [
327
+ config.bufferSize,
328
+ config.enabled,
329
+ config.virtualizeThreshold,
330
+ config.windowSize,
331
+ configurationRevision,
332
+ generation,
333
+ heightIndex,
334
+ ownerId,
335
+ ownerIdentity,
336
+ totalLines,
337
+ writeScrollTop
338
+ ]), (0, import_react.useEffect)(() => {
339
+ let container = containerRef.current;
340
+ if (!container || typeof ResizeObserver > "u") return;
341
+ let resizeObserver = new ResizeObserver((entries) => {
342
+ if (!(isPrintingRef.current || printGeometrySuspendedRef.current || restoringPrintScrollRef.current))
343
+ for (let entry of entries) {
344
+ if (entry.target !== container) continue;
345
+ let nextHeight = Math.ceil(entry.contentRect.height || container.clientHeight);
346
+ setContainerHeight((current) => current === nextHeight ? current : nextHeight);
347
+ let nextWidth = Math.ceil(container.clientWidth || entry.contentRect.width), previousWidth = measuredWidthRef.current;
348
+ if (previousWidth === null)
349
+ measuredWidthRef.current = nextWidth;
350
+ else if (previousWidth !== nextWidth) {
351
+ measuredWidthRef.current = nextWidth;
352
+ let lineCount = totalLinesRef.current;
353
+ heightIndex.reset(lineCount, DEFAULT_CODE_LINE_HEIGHT), indexedCountRef.current = lineCount, measurementGenerationRef.current += 1, viewportAnchorRef.current = null, setMeasureVersion((version) => version + 1);
354
+ }
355
+ setContainerWidth((current) => current === nextWidth ? current : nextWidth);
356
+ }
357
+ });
358
+ return resizeObserver.observe(container), () => resizeObserver.disconnect();
359
+ }, [heightIndex]);
360
+ let handleScroll = (0, import_react.useCallback)((event) => {
361
+ let target = event.currentTarget;
362
+ if (target !== containerRef.current || isPrintingRef.current || restoringPrintScrollRef.current) return;
363
+ refreshSelection();
364
+ let nextScrollTop = target.scrollTop, previousScrollTop = previousScrollTopRef.current, scrolledUp = previousScrollTop !== null && previousScrollTop - nextScrollTop > CODE_FOLLOW_TAIL_DETACH_SCROLL_DELTA_PX;
365
+ previousScrollTopRef.current = nextScrollTop, setScrollTop(nextScrollTop), setContainerHeight(target.clientHeight);
366
+ let atBottom = target.scrollHeight - target.clientHeight - nextScrollTop <= CODE_FOLLOW_TAIL_EPS_BOTTOM_PX;
367
+ if (config.followTail !== !0 || scrolledUp ? followTailRef.current = !1 : atBottom && (followTailRef.current = !0), atBottom && !scrolledUp) {
368
+ viewportAnchorRef.current = null;
369
+ return;
370
+ }
371
+ let lineIndex = Math.min(
372
+ Math.max(0, totalLines - 1),
373
+ geometryReady ? heightIndex.indexAt(nextScrollTop) : Math.floor(Math.max(0, nextScrollTop) / DEFAULT_CODE_LINE_HEIGHT)
374
+ ), lineTop = geometryReady ? heightIndex.offsetAt(lineIndex) : lineIndex * DEFAULT_CODE_LINE_HEIGHT;
375
+ viewportAnchorRef.current = {
376
+ lineIndex,
377
+ offset: lineTop - nextScrollTop,
378
+ ownerId,
379
+ ownerIdentity,
380
+ generation,
381
+ configurationRevision
382
+ };
383
+ }, [config.followTail, configurationRevision, generation, geometryReady, heightIndex, ownerId, ownerIdentity, refreshSelection, totalLines]);
92
384
  (0, import_react.useEffect)(() => {
93
- if (!containerRef.current) return;
94
- const resizeObserver = new ResizeObserver((entries) => {
95
- for (const entry of entries) {
96
- setContainerHeight(entry.contentRect.height);
385
+ let container = containerRef.current;
386
+ if (!container || !geometryReady || !config.enabled || totalLines < config.virtualizeThreshold)
387
+ return;
388
+ let measurementGeneration = measurementGenerationRef.current, active = !0, preserveViewport = () => {
389
+ if (!active || measurementGenerationRef.current !== measurementGeneration || isPrintingRef.current || printGeometrySuspendedRef.current || restoringPrintScrollRef.current) return;
390
+ if (followTailRef.current) {
391
+ let nextScrollTop2 = Math.max(0, container.scrollHeight - container.clientHeight);
392
+ container.scrollTop !== nextScrollTop2 && (writeScrollTop(container, nextScrollTop2), setScrollTop(nextScrollTop2));
393
+ return;
97
394
  }
395
+ let anchor = viewportAnchorRef.current;
396
+ if (!anchor || anchor.ownerId !== ownerId || anchor.ownerIdentity !== ownerIdentity || anchor.generation !== generation || anchor.configurationRevision !== configurationRevision)
397
+ return;
398
+ let lineIndex = Math.min(Math.max(0, totalLines - 1), anchor.lineIndex), nextScrollTop = Math.max(0, heightIndex.offsetAt(lineIndex) - anchor.offset);
399
+ Math.abs(container.scrollTop - nextScrollTop) > 0.5 && (writeScrollTop(container, nextScrollTop), setScrollTop(nextScrollTop));
400
+ }, measureRow = (row) => {
401
+ if (isPrintingRef.current || printGeometrySuspendedRef.current || restoringPrintScrollRef.current || !row.matches(".line[data-line]") || !container.contains(row)) return !1;
402
+ let lineIndex = Number(row.getAttribute("data-line")) - 1;
403
+ if (!Number.isSafeInteger(lineIndex) || lineIndex < 0 || lineIndex >= totalLines) return !1;
404
+ let element = row, height = element.getBoundingClientRect().height || element.offsetHeight;
405
+ return height > 0 && heightIndex.setHeight(lineIndex, height);
406
+ }, observer = typeof ResizeObserver > "u" ? null : new ResizeObserver((entries) => {
407
+ if (!active || measurementGenerationRef.current !== measurementGeneration || isPrintingRef.current || printGeometrySuspendedRef.current || restoringPrintScrollRef.current) return;
408
+ let changed2 = !1;
409
+ for (let entry of entries) changed2 = measureRow(entry.target) || changed2;
410
+ changed2 && (preserveViewport(), setMeasureVersion((version) => version + 1));
98
411
  });
99
- resizeObserver.observe(containerRef.current);
100
- return () => resizeObserver.disconnect();
101
- }, []);
102
- const window = (0, import_react.useMemo)(() => {
103
- return calculateCodeWindow(lines, scrollTop, containerHeight, lineHeightRef.current, config);
104
- }, [lines, scrollTop, containerHeight, config]);
105
- const scrollToLine = (0, import_react.useCallback)((lineIndex) => {
106
- if (!containerRef.current) return;
107
- const targetScrollTop = lineIndex * lineHeightRef.current;
108
- containerRef.current.scrollTop = targetScrollTop;
109
- setScrollTop(targetScrollTop);
110
- }, []);
412
+ rowResizeObserverRef.current = observer;
413
+ let changed = !1, rows = container.querySelectorAll(".line[data-line]");
414
+ for (let row of rows)
415
+ observer?.observe(row), isPrintingRef.current && printRowsRef.current.add(row), changed = measureRow(row) || changed;
416
+ return changed && (preserveViewport(), setMeasureVersion((version) => version + 1)), () => {
417
+ rowResizeObserverRef.current === observer && (rowResizeObserverRef.current = null), observer?.disconnect(), printRowsRef.current.clear();
418
+ };
419
+ }, [
420
+ config.enabled,
421
+ config.virtualizeThreshold,
422
+ configurationRevision,
423
+ generation,
424
+ geometryReady,
425
+ heightIndex,
426
+ layout.window.endIndex,
427
+ layout.window.startIndex,
428
+ ownerIdentity,
429
+ ownerId,
430
+ totalLines,
431
+ containerWidth,
432
+ selectionLineIndices,
433
+ writeScrollTop
434
+ ]);
435
+ let scrollToLine = (0, import_react.useCallback)((lineIndex) => {
436
+ let container = containerRef.current;
437
+ if (!container || isPrintingRef.current || printGeometrySuspendedRef.current || restoringPrintScrollRef.current) return;
438
+ let targetIndex = Math.max(0, Math.min(Math.floor(lineIndex), totalLines - 1)), targetScrollTop = geometryReady ? heightIndex.offsetAt(targetIndex) : targetIndex * DEFAULT_CODE_LINE_HEIGHT;
439
+ writeScrollTop(container, targetScrollTop), setScrollTop(targetScrollTop), followTailRef.current = !1, viewportAnchorRef.current = {
440
+ lineIndex: targetIndex,
441
+ offset: 0,
442
+ ownerId,
443
+ ownerIdentity,
444
+ generation,
445
+ configurationRevision
446
+ };
447
+ }, [configurationRevision, generation, geometryReady, heightIndex, ownerId, ownerIdentity, totalLines, writeScrollTop]), lineHeight = heightIndex.offsetAt(1) - heightIndex.offsetAt(0) || DEFAULT_CODE_LINE_HEIGHT;
111
448
  return {
112
449
  containerRef,
113
- window,
450
+ isPrinting,
451
+ selectionLineIndices,
452
+ window: layout.window,
453
+ renderRanges: renderLayout.ranges,
454
+ mountedLineCount: renderLayout.mountedLines,
455
+ spacerTopHeight: renderLayout.spacerTopHeight,
456
+ spacerBottomHeight: renderLayout.spacerBottomHeight,
114
457
  handleScroll,
115
458
  scrollToLine,
116
- lineHeight: lineHeightRef.current,
459
+ lineHeight,
117
460
  config
118
461
  };
119
462
  }
@@ -121,5 +464,6 @@ function useVirtualizedCode(lines, config = DEFAULT_VIRTUALIZED_CODE_CONFIG) {
121
464
  0 && (module.exports = {
122
465
  DEFAULT_VIRTUALIZED_CODE_CONFIG,
123
466
  calculateCodeWindow,
467
+ resolveVirtualizedCodeConfig,
124
468
  useVirtualizedCode
125
469
  });
@@ -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 };