@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
@@ -4,17 +4,9 @@
4
4
  // src/components/bottom-stick-scroll-area.tsx
5
5
  import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
- var EPS_BOTTOM_PX = 2;
8
- var EPS_OVERFLOW_PX = 1;
9
- var DETACH_SCROLL_DELTA_PX = 1;
10
- var RETURN_MIN_MS = 180;
11
- var RETURN_MAX_MS = 520;
12
- var RETURN_PX_PER_MS = 2.2;
13
- var RETURN_SETTLE_FRAMES = 2;
7
+ var EPS_BOTTOM_PX = 2, EPS_OVERFLOW_PX = 1, DETACH_SCROLL_DELTA_PX = 1, RETURN_MIN_MS = 180, RETURN_MAX_MS = 520, RETURN_PX_PER_MS = 2.2, RETURN_SETTLE_FRAMES = 2;
14
8
  function clamp01(value) {
15
- if (value < 0) return 0;
16
- if (value > 1) return 1;
17
- return value;
9
+ return value < 0 ? 0 : value > 1 ? 1 : value;
18
10
  }
19
11
  function easeOutCubic(value) {
20
12
  return 1 - (1 - value) ** 3;
@@ -23,14 +15,34 @@ function joinClasses(...parts) {
23
15
  return parts.filter(Boolean).join(" ");
24
16
  }
25
17
  function measure(viewport) {
26
- const scrollTop = viewport.scrollTop;
27
- const scrollHeight = viewport.scrollHeight;
28
- const clientHeight = viewport.clientHeight;
29
- const maxScrollTop = Math.max(0, scrollHeight - clientHeight);
30
- const distanceToBottom = Math.max(0, maxScrollTop - scrollTop);
31
- const isOverflowing = scrollHeight > clientHeight + EPS_OVERFLOW_PX;
18
+ let scrollTop = viewport.scrollTop, scrollHeight = viewport.scrollHeight, clientHeight = viewport.clientHeight, maxScrollTop = Math.max(0, scrollHeight - clientHeight), distanceToBottom = Math.max(0, maxScrollTop - scrollTop), isOverflowing = scrollHeight > clientHeight + EPS_OVERFLOW_PX;
32
19
  return { scrollTop, scrollHeight, clientHeight, maxScrollTop, distanceToBottom, isOverflowing };
33
20
  }
21
+ function isEditableTarget(target) {
22
+ if (!(target instanceof Element)) return !1;
23
+ if (target.closest("input, textarea, select, [role='textbox']")) return !0;
24
+ let contentEditable = target.closest("[contenteditable]");
25
+ return contentEditable !== null && contentEditable.getAttribute("contenteditable")?.toLowerCase() !== "false";
26
+ }
27
+ function isScrollKeyIntent(event) {
28
+ if (event.defaultPrevented || isEditableTarget(event.target)) return !1;
29
+ switch (event.key) {
30
+ case "ArrowDown":
31
+ case "ArrowUp":
32
+ case "PageDown":
33
+ case "PageUp":
34
+ case "Home":
35
+ case "End":
36
+ return !0;
37
+ case " ":
38
+ case "Spacebar":
39
+ return event.target instanceof Element ? event.target.closest(
40
+ "button, input, summary, [role='button'], [role='checkbox'], [role='switch'], [role='radio'], [role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']"
41
+ ) === null : !0;
42
+ default:
43
+ return !1;
44
+ }
45
+ }
34
46
  function DefaultJumpButton({ canJump, jumpToBottom }) {
35
47
  return /* @__PURE__ */ jsx(
36
48
  "button",
@@ -51,7 +63,7 @@ function DefaultJumpButton({ canJump, jumpToBottom }) {
51
63
  color: "var(--foreground, #111)",
52
64
  cursor: canJump ? "pointer" : "default"
53
65
  },
54
- children: /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\u2193" })
66
+ children: /* @__PURE__ */ jsx("span", { "aria-hidden": !0, children: "\u2193" })
55
67
  }
56
68
  );
57
69
  }
