@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
@@ -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 };