@@ -60,45 +72,23 @@ function BottomStickScrollArea({
60
72
  className,
61
73
  viewportClassName,
62
74
  contentClassName,
63
- showJumpToBottom = true,
75
+ showJumpToBottom = !0,
64
76
  onDebugStateChange,
65
77
  onModeChange,
66
- debugDomAttributes = false,
78
+ debugDomAttributes = !1,
67
79
  renderJumpToBottom,
68
80
  jumpContainerClassName
69
81
  }) {
70
- const viewportRef = useRef(null);
71
- const contentRef = useRef(null);
72
- const modeRef = useRef("STICKY_INSTANT");
73
- const [mode, setMode] = useState("STICKY_INSTANT");
74
- const [isOverflowing, setIsOverflowing] = useState(false);
75
- const [distanceToBottom, setDistanceToBottom] = useState(0);
76
- const prevScrollTopRef = useRef(0);
77
- const lastProgrammaticWriteRef = useRef(null);
78
- const programmaticWriteCountRef = useRef(0);
79
- const pendingStickRafRef = useRef(null);
80
- const stickRequestedRef = useRef(false);
81
- const returnRafRef = useRef(null);
82
- const returnAnimRef = useRef(null);
83
- const setModeBoth = useCallback(
82
+ let viewportRef = useRef(null), contentRef = useRef(null), modeRef = useRef("STICKY_INSTANT"), [mode, setMode] = useState("STICKY_INSTANT"), [isOverflowing, setIsOverflowing] = useState(!1), [distanceToBottom, setDistanceToBottom] = useState(0), [prefersReducedMotion, setPrefersReducedMotion] = useState(!1), prefersReducedMotionRef = useRef(!1), prevScrollTopRef = useRef(0), lastProgrammaticWriteRef = useRef(null), programmaticWriteCountRef = useRef(0), pendingStickRafRef = useRef(null), stickRequestedRef = useRef(!1), returnRafRef = useRef(null), returnAnimRef = useRef(null), setModeBoth = useCallback(
84
83
  (next) => {
85
- modeRef.current = next;
86
- setMode(next);
87
- onModeChange?.(next);
84
+ modeRef.current = next, setMode(next), onModeChange?.(next);
88
85
  },
89
86
  [onModeChange]
90
- );
91
- const updateDebug = useCallback(() => {
92
- const viewport = viewportRef.current;
87
+ ), updateDebug = useCallback(() => {
88
+ let viewport = viewportRef.current;
93
89
  if (!viewport) return;
94
- const m = measure(viewport);
95
- if (debugDomAttributes) {
96
- viewport.dataset.stickyMode = modeRef.current;
97
- viewport.dataset.distanceToBottom = String(m.distanceToBottom);
98
- viewport.dataset.isOverflowing = m.isOverflowing ? "1" : "0";
99
- viewport.dataset.programmaticWrites = String(programmaticWriteCountRef.current);
100
- }
101
- onDebugStateChange?.({
90
+ let m = measure(viewport);
91
+ debugDomAttributes && (viewport.dataset.stickyMode = modeRef.current, viewport.dataset.distanceToBottom = String(m.distanceToBottom), viewport.dataset.isOverflowing = m.isOverflowing ? "1" : "0", viewport.dataset.programmaticWrites = String(programmaticWriteCountRef.current)), onDebugStateChange?.({
102
92
  mode: modeRef.current,
103
93
  isOverflowing: m.isOverflowing,
104
94
  distanceToBottom: m.distanceToBottom,
@@ -107,60 +97,54 @@ function BottomStickScrollArea({
107
97
  scrollHeight: m.scrollHeight,
108
98
  clientHeight: m.clientHeight,
109
99
  programmaticWrites: programmaticWriteCountRef.current
110
- });
111
- setDistanceToBottom(
100
+ }), setDistanceToBottom(
112
101
  (previous) => Math.abs(previous - m.distanceToBottom) <= 0.1 ? previous : m.distanceToBottom
113
102
  );
114
- }, [debugDomAttributes, onDebugStateChange]);
115
- const writeScrollTop = useCallback((nextTop) => {
116
- const viewport = viewportRef.current;
103
+ }, [debugDomAttributes, onDebugStateChange]), writeScrollTop = useCallback((nextTop) => {
104
+ let viewport = viewportRef.current;
105
+ viewport && (viewport.scrollTop = nextTop, programmaticWriteCountRef.current += 1, lastProgrammaticWriteRef.current = { at: performance.now(), top: nextTop });
106
+ }, []), cancelPendingStick = useCallback(() => {
107
+ pendingStickRafRef.current !== null && (cancelAnimationFrame(pendingStickRafRef.current), pendingStickRafRef.current = null), stickRequestedRef.current = !1;
108
+ }, []), cancelReturn = useCallback(() => {
109
+ returnRafRef.current !== null && (cancelAnimationFrame(returnRafRef.current), returnRafRef.current = null), returnAnimRef.current = null;
110
+ }, []), handleReducedMotionChange = useCallback(
111
+ (matches) => {
112
+ if (prefersReducedMotionRef.current !== matches && (prefersReducedMotionRef.current = matches, setPrefersReducedMotion(matches)), !matches || modeRef.current !== "RETURNING_SMOOTH") return;
113
+ cancelPendingStick(), cancelReturn(), setModeBoth("STICKY_INSTANT");
114
+ let viewport = viewportRef.current;
115
+ viewport && (writeScrollTop(measure(viewport).maxScrollTop), prevScrollTopRef.current = viewport.scrollTop), updateDebug();
116
+ },
117
+ [cancelPendingStick, cancelReturn, setModeBoth, updateDebug, writeScrollTop]
118
+ ), reducedMotionChangeHandlerRef = useRef(handleReducedMotionChange);
119
+ reducedMotionChangeHandlerRef.current = handleReducedMotionChange;
120
+ let interruptReturnForUserIntent = useCallback(() => {
121
+ modeRef.current === "RETURNING_SMOOTH" && (cancelPendingStick(), cancelReturn(), setModeBoth("DETACHED"), updateDebug());
122
+ }, [cancelPendingStick, cancelReturn, setModeBoth, updateDebug]), onViewportKeyDown = useCallback(
123
+ (event) => {
124
+ isScrollKeyIntent(event) && interruptReturnForUserIntent();
125
+ },
126
+ [interruptReturnForUserIntent]
127
+ ), stickToBottomInstant = useCallback(() => {
128
+ let viewport = viewportRef.current;
129
+ viewport && (stickRequestedRef.current = !0, pendingStickRafRef.current === null && (pendingStickRafRef.current = requestAnimationFrame(() => {
130
+ if (pendingStickRafRef.current = null, !stickRequestedRef.current || (stickRequestedRef.current = !1, modeRef.current !== "STICKY_INSTANT")) return;
131
+ let m = measure(viewport);
132
+ writeScrollTop(m.maxScrollTop), updateDebug();
133
+ })));
134
+ }, [updateDebug, writeScrollTop]), startReturnToBottomSmooth = useCallback(() => {
135
+ let viewport = viewportRef.current;
117
136
  if (!viewport) return;
118
- viewport.scrollTop = nextTop;
119
- programmaticWriteCountRef.current += 1;
120
- lastProgrammaticWriteRef.current = { at: performance.now(), top: nextTop };
121
- }, []);
122
- const cancelPendingStick = useCallback(() => {
123
- if (pendingStickRafRef.current !== null) {
124
- cancelAnimationFrame(pendingStickRafRef.current);
125
- pendingStickRafRef.current = null;
126
- }
127
- stickRequestedRef.current = false;
128
- }, []);
129
- const cancelReturn = useCallback(() => {
130
- if (returnRafRef.current !== null) {
131
- cancelAnimationFrame(returnRafRef.current);
132
- returnRafRef.current = null;
137
+ if (prefersReducedMotionRef.current) {
138
+ cancelPendingStick(), cancelReturn(), setModeBoth("STICKY_INSTANT"), writeScrollTop(measure(viewport).maxScrollTop), prevScrollTopRef.current = viewport.scrollTop, updateDebug();
139
+ return;
133
140
  }
134
- returnAnimRef.current = null;
135
- }, []);
136
- const stickToBottomInstant = useCallback(() => {
137
- const viewport = viewportRef.current;
138
- if (!viewport) return;
139
- stickRequestedRef.current = true;
140
- if (pendingStickRafRef.current !== null) return;
141
- pendingStickRafRef.current = requestAnimationFrame(() => {
142
- pendingStickRafRef.current = null;
143
- if (!stickRequestedRef.current) return;
144
- stickRequestedRef.current = false;
145
- if (modeRef.current !== "STICKY_INSTANT") return;
146
- const m = measure(viewport);
147
- writeScrollTop(m.maxScrollTop);
148
- updateDebug();
149
- });
150
- }, [updateDebug, writeScrollTop]);
151
- const startReturnToBottomSmooth = useCallback(() => {
152
- const viewport = viewportRef.current;
153
- if (!viewport) return;
154
- cancelPendingStick();
155
- cancelReturn();
156
- const initial = measure(viewport);
141
+ cancelPendingStick(), cancelReturn();
142
+ let initial = measure(viewport);
157
143
  if (initial.distanceToBottom <= EPS_BOTTOM_PX) {
158
- setModeBoth("STICKY_INSTANT");
159
- stickToBottomInstant();
160
- updateDebug();
144
+ setModeBoth("STICKY_INSTANT"), stickToBottomInstant(), updateDebug();
161
145
  return;
162
146
  }
163
- const durationMs = Math.max(
147
+ let durationMs = Math.max(
164
148
  RETURN_MIN_MS,
165
149
  Math.min(RETURN_MAX_MS, initial.distanceToBottom / RETURN_PX_PER_MS)
166
150
  );
@@ -169,118 +153,60 @@ function BottomStickScrollArea({
169
153
  startTop: initial.scrollTop,
170
154
  durationMs,
171
155
  settleFrames: 0
172
- };
173
- setModeBoth("RETURNING_SMOOTH");
174
- const tick = (now) => {
175
- const viewportCurrent = viewportRef.current;
176
- const anim = returnAnimRef.current;
156
+ }, setModeBoth("RETURNING_SMOOTH");
157
+ let tick = (now) => {
158
+ let viewportCurrent = viewportRef.current, anim = returnAnimRef.current;
177
159
  if (!viewportCurrent || !anim || modeRef.current !== "RETURNING_SMOOTH") {
178
160
  cancelReturn();
179
161
  return;
180
162
  }
181
- const before = measure(viewportCurrent);
182
- const progress = clamp01((now - anim.startTime) / anim.durationMs);
183
- const eased = easeOutCubic(progress);
184
- const targetBottom = before.maxScrollTop;
185
- let nextTop = anim.startTop + (targetBottom - anim.startTop) * eased;
186
- if (nextTop < before.scrollTop) {
187
- nextTop = before.scrollTop;
188
- }
189
- writeScrollTop(nextTop);
190
- const after = measure(viewportCurrent);
191
- if (after.distanceToBottom <= EPS_BOTTOM_PX) {
192
- anim.settleFrames += 1;
193
- } else {
194
- anim.settleFrames = 0;
195
- }
196
- const doneByTime = progress >= 1;
197
- const doneBySettle = anim.settleFrames >= RETURN_SETTLE_FRAMES && progress >= 0.6;
163
+ let before = measure(viewportCurrent), progress = clamp01((now - anim.startTime) / anim.durationMs), eased = easeOutCubic(progress), targetBottom = before.maxScrollTop, nextTop = anim.startTop + (targetBottom - anim.startTop) * eased;
164
+ nextTop < before.scrollTop && (nextTop = before.scrollTop), writeScrollTop(nextTop), measure(viewportCurrent).distanceToBottom <= EPS_BOTTOM_PX ? anim.settleFrames += 1 : anim.settleFrames = 0;
165
+ let doneByTime = progress >= 1, doneBySettle = anim.settleFrames >= RETURN_SETTLE_FRAMES && progress >= 0.6;
198
166
  if (doneByTime && doneBySettle) {
199
- cancelReturn();
200
- setModeBoth("STICKY_INSTANT");
201
- stickToBottomInstant();
202
- updateDebug();
167
+ cancelReturn(), setModeBoth("STICKY_INSTANT"), stickToBottomInstant(), updateDebug();
203
168
  return;
204
169
  }
205
- updateDebug();
206
- returnRafRef.current = requestAnimationFrame(tick);
170
+ updateDebug(), returnRafRef.current = requestAnimationFrame(tick);
207
171
  };
208
172
  returnRafRef.current = requestAnimationFrame(tick);
209
- }, [cancelPendingStick, cancelReturn, setModeBoth, stickToBottomInstant, updateDebug, writeScrollTop]);
210
- const onViewportScroll = useCallback(
173
+ }, [cancelPendingStick, cancelReturn, setModeBoth, stickToBottomInstant, updateDebug, writeScrollTop]), onViewportScroll = useCallback(
211
174
  (event) => {
212
- const viewport = event.currentTarget;
213
- const m = measure(viewport);
175
+ let viewport = event.currentTarget, m = measure(viewport);
214
176
  setIsOverflowing((previous) => previous === m.isOverflowing ? previous : m.isOverflowing);
215
- const previousTop = prevScrollTopRef.current;
216
- const delta = m.scrollTop - previousTop;
217
- const lastWrite = lastProgrammaticWriteRef.current;
218
- const wroteRecently = lastWrite !== null && performance.now() - lastWrite.at < 60;
219
- const isLikelyOwnWrite = wroteRecently && lastWrite !== null && Math.abs(m.scrollTop - lastWrite.top) <= 2;
220
- if (modeRef.current === "RETURNING_SMOOTH") {
221
- if (!isLikelyOwnWrite && delta < -DETACH_SCROLL_DELTA_PX) {
222
- cancelReturn();
223
- setModeBoth("DETACHED");
224
- }
225
- } else if (modeRef.current === "STICKY_INSTANT") {
226
- if (!isLikelyOwnWrite && delta < -DETACH_SCROLL_DELTA_PX && m.isOverflowing) {
227
- cancelPendingStick();
228
- setModeBoth("DETACHED");
229
- }
230
- } else if (modeRef.current === "DETACHED") {
231
- if (m.distanceToBottom <= EPS_BOTTOM_PX) {
232
- setModeBoth("STICKY_INSTANT");
233
- stickToBottomInstant();
234
- }
235
- }
236
- prevScrollTopRef.current = m.scrollTop;
237
- updateDebug();
177
+ let previousTop = prevScrollTopRef.current, delta = m.scrollTop - previousTop, lastWrite = lastProgrammaticWriteRef.current, isLikelyOwnWrite = lastWrite !== null && performance.now() - lastWrite.at < 60 && lastWrite !== null && Math.abs(m.scrollTop - lastWrite.top) <= 2;
178
+ modeRef.current === "RETURNING_SMOOTH" ? !isLikelyOwnWrite && delta < -DETACH_SCROLL_DELTA_PX && (cancelReturn(), setModeBoth("DETACHED")) : modeRef.current === "STICKY_INSTANT" ? !isLikelyOwnWrite && delta < -DETACH_SCROLL_DELTA_PX && m.isOverflowing && (cancelPendingStick(), setModeBoth("DETACHED")) : modeRef.current === "DETACHED" && m.distanceToBottom <= EPS_BOTTOM_PX && (setModeBoth("STICKY_INSTANT"), stickToBottomInstant()), prevScrollTopRef.current = m.scrollTop, updateDebug();
238
179
  },
239
180
  [cancelPendingStick, cancelReturn, setModeBoth, stickToBottomInstant, updateDebug]
240
181
  );
241
182
  useEffect(() => {
242
- const viewport = viewportRef.current;
243
- const content = contentRef.current;
183
+ let viewport = viewportRef.current, content = contentRef.current;
244
184
  if (!viewport || !content) return;
245
- const observer = new ResizeObserver(() => {
246
- const m = measure(viewport);
247
- setIsOverflowing((previous) => previous === m.isOverflowing ? previous : m.isOverflowing);
248
- if (modeRef.current === "STICKY_INSTANT") {
249
- stickToBottomInstant();
250
- }
251
- updateDebug();
185
+ let observer = new ResizeObserver(() => {
186
+ let m = measure(viewport);
187
+ setIsOverflowing((previous) => previous === m.isOverflowing ? previous : m.isOverflowing), modeRef.current === "STICKY_INSTANT" && stickToBottomInstant(), updateDebug();
252
188
  });
253
- observer.observe(content);
254
- observer.observe(viewport);
255
- return () => observer.disconnect();
256
- }, [stickToBottomInstant, updateDebug]);
257
- useLayoutEffect(() => {
258
- const viewport = viewportRef.current;
189
+ return observer.observe(content), observer.observe(viewport), () => observer.disconnect();
190
+ }, [stickToBottomInstant, updateDebug]), useLayoutEffect(() => {
191
+ let viewport = viewportRef.current;
259
192
  if (!viewport) return;
260
193
  setModeBoth("STICKY_INSTANT");
261
- const initial = measure(viewport);
262
- writeScrollTop(initial.maxScrollTop);
263
- prevScrollTopRef.current = viewport.scrollTop;
264
- setIsOverflowing(initial.isOverflowing);
265
- setDistanceToBottom(initial.distanceToBottom);
266
- updateDebug();
267
- }, [setModeBoth, updateDebug, writeScrollTop]);
268
- useEffect(() => {
269
- return () => {
270
- cancelPendingStick();
271
- cancelReturn();
272
- };
194
+ let initial = measure(viewport);
195
+ writeScrollTop(initial.maxScrollTop), prevScrollTopRef.current = viewport.scrollTop, setIsOverflowing(initial.isOverflowing), setDistanceToBottom(initial.distanceToBottom), updateDebug();
196
+ }, [setModeBoth, updateDebug, writeScrollTop]), useLayoutEffect(() => {
197
+ if (typeof window > "u" || typeof window.matchMedia != "function") return;
198
+ let mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)"), onChange = () => reducedMotionChangeHandlerRef.current(mediaQuery.matches);
199
+ return onChange(), typeof mediaQuery.addEventListener == "function" ? (mediaQuery.addEventListener("change", onChange), () => mediaQuery.removeEventListener("change", onChange)) : (mediaQuery.addListener(onChange), () => mediaQuery.removeListener(onChange));
200
+ }, []), useEffect(() => () => {
201
+ cancelPendingStick(), cancelReturn();
273
202
  }, [cancelPendingStick, cancelReturn]);
274
- const canJump = isOverflowing && mode === "DETACHED";
275
- const jumpButton = renderJumpToBottom?.({
203
+ let canJump = isOverflowing && mode === "DETACHED", jumpButton = renderJumpToBottom?.({
276
204
  mode,
277
205
  canJump,
278
206
  isOverflowing,
279
207
  distanceToBottom,
280
208
  jumpToBottom: startReturnToBottomSmooth
281
- }) ?? /* @__PURE__ */ jsx(DefaultJumpButton, { canJump, jumpToBottom: startReturnToBottomSmooth });
282
- const jumpContainerClassNameBase = "pointer-events-none absolute z-20";
283
- const jumpContainerStyle = {
209
+ }) ?? /* @__PURE__ */ jsx(DefaultJumpButton, { canJump, jumpToBottom: startReturnToBottomSmooth }), jumpContainerClassNameBase = "pointer-events-none absolute z-20", jumpContainerStyle = {
284
210
  position: "absolute",
285
211
  zIndex: 20,
286
212
  left: 0,
@@ -289,8 +215,8 @@ function BottomStickScrollArea({
289
215
  display: "flex",
290
216
  justifyContent: "center",
291
217
  opacity: canJump ? 1 : 0,
292
- transform: canJump ? "translateY(0px)" : "translateY(4px)",
293
- transition: "opacity 150ms ease, transform 150ms ease",
218
+ transform: prefersReducedMotion ? "none" : canJump ? "translateY(0px)" : "translateY(4px)",
219
+ transition: prefersReducedMotion ? "none" : "opacity 150ms ease, transform 150ms ease",
294
220
  pointerEvents: "none"
295
221
  };
296
222
  return /* @__PURE__ */ jsxs("div", { className: joinClasses("relative h-full w-full overflow-hidden", className), children: [
@@ -300,6 +226,9 @@ function BottomStickScrollArea({
300
226
  ref: viewportRef,
301
227
  className: joinClasses("h-full w-full overflow-auto", viewportClassName),
302
228
  onScroll: onViewportScroll,
229
+ onWheel: interruptReturnForUserIntent,
230
+ onTouchStart: interruptReturnForUserIntent,
231
+ onKeyDown: onViewportKeyDown,
303
232
  "data-testid": "sticky-scroll-viewport",
304
233
  children: /* @__PURE__ */ jsx("div", { ref: contentRef, className: joinClasses("flex min-h-full w-full flex-col", contentClassName), children })
305
234
  }
@@ -0,0 +1,86 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: !0 });
10
+ }, __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from == "object" || typeof from == "function")
12
+ for (let key of __getOwnPropNames(from))
13
+ !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ return to;
15
+ };
16
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: !0 }), mod);
17
+
18
+ // src/components/diff-renderer.tsx
19
+ var diff_renderer_exports = {};
20
+ __export(diff_renderer_exports, {
21
+ DefaultDiffRenderer: () => DefaultDiffRenderer
22
+ });
23
+ module.exports = __toCommonJS(diff_renderer_exports);
24
+ var import_jsx_runtime = require("react/jsx-runtime"), DefaultDiffRenderer = ({
25
+ meta,
26
+ renderCode,
27
+ className = "",
28
+ style
29
+ }) => {
30
+ let diffBlocks = Array.isArray(meta?.diffBlocks) ? meta.diffBlocks : null;
31
+ return !diffBlocks || diffBlocks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: renderCode }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
32
+ "div",
33
+ {
34
+ className: `stream-mdx-diff-container my-4 rounded-md border border-border bg-muted/20 font-mono text-sm overflow-hidden ${className}`.trim(),
35
+ style,
36
+ children: diffBlocks.map((diffBlock, bIdx) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stream-mdx-diff-block", children: [
37
+ (diffBlock.filePath || diffBlock.additions != null || diffBlock.deletions != null) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stream-mdx-diff-header flex items-center justify-between border-b border-border bg-muted/40 px-4 py-2 text-xs font-semibold text-muted-foreground", children: [
38
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "stream-mdx-diff-filepath", children: diffBlock.filePath ?? "diff" }),
39
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "stream-mdx-diff-stats flex gap-2", children: [
40
+ diffBlock.additions != null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "stream-mdx-diff-stat-add text-green-600 dark:text-green-400", children: [
41
+ "+",
42
+ diffBlock.additions
43
+ ] }),
44
+ diffBlock.deletions != null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "stream-mdx-diff-stat-del text-red-600 dark:text-red-400", children: [
45
+ "-",
46
+ diffBlock.deletions
47
+ ] })
48
+ ] })
49
+ ] }),
50
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "stream-mdx-diff-lines overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", { className: "w-full border-collapse font-mono text-xs", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: diffBlock.lines.map((line, lIdx) => {
51
+ let kindClass = `diff-line-${line.kind} ${line.kind === "add" ? "bg-green-500/10 text-green-900 dark:text-green-200" : line.kind === "del" ? "bg-red-500/10 text-red-900 dark:text-red-200" : line.kind === "hunk" ? "bg-blue-500/10 text-blue-800 dark:text-blue-300 font-semibold" : line.kind === "meta" ? "bg-muted/50 text-muted-foreground italic" : "text-foreground"}`;
52
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
53
+ "tr",
54
+ {
55
+ className: `stream-mdx-diff-row ${kindClass}`,
56
+ "data-diff-kind": line.kind,
57
+ children: [
58
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "stream-mdx-diff-old-no select-none px-2 py-0.5 text-right opacity-50 w-10", children: line.oldNo != null ? line.oldNo : "" }),
59
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "stream-mdx-diff-new-no select-none px-2 py-0.5 text-right opacity-50 w-10", children: line.newNo != null ? line.newNo : "" }),
60
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "stream-mdx-diff-prefix select-none px-1 py-0.5 text-center w-4 font-bold", children: line.kind === "add" ? "+" : line.kind === "del" ? "-" : line.kind === "hunk" ? "@" : " " }),
61
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "stream-mdx-diff-content px-2 py-0.5 whitespace-pre", children: line.tokens && line.tokens.length > 0 ? line.tokens.map((token, tIdx) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
62
+ "span",
63
+ {
64
+ style: {
65
+ color: token.color ?? void 0,
66
+ fontStyle: token.fontStyle === 1 || token.fontStyle === 3 ? "italic" : void 0,
67
+ fontWeight: token.fontStyle === 2 || token.fontStyle === 3 ? "bold" : void 0
68
+ },
69
+ children: token.content
70
+ },
71
+ tIdx
72
+ )) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: line.raw.replace(/^[+\- ]/, "") }) })
73
+ ]
74
+ },
75
+ lIdx
76
+ );
77
+ }) }) }) })
78
+ ] }, bIdx))
79
+ }
80
+ );
81
+ };
82
+ DefaultDiffRenderer.displayName = "DefaultDiffRenderer";
83
+ // Annotate the CommonJS export names for ESM import in node:
84
+ 0 && (module.exports = {
85
+ DefaultDiffRenderer
86
+ });
@@ -0,0 +1,11 @@
1
+ import React__default from 'react';
2
+
3
+ interface DiffRendererProps {
4
+ meta?: Record<string, unknown>;
5
+ renderCode: React__default.ReactNode;
6
+ className?: string;
7
+ style?: React__default.CSSProperties;
8
+ }
9
+ declare const DefaultDiffRenderer: React__default.FC<DiffRendererProps>;
10
+
11
+ export { DefaultDiffRenderer, type DiffRendererProps };
@@ -0,0 +1,11 @@
1
+ import React__default from 'react';
2
+
3
+ interface DiffRendererProps {
4
+ meta?: Record<string, unknown>;
5
+ renderCode: React__default.ReactNode;
6
+ className?: string;
7
+ style?: React__default.CSSProperties;
8
+ }
9
+ declare const DefaultDiffRenderer: React__default.FC<DiffRendererProps>;
10
+
11
+ export { DefaultDiffRenderer, type DiffRendererProps };
@@ -0,0 +1,66 @@
1
+ "use client";
2
+
3
+ // src/components/diff-renderer.tsx
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ var DefaultDiffRenderer = ({
6
+ meta,
7
+ renderCode,
8
+ className = "",
9
+ style
10
+ }) => {
11
+ let diffBlocks = Array.isArray(meta?.diffBlocks) ? meta.diffBlocks : null;
12
+ return !diffBlocks || diffBlocks.length === 0 ? /* @__PURE__ */ jsx(Fragment, { children: renderCode }) : /* @__PURE__ */ jsx(
13
+ "div",
14
+ {
15
+ className: `stream-mdx-diff-container my-4 rounded-md border border-border bg-muted/20 font-mono text-sm overflow-hidden ${className}`.trim(),
16
+ style,
17
+ children: diffBlocks.map((diffBlock, bIdx) => /* @__PURE__ */ jsxs("div", { className: "stream-mdx-diff-block", children: [
18
+ (diffBlock.filePath || diffBlock.additions != null || diffBlock.deletions != null) && /* @__PURE__ */ jsxs("div", { className: "stream-mdx-diff-header flex items-center justify-between border-b border-border bg-muted/40 px-4 py-2 text-xs font-semibold text-muted-foreground", children: [
19
+ /* @__PURE__ */ jsx("span", { className: "stream-mdx-diff-filepath", children: diffBlock.filePath ?? "diff" }),
20
+ /* @__PURE__ */ jsxs("span", { className: "stream-mdx-diff-stats flex gap-2", children: [
21
+ diffBlock.additions != null && /* @__PURE__ */ jsxs("span", { className: "stream-mdx-diff-stat-add text-green-600 dark:text-green-400", children: [
22
+ "+",
23
+ diffBlock.additions
24
+ ] }),
25
+ diffBlock.deletions != null && /* @__PURE__ */ jsxs("span", { className: "stream-mdx-diff-stat-del text-red-600 dark:text-red-400", children: [
26
+ "-",
27
+ diffBlock.deletions
28
+ ] })
29
+ ] })
30
+ ] }),
31
+ /* @__PURE__ */ jsx("div", { className: "stream-mdx-diff-lines overflow-x-auto", children: /* @__PURE__ */ jsx("table", { className: "w-full border-collapse font-mono text-xs", children: /* @__PURE__ */ jsx("tbody", { children: diffBlock.lines.map((line, lIdx) => {
32
+ let kindClass = `diff-line-${line.kind} ${line.kind === "add" ? "bg-green-500/10 text-green-900 dark:text-green-200" : line.kind === "del" ? "bg-red-500/10 text-red-900 dark:text-red-200" : line.kind === "hunk" ? "bg-blue-500/10 text-blue-800 dark:text-blue-300 font-semibold" : line.kind === "meta" ? "bg-muted/50 text-muted-foreground italic" : "text-foreground"}`;
33
+ return /* @__PURE__ */ jsxs(
34
+ "tr",
35
+ {
36
+ className: `stream-mdx-diff-row ${kindClass}`,
37
+ "data-diff-kind": line.kind,
38
+ children: [
39
+ /* @__PURE__ */ jsx("td", { className: "stream-mdx-diff-old-no select-none px-2 py-0.5 text-right opacity-50 w-10", children: line.oldNo != null ? line.oldNo : "" }),
40
+ /* @__PURE__ */ jsx("td", { className: "stream-mdx-diff-new-no select-none px-2 py-0.5 text-right opacity-50 w-10", children: line.newNo != null ? line.newNo : "" }),
41
+ /* @__PURE__ */ jsx("td", { className: "stream-mdx-diff-prefix select-none px-1 py-0.5 text-center w-4 font-bold", children: line.kind === "add" ? "+" : line.kind === "del" ? "-" : line.kind === "hunk" ? "@" : " " }),
42
+ /* @__PURE__ */ jsx("td", { className: "stream-mdx-diff-content px-2 py-0.5 whitespace-pre", children: line.tokens && line.tokens.length > 0 ? line.tokens.map((token, tIdx) => /* @__PURE__ */ jsx(
43
+ "span",
44
+ {
45
+ style: {
46
+ color: token.color ?? void 0,
47
+ fontStyle: token.fontStyle === 1 || token.fontStyle === 3 ? "italic" : void 0,
48
+ fontWeight: token.fontStyle === 2 || token.fontStyle === 3 ? "bold" : void 0
49
+ },
50
+ children: token.content
51
+ },
52
+ tIdx
53
+ )) : /* @__PURE__ */ jsx("span", { children: line.raw.replace(/^[+\- ]/, "") }) })
54
+ ]
55
+ },
56
+ lIdx
57
+ );
58
+ }) }) }) })
59
+ ] }, bIdx))
60
+ }
61
+ );
62
+ };
63
+ DefaultDiffRenderer.displayName = "DefaultDiffRenderer";
64
+ export {
65
+ DefaultDiffRenderer
66
+ };