@mastra/playground-ui 40.0.0-alpha.3 → 40.0.0-alpha.5

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 (69) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/components/CodeBlock.cjs.js +1 -1
  3. package/dist/components/CodeBlock.es.js +1 -1
  4. package/dist/components/MessageScroller.cjs.js +538 -0
  5. package/dist/components/MessageScroller.cjs.js.map +1 -0
  6. package/dist/components/MessageScroller.d.ts +2 -0
  7. package/dist/components/MessageScroller.es.js +505 -0
  8. package/dist/components/MessageScroller.es.js.map +1 -0
  9. package/dist/components/Tabs.cjs.js +1 -1
  10. package/dist/components/Tabs.es.js +1 -1
  11. package/dist/components/ThreadRail.cjs.js +432 -0
  12. package/dist/components/ThreadRail.cjs.js.map +1 -0
  13. package/dist/components/ThreadRail.d.ts +2 -0
  14. package/dist/components/ThreadRail.es.js +407 -0
  15. package/dist/components/ThreadRail.es.js.map +1 -0
  16. package/dist/components/Tree.cjs.js +2 -2
  17. package/dist/components/Tree.cjs.js.map +1 -1
  18. package/dist/components/Tree.es.js +1 -1
  19. package/dist/components/Tree.es.js.map +1 -1
  20. package/dist/domains/metrics/components/latency-card-view.cjs.js +64 -42
  21. package/dist/domains/metrics/components/latency-card-view.cjs.js.map +1 -1
  22. package/dist/domains/metrics/components/latency-card-view.es.js +65 -43
  23. package/dist/domains/metrics/components/latency-card-view.es.js.map +1 -1
  24. package/dist/domains/metrics/components/memory-card-view.cjs.js +1 -1
  25. package/dist/domains/metrics/components/memory-card-view.es.js +1 -1
  26. package/dist/domains/metrics/components/scores-card-view.cjs.js +1 -1
  27. package/dist/domains/metrics/components/scores-card-view.es.js +1 -1
  28. package/dist/domains/metrics/components/token-usage-by-agent-card-view.cjs.js +1 -1
  29. package/dist/domains/metrics/components/token-usage-by-agent-card-view.es.js +1 -1
  30. package/dist/domains/metrics/components/token-usage-timeline-card-view.cjs.js +1 -1
  31. package/dist/domains/metrics/components/token-usage-timeline-card-view.es.js +1 -1
  32. package/dist/domains/metrics/components/traces-volume-card-view.cjs.js +1 -1
  33. package/dist/domains/metrics/components/traces-volume-card-view.es.js +1 -1
  34. package/dist/domains/traces/components/span-data-panel-view.cjs.js +1 -1
  35. package/dist/domains/traces/components/span-data-panel-view.es.js +1 -1
  36. package/dist/ee/signals/components/signal-details-page.cjs.js +1 -1
  37. package/dist/ee/signals/components/signal-details-page.es.js +1 -1
  38. package/dist/hooks/use-isomorphic-layout-effect.cjs.js +29 -0
  39. package/dist/hooks/use-isomorphic-layout-effect.cjs.js.map +1 -0
  40. package/dist/hooks/use-isomorphic-layout-effect.d.ts +2 -0
  41. package/dist/hooks/use-isomorphic-layout-effect.es.js +6 -0
  42. package/dist/hooks/use-isomorphic-layout-effect.es.js.map +1 -0
  43. package/dist/hooks/use-measured-auto-height.cjs.js +75 -0
  44. package/dist/hooks/use-measured-auto-height.cjs.js.map +1 -0
  45. package/dist/hooks/use-measured-auto-height.d.ts +2 -0
  46. package/dist/hooks/use-measured-auto-height.es.js +52 -0
  47. package/dist/hooks/use-measured-auto-height.es.js.map +1 -0
  48. package/dist/message-scroller-hooks-CdsHnth6.js +62 -0
  49. package/dist/message-scroller-hooks-CdsHnth6.js.map +1 -0
  50. package/dist/message-scroller-hooks-DdXqrmC2.cjs +98 -0
  51. package/dist/message-scroller-hooks-DdXqrmC2.cjs.map +1 -0
  52. package/dist/src/ds/components/MessageScroller/index.d.ts +2 -0
  53. package/dist/src/ds/components/MessageScroller/message-scroller-context.d.ts +38 -0
  54. package/dist/src/ds/components/MessageScroller/message-scroller-hooks.d.ts +26 -0
  55. package/dist/src/ds/components/MessageScroller/message-scroller.d.ts +44 -0
  56. package/dist/src/ds/components/Tabs/tabs-tab.d.ts +2 -1
  57. package/dist/src/ds/components/ThreadRail/index.d.ts +2 -0
  58. package/dist/src/ds/components/ThreadRail/thread-rail-turns.d.ts +11 -0
  59. package/dist/src/ds/components/ThreadRail/thread-rail.d.ts +12 -0
  60. package/dist/src/hooks/use-isomorphic-layout-effect.d.ts +2 -0
  61. package/dist/src/hooks/use-measured-auto-height.d.ts +8 -0
  62. package/dist/style.css +249 -5
  63. package/dist/{tabs-tab-CaZnZdY8.js → tabs-tab-1KGOybMG.js} +27 -17
  64. package/dist/tabs-tab-1KGOybMG.js.map +1 -0
  65. package/dist/{tabs-tab-BeUPTvaP.cjs → tabs-tab-DkeS2Iv0.cjs} +27 -17
  66. package/dist/tabs-tab-DkeS2Iv0.cjs.map +1 -0
  67. package/package.json +6 -6
  68. package/dist/tabs-tab-BeUPTvaP.cjs.map +0 -1
  69. package/dist/tabs-tab-CaZnZdY8.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # @mastra/playground-ui
2
2
 
3
+ ## 40.0.0-alpha.5
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`a0085fa`](https://github.com/mastra-ai/mastra/commit/a0085fa0934e52c37c8c8b3d75a6bb5cd199af36)]:
8
+ - @mastra/core@1.50.0-alpha.5
9
+ - @mastra/client-js@1.31.0-alpha.5
10
+ - @mastra/react@1.2.3-alpha.5
11
+
12
+ ## 40.0.0-alpha.4
13
+
14
+ ### Patch Changes
15
+
16
+ - Improved Studio metrics latency tabs so empty entity types are clearly disabled. ([#18996](https://github.com/mastra-ai/mastra/pull/18996))
17
+
18
+ - Added reusable message scroller and thread rail primitives for conversation navigation, plus a shared `useIsomorphicLayoutEffect` hook for layout work that must also render safely on the server. ([#18902](https://github.com/mastra-ai/mastra/pull/18902))
19
+
20
+ ```tsx
21
+ import {
22
+ MessageScroller,
23
+ MessageScrollerButton,
24
+ MessageScrollerContent,
25
+ MessageScrollerItem,
26
+ MessageScrollerProvider,
27
+ MessageScrollerViewport,
28
+ } from '@mastra/playground-ui/components/MessageScroller';
29
+ import { ThreadRail, buildThreadRailTurns } from '@mastra/playground-ui/components/ThreadRail';
30
+
31
+ const turns = buildThreadRailTurns(messages);
32
+
33
+ <MessageScrollerProvider>
34
+ <MessageScroller>
35
+ <MessageScrollerViewport>
36
+ <MessageScrollerContent>
37
+ {messages.map(message => (
38
+ <MessageScrollerItem key={message.id} messageId={message.id}>
39
+ <MessageRow message={message} />
40
+ </MessageScrollerItem>
41
+ ))}
42
+ </MessageScrollerContent>
43
+ </MessageScrollerViewport>
44
+ <MessageScrollerButton />
45
+ <ThreadRail turns={turns} />
46
+ </MessageScroller>
47
+ </MessageScrollerProvider>;
48
+ ```
49
+
50
+ - Updated dependencies [[`4039488`](https://github.com/mastra-ai/mastra/commit/403948898af7293198d9e8b3e7fb47f623c78b94), [`b2c9d70`](https://github.com/mastra-ai/mastra/commit/b2c9d70757207fb01a9069549e69b6f0d73a6636), [`252f63d`](https://github.com/mastra-ai/mastra/commit/252f63d8fec723955adb2202be2f01a75ad0e69c), [`c547a77`](https://github.com/mastra-ai/mastra/commit/c547a7729bdf64dfc2df29c965046c0712a18f10), [`81542c1`](https://github.com/mastra-ai/mastra/commit/81542c1835c35bc32f2ce4fa9136ee11993cd299), [`cb24ce7`](https://github.com/mastra-ai/mastra/commit/cb24ce76bd16ca88eb6a963f6277f8780e703029), [`5f9858f`](https://github.com/mastra-ai/mastra/commit/5f9858f791f1137ca7d52d23559fb4568f7a9026)]:
51
+ - @mastra/core@1.50.0-alpha.4
52
+ - @mastra/client-js@1.31.0-alpha.4
53
+ - @mastra/react@1.2.3-alpha.4
54
+
3
55
  ## 40.0.0-alpha.3
4
56
 
5
57
  ### Patch Changes
@@ -6,7 +6,7 @@ const jsxRuntime = require('react/jsx-runtime');
6
6
  const code = require('../code-B3WONxnA.cjs');
7
7
  const copyButton = require('../copy-button-DN9mi_LV.cjs');
8
8
  const select = require('../select-BxSmi19p.cjs');
9
- const tabsTab = require('../tabs-tab-BeUPTvaP.cjs');
9
+ const tabsTab = require('../tabs-tab-DkeS2Iv0.cjs');
10
10
  const primitives_transitions = require('../primitives/transitions.cjs.js');
11
11
  const utils_cn = require('../cn-vA5sWo9W.cjs');
12
12
 
@@ -2,7 +2,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { C as Code } from '../code-C5KVVzOZ.js';
3
3
  import { C as CopyButton } from '../copy-button-CJjjhHKN.js';
4
4
  import { S as Select, d as SelectTrigger, e as SelectValue, a as SelectContent, c as SelectItem } from '../select-CFB6QZ9B.js';
5
- import { b as Tabs, a as TabList, T as Tab } from '../tabs-tab-CaZnZdY8.js';
5
+ import { b as Tabs, a as TabList, T as Tab } from '../tabs-tab-1KGOybMG.js';
6
6
  import { transitions } from '../primitives/transitions.es.js';
7
7
  import { c as cn } from '../cn-BakJzhuv.js';
8
8
 
@@ -0,0 +1,538 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+
5
+ const jsxRuntime = require('react/jsx-runtime');
6
+ const lucideReact = require('lucide-react');
7
+ const React = require('react');
8
+ const messageScrollerHooks = require('../message-scroller-hooks-DdXqrmC2.cjs');
9
+ const utils_cn = require('../cn-vA5sWo9W.cjs');
10
+
11
+ function _interopNamespaceDefault(e) {
12
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
13
+ if (e) {
14
+ for (const k in e) {
15
+ if (k !== 'default') {
16
+ const d = Object.getOwnPropertyDescriptor(e, k);
17
+ Object.defineProperty(n, k, d.get ? d : {
18
+ enumerable: true,
19
+ get: () => e[k]
20
+ });
21
+ }
22
+ }
23
+ }
24
+ n.default = e;
25
+ return Object.freeze(n);
26
+ }
27
+
28
+ const React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
29
+
30
+ const VISIBILITY_EPSILON = 0.5;
31
+ const mergeRefs = (...refs) => (element) => {
32
+ refs.forEach((ref) => {
33
+ if (!ref) return;
34
+ if (typeof ref === "function") {
35
+ ref(element);
36
+ return;
37
+ }
38
+ ref.current = element;
39
+ });
40
+ };
41
+ const scrollableMatches = (left, right) => left.start === right.start && left.end === right.end;
42
+ const visibilityMatches = (left, right) => left.currentAnchorId === right.currentAnchorId && left.visibleMessageIds.length === right.visibleMessageIds.length && left.visibleMessageIds.every((messageId, index) => messageId === right.visibleMessageIds[index]);
43
+ const getContentPadding = (contentElement) => {
44
+ if (!contentElement) return { start: 0, end: 0 };
45
+ const styles = window.getComputedStyle(contentElement);
46
+ return {
47
+ start: Number.parseFloat(styles.paddingBlockStart || styles.paddingTop || "0") || 0,
48
+ end: Number.parseFloat(styles.paddingBlockEnd || styles.paddingBottom || "0") || 0
49
+ };
50
+ };
51
+ const getRelativeTop = (element, viewportElement) => {
52
+ const elementRect = element.getBoundingClientRect();
53
+ const viewportRect = viewportElement.getBoundingClientRect();
54
+ return elementRect.top - viewportRect.top + viewportElement.scrollTop;
55
+ };
56
+ const scrollViewportTo = (viewportElement, top, behavior) => {
57
+ if (typeof viewportElement.scrollTo === "function") {
58
+ viewportElement.scrollTo({ top, behavior });
59
+ return;
60
+ }
61
+ viewportElement.scrollTop = top;
62
+ };
63
+ const scheduleScrollSync = (callback) => {
64
+ if (typeof window.requestAnimationFrame === "function") {
65
+ window.requestAnimationFrame(callback);
66
+ return;
67
+ }
68
+ window.setTimeout(callback, 0);
69
+ };
70
+ const getScrollTarget = ({
71
+ align,
72
+ element,
73
+ scrollMargin,
74
+ viewportElement
75
+ }) => {
76
+ const contentPadding = getContentPadding(element.parentElement);
77
+ const elementTop = getRelativeTop(element, viewportElement);
78
+ const elementHeight = element.getBoundingClientRect().height;
79
+ const visibleHeight = Math.max(0, viewportElement.clientHeight - contentPadding.start - contentPadding.end);
80
+ if (align === "center") return elementTop - contentPadding.start - (visibleHeight - elementHeight) / 2 - scrollMargin;
81
+ if (align === "end")
82
+ return elementTop - viewportElement.clientHeight + elementHeight + contentPadding.end + scrollMargin;
83
+ if (align === "nearest") {
84
+ const elementBottom = elementTop + elementHeight;
85
+ const viewportTop = viewportElement.scrollTop + contentPadding.start;
86
+ const viewportBottom = viewportElement.scrollTop + viewportElement.clientHeight - contentPadding.end;
87
+ if (elementTop >= viewportTop && elementBottom <= viewportBottom) return viewportElement.scrollTop;
88
+ return elementTop < viewportTop ? elementTop - contentPadding.start - scrollMargin : elementBottom - viewportElement.clientHeight + contentPadding.end + scrollMargin;
89
+ }
90
+ return elementTop - contentPadding.start - scrollMargin;
91
+ };
92
+ const getCurrentAnchorId = ({
93
+ fallbackAnchorId,
94
+ items,
95
+ scrollMargin,
96
+ scrollPreviousItemPeek,
97
+ visibleMessageIds,
98
+ viewportElement
99
+ }) => {
100
+ const anchorLine = viewportElement.getBoundingClientRect().top + scrollMargin + scrollPreviousItemPeek;
101
+ const anchors = items.filter(([, item]) => item.scrollAnchor);
102
+ let anchoredAboveViewport;
103
+ for (const [messageId, item] of anchors) {
104
+ if (item.element.getBoundingClientRect().top <= anchorLine + VISIBILITY_EPSILON) {
105
+ anchoredAboveViewport = messageId;
106
+ }
107
+ }
108
+ if (anchoredAboveViewport) return anchoredAboveViewport;
109
+ return anchors.find(([messageId]) => visibleMessageIds.has(messageId))?.[0] ?? fallbackAnchorId;
110
+ };
111
+ function MessageScrollerProvider({
112
+ autoScroll = false,
113
+ children,
114
+ defaultScrollPosition = "end",
115
+ scrollEdgeThreshold = messageScrollerHooks.DEFAULT_SCROLL_EDGE_THRESHOLD,
116
+ scrollMargin = messageScrollerHooks.DEFAULT_SCROLL_MARGIN,
117
+ scrollPreviousItemPeek = messageScrollerHooks.DEFAULT_SCROLL_PREVIOUS_ITEM_PEEK
118
+ }) {
119
+ const itemsRef = React__namespace.useRef(null);
120
+ itemsRef.current ??= /* @__PURE__ */ new Map();
121
+ const itemsRegistry = itemsRef.current;
122
+ const visibleMessageIdsRef = React__namespace.useRef(null);
123
+ visibleMessageIdsRef.current ??= /* @__PURE__ */ new Set();
124
+ const intersectingMessageIds = visibleMessageIdsRef.current;
125
+ const [itemsVersion, setItemsVersion] = React__namespace.useState(0);
126
+ const [rootElement, setRootElement] = React__namespace.useState(null);
127
+ const [viewportElement, setViewportElement] = React__namespace.useState(null);
128
+ const [contentElement, setContentElement] = React__namespace.useState(null);
129
+ const defaultScrollAppliedRef = React__namespace.useRef(false);
130
+ const [scrollable, setScrollable] = React__namespace.useState(messageScrollerHooks.DEFAULT_SCROLLABLE);
131
+ const [visibility, setVisibility] = React__namespace.useState(messageScrollerHooks.DEFAULT_VISIBILITY);
132
+ const publishScrollable = React__namespace.useCallback(
133
+ (nextScrollable) => {
134
+ const scrollableValue = [nextScrollable.start && "start", nextScrollable.end && "end"].filter(Boolean).join(" ");
135
+ for (const element of [rootElement, viewportElement]) {
136
+ if (!element) continue;
137
+ if (scrollableValue) {
138
+ element.setAttribute("data-scrollable", scrollableValue);
139
+ } else {
140
+ element.removeAttribute("data-scrollable");
141
+ }
142
+ }
143
+ setScrollable((current) => scrollableMatches(current, nextScrollable) ? current : nextScrollable);
144
+ },
145
+ [rootElement, viewportElement]
146
+ );
147
+ const publishVisibility = React__namespace.useCallback((nextVisibility) => {
148
+ setVisibility((current) => visibilityMatches(current, nextVisibility) ? current : nextVisibility);
149
+ }, []);
150
+ const updateScrollable = React__namespace.useCallback(() => {
151
+ if (!viewportElement) {
152
+ publishScrollable(messageScrollerHooks.DEFAULT_SCROLLABLE);
153
+ return;
154
+ }
155
+ const remainingScroll = viewportElement.scrollHeight - viewportElement.scrollTop - viewportElement.clientHeight;
156
+ publishScrollable({
157
+ start: viewportElement.scrollTop > scrollEdgeThreshold,
158
+ end: remainingScroll > scrollEdgeThreshold
159
+ });
160
+ }, [publishScrollable, scrollEdgeThreshold, viewportElement]);
161
+ const updateVisibility = React__namespace.useCallback(() => {
162
+ const items = Array.from(itemsRegistry.entries());
163
+ const fallbackAnchorId = items.filter(([, item]) => item.scrollAnchor).at(-1)?.[0] ?? items.at(-1)?.[0];
164
+ if (items.length === 0) {
165
+ publishVisibility(messageScrollerHooks.DEFAULT_VISIBILITY);
166
+ return;
167
+ }
168
+ if (!viewportElement) {
169
+ publishVisibility({
170
+ currentAnchorId: fallbackAnchorId,
171
+ visibleMessageIds: fallbackAnchorId ? [fallbackAnchorId] : []
172
+ });
173
+ return;
174
+ }
175
+ const viewportRect = viewportElement.getBoundingClientRect();
176
+ const visibleMessageIds = /* @__PURE__ */ new Set();
177
+ if (typeof IntersectionObserver === "undefined") {
178
+ const visibilityTop = viewportRect.top + scrollMargin + scrollPreviousItemPeek;
179
+ items.forEach(([messageId, item]) => {
180
+ const rect = item.element.getBoundingClientRect();
181
+ if (rect.bottom > visibilityTop && rect.top < viewportRect.bottom) visibleMessageIds.add(messageId);
182
+ });
183
+ } else {
184
+ intersectingMessageIds.forEach((messageId) => visibleMessageIds.add(messageId));
185
+ }
186
+ const orderedVisibleMessageIds = items.flatMap(
187
+ ([messageId]) => visibleMessageIds.has(messageId) ? [messageId] : []
188
+ );
189
+ publishVisibility({
190
+ currentAnchorId: getCurrentAnchorId({
191
+ fallbackAnchorId,
192
+ items,
193
+ scrollMargin,
194
+ scrollPreviousItemPeek,
195
+ visibleMessageIds,
196
+ viewportElement
197
+ }),
198
+ visibleMessageIds: orderedVisibleMessageIds.length > 0 ? orderedVisibleMessageIds : fallbackAnchorId ? [fallbackAnchorId] : []
199
+ });
200
+ }, [intersectingMessageIds, itemsRegistry, publishVisibility, scrollMargin, scrollPreviousItemPeek, viewportElement]);
201
+ const syncAfterScroll = React__namespace.useCallback(() => {
202
+ updateScrollable();
203
+ updateVisibility();
204
+ }, [updateScrollable, updateVisibility]);
205
+ const scrollToElement = React__namespace.useCallback(
206
+ (element, {
207
+ align = "start",
208
+ behavior = "auto",
209
+ scrollMargin: optionScrollMargin = scrollMargin
210
+ } = {}) => {
211
+ if (!viewportElement || !contentElement?.contains(element)) return false;
212
+ const nextScrollTop = Math.max(
213
+ 0,
214
+ getScrollTarget({ align, element, scrollMargin: optionScrollMargin, viewportElement })
215
+ );
216
+ if (Math.abs(viewportElement.scrollTop - nextScrollTop) <= VISIBILITY_EPSILON) {
217
+ scrollViewportTo(viewportElement, nextScrollTop, "auto");
218
+ syncAfterScroll();
219
+ return true;
220
+ }
221
+ scrollViewportTo(viewportElement, nextScrollTop, behavior);
222
+ scheduleScrollSync(syncAfterScroll);
223
+ return true;
224
+ },
225
+ [contentElement, scrollMargin, syncAfterScroll, viewportElement]
226
+ );
227
+ const scrollToStart = React__namespace.useCallback(
228
+ ({ behavior = "auto" } = {}) => {
229
+ if (!viewportElement) return false;
230
+ scrollViewportTo(viewportElement, 0, behavior);
231
+ scheduleScrollSync(syncAfterScroll);
232
+ return true;
233
+ },
234
+ [syncAfterScroll, viewportElement]
235
+ );
236
+ const scrollToEnd = React__namespace.useCallback(
237
+ ({ behavior = "auto" } = {}) => {
238
+ if (!viewportElement) return false;
239
+ scrollViewportTo(
240
+ viewportElement,
241
+ Math.max(0, viewportElement.scrollHeight - viewportElement.clientHeight),
242
+ behavior
243
+ );
244
+ scheduleScrollSync(syncAfterScroll);
245
+ return true;
246
+ },
247
+ [syncAfterScroll, viewportElement]
248
+ );
249
+ const scrollToMessage = React__namespace.useCallback(
250
+ (messageId, options) => {
251
+ const item = itemsRegistry.get(messageId);
252
+ if (!item) return false;
253
+ return scrollToElement(item.element, options);
254
+ },
255
+ [itemsRegistry, scrollToElement]
256
+ );
257
+ const registerItem = React__namespace.useCallback(
258
+ (messageId, element, scrollAnchor) => {
259
+ itemsRegistry.set(messageId, { element, scrollAnchor });
260
+ setItemsVersion((version) => version + 1);
261
+ return () => {
262
+ const current = itemsRegistry.get(messageId);
263
+ if (current?.element !== element) return;
264
+ itemsRegistry.delete(messageId);
265
+ intersectingMessageIds.delete(messageId);
266
+ setItemsVersion((version) => version + 1);
267
+ };
268
+ },
269
+ [intersectingMessageIds, itemsRegistry]
270
+ );
271
+ React__namespace.useEffect(() => {
272
+ if (!contentElement || !viewportElement || typeof IntersectionObserver === "undefined") {
273
+ updateVisibility();
274
+ return void 0;
275
+ }
276
+ const messageIdByElement = /* @__PURE__ */ new Map();
277
+ for (const [messageId, item] of itemsRegistry) {
278
+ messageIdByElement.set(item.element, messageId);
279
+ }
280
+ const observer = new IntersectionObserver(
281
+ (entries) => {
282
+ for (const entry of entries) {
283
+ const messageId = messageIdByElement.get(entry.target);
284
+ if (!messageId) continue;
285
+ if (entry.isIntersecting) {
286
+ intersectingMessageIds.add(messageId);
287
+ } else {
288
+ intersectingMessageIds.delete(messageId);
289
+ }
290
+ }
291
+ updateVisibility();
292
+ },
293
+ {
294
+ root: viewportElement,
295
+ rootMargin: `${-(scrollMargin + scrollPreviousItemPeek)}px 0px 0px 0px`,
296
+ threshold: [0, 0.01, 0.5, 1]
297
+ }
298
+ );
299
+ for (const [, item] of itemsRegistry) observer.observe(item.element);
300
+ updateVisibility();
301
+ return () => observer.disconnect();
302
+ }, [
303
+ contentElement,
304
+ intersectingMessageIds,
305
+ itemsRegistry,
306
+ itemsVersion,
307
+ scrollMargin,
308
+ scrollPreviousItemPeek,
309
+ updateVisibility,
310
+ viewportElement
311
+ ]);
312
+ React__namespace.useEffect(() => {
313
+ if (!viewportElement || typeof ResizeObserver === "undefined") return void 0;
314
+ const observer = new ResizeObserver(syncAfterScroll);
315
+ observer.observe(viewportElement);
316
+ return () => observer.disconnect();
317
+ }, [syncAfterScroll, viewportElement]);
318
+ React__namespace.useLayoutEffect(() => {
319
+ updateScrollable();
320
+ updateVisibility();
321
+ }, [itemsVersion, updateScrollable, updateVisibility]);
322
+ React__namespace.useLayoutEffect(() => {
323
+ if (defaultScrollAppliedRef.current || !viewportElement || itemsRegistry.size === 0) return;
324
+ let didScroll = false;
325
+ if (defaultScrollPosition === "start") {
326
+ didScroll = scrollToStart({ behavior: "auto" });
327
+ } else if (defaultScrollPosition === "last-anchor") {
328
+ const lastAnchorId = Array.from(itemsRegistry.entries()).filter(([, item]) => item.scrollAnchor).at(-1)?.[0];
329
+ didScroll = lastAnchorId ? scrollToMessage(lastAnchorId, { align: "start", behavior: "auto" }) : scrollToEnd({ behavior: "auto" });
330
+ } else {
331
+ didScroll = scrollToEnd({ behavior: "auto" });
332
+ }
333
+ if (didScroll) defaultScrollAppliedRef.current = true;
334
+ }, [
335
+ defaultScrollPosition,
336
+ itemsRegistry,
337
+ itemsVersion,
338
+ scrollToEnd,
339
+ scrollToMessage,
340
+ scrollToStart,
341
+ viewportElement
342
+ ]);
343
+ React__namespace.useLayoutEffect(() => {
344
+ if (!autoScroll || !defaultScrollAppliedRef.current) return;
345
+ scrollToEnd({ behavior: "auto" });
346
+ }, [autoScroll, itemsVersion, scrollToEnd]);
347
+ const actionsContextValue = React__namespace.useMemo(
348
+ () => ({
349
+ registerItem,
350
+ scrollToEnd,
351
+ scrollToMessage,
352
+ scrollToStart,
353
+ setContentElement,
354
+ setRootElement,
355
+ setViewportElement,
356
+ syncAfterScroll
357
+ }),
358
+ [registerItem, scrollToEnd, scrollToMessage, scrollToStart, syncAfterScroll]
359
+ );
360
+ const scrollableContextValue = React__namespace.useMemo(
361
+ () => ({
362
+ end: scrollable.end,
363
+ start: scrollable.start
364
+ }),
365
+ [scrollable.end, scrollable.start]
366
+ );
367
+ const visibilityContextValue = React__namespace.useMemo(
368
+ () => ({
369
+ currentAnchorId: visibility.currentAnchorId,
370
+ visibleMessageIds: visibility.visibleMessageIds
371
+ }),
372
+ [visibility.currentAnchorId, visibility.visibleMessageIds]
373
+ );
374
+ return /* @__PURE__ */ jsxRuntime.jsx(messageScrollerHooks.MessageScrollerActionsContext.Provider, { value: actionsContextValue, children: /* @__PURE__ */ jsxRuntime.jsx(messageScrollerHooks.MessageScrollerScrollableContext.Provider, { value: scrollableContextValue, children: /* @__PURE__ */ jsxRuntime.jsx(messageScrollerHooks.MessageScrollerVisibilityContext.Provider, { value: visibilityContextValue, children }) }) });
375
+ }
376
+ const MessageScroller = React__namespace.forwardRef(
377
+ ({ className, ...props }, ref) => {
378
+ const { setRootElement } = messageScrollerHooks.useRequiredMessageScrollerActionsContext("MessageScroller");
379
+ return /* @__PURE__ */ jsxRuntime.jsx(
380
+ "div",
381
+ {
382
+ ref: mergeRefs(setRootElement, ref),
383
+ "data-slot": "message-scroller",
384
+ className: utils_cn.cn("group/message-scroller relative flex size-full min-h-0 flex-col overflow-hidden", className),
385
+ ...props
386
+ }
387
+ );
388
+ }
389
+ );
390
+ MessageScroller.displayName = "MessageScroller";
391
+ const MessageScrollerViewport = React__namespace.forwardRef(
392
+ ({ className, onScroll, preserveScrollOnPrepend, role, tabIndex, ...props }, ref) => {
393
+ const { setViewportElement, syncAfterScroll } = messageScrollerHooks.useRequiredMessageScrollerActionsContext("MessageScrollerViewport");
394
+ const viewportRef = React__namespace.useMemo(() => mergeRefs(setViewportElement, ref), [ref, setViewportElement]);
395
+ return /* @__PURE__ */ jsxRuntime.jsx(
396
+ "div",
397
+ {
398
+ ref: viewportRef,
399
+ role: role ?? "region",
400
+ tabIndex: tabIndex ?? 0,
401
+ "data-slot": "message-scroller-viewport",
402
+ "data-preserve-scroll-on-prepend": preserveScrollOnPrepend ? "true" : void 0,
403
+ className: utils_cn.cn(
404
+ "size-full min-h-0 min-w-0 overflow-y-auto overscroll-contain data-autoscrolling:scrollbar-thumb-transparent data-autoscrolling:scrollbar-track-transparent",
405
+ className
406
+ ),
407
+ onScroll: (event) => {
408
+ syncAfterScroll();
409
+ onScroll?.(event);
410
+ },
411
+ ...props
412
+ }
413
+ );
414
+ }
415
+ );
416
+ MessageScrollerViewport.displayName = "MessageScrollerViewport";
417
+ const MessageScrollerContent = React__namespace.forwardRef(
418
+ ({ children, className, spacerClassName, role, "aria-relevant": ariaRelevant = "additions", ...props }, ref) => {
419
+ const { setContentElement, syncAfterScroll } = messageScrollerHooks.useRequiredMessageScrollerActionsContext("MessageScrollerContent");
420
+ const [contentElement, setLocalContentElement] = React__namespace.useState(null);
421
+ const contentRef = React__namespace.useMemo(
422
+ () => mergeRefs(setContentElement, setLocalContentElement, ref),
423
+ [ref, setContentElement]
424
+ );
425
+ React__namespace.useLayoutEffect(() => {
426
+ syncAfterScroll();
427
+ }, [syncAfterScroll]);
428
+ React__namespace.useEffect(() => {
429
+ if (!contentElement || typeof MutationObserver === "undefined") return void 0;
430
+ const observer = new MutationObserver(syncAfterScroll);
431
+ observer.observe(contentElement, { childList: true, subtree: false });
432
+ return () => observer.disconnect();
433
+ }, [contentElement, syncAfterScroll]);
434
+ React__namespace.useEffect(() => {
435
+ if (!contentElement || typeof ResizeObserver === "undefined") return void 0;
436
+ const observer = new ResizeObserver(syncAfterScroll);
437
+ observer.observe(contentElement);
438
+ return () => observer.disconnect();
439
+ }, [contentElement, syncAfterScroll]);
440
+ return /* @__PURE__ */ jsxRuntime.jsxs(
441
+ "div",
442
+ {
443
+ ref: contentRef,
444
+ role: role ?? "log",
445
+ "aria-relevant": ariaRelevant,
446
+ "data-slot": "message-scroller-content",
447
+ className: utils_cn.cn("flex h-max min-h-full flex-col gap-6", className),
448
+ ...props,
449
+ children: [
450
+ children,
451
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", "data-message-scroller-spacer": "", hidden: true, className: spacerClassName })
452
+ ]
453
+ }
454
+ );
455
+ }
456
+ );
457
+ MessageScrollerContent.displayName = "MessageScrollerContent";
458
+ const MessageScrollerItem = React__namespace.forwardRef(
459
+ ({ className, messageId, scrollAnchor = false, ...props }, ref) => {
460
+ const { registerItem } = messageScrollerHooks.useRequiredMessageScrollerActionsContext("MessageScrollerItem");
461
+ const unregisterRef = React__namespace.useRef(void 0);
462
+ const itemRef = React__namespace.useCallback(
463
+ (element) => {
464
+ unregisterRef.current?.();
465
+ unregisterRef.current = void 0;
466
+ mergeRefs(ref)(element);
467
+ if (!element || !messageId) return;
468
+ unregisterRef.current = registerItem(messageId, element, scrollAnchor);
469
+ },
470
+ [messageId, ref, registerItem, scrollAnchor]
471
+ );
472
+ return /* @__PURE__ */ jsxRuntime.jsx(
473
+ "div",
474
+ {
475
+ ref: itemRef,
476
+ "data-slot": "message-scroller-item",
477
+ "data-message-id": messageId,
478
+ "data-scroll-anchor": scrollAnchor ? "true" : "false",
479
+ className: utils_cn.cn("min-w-0 shrink-0 [contain-intrinsic-size:auto_10rem] [content-visibility:auto]", className),
480
+ ...props
481
+ }
482
+ );
483
+ }
484
+ );
485
+ MessageScrollerItem.displayName = "MessageScrollerItem";
486
+ const MessageScrollerButton = React__namespace.forwardRef(
487
+ ({ behavior = "smooth", direction = "end", className, children, onClick, tabIndex, type = "button", ...props }, ref) => {
488
+ const { scrollToEnd, scrollToStart } = messageScrollerHooks.useRequiredMessageScrollerActionsContext("MessageScrollerButton");
489
+ const { start, end } = messageScrollerHooks.useRequiredMessageScrollerScrollableContext("MessageScrollerButton");
490
+ const active = direction === "start" ? start : end;
491
+ return /* @__PURE__ */ jsxRuntime.jsx(
492
+ "button",
493
+ {
494
+ ref,
495
+ type,
496
+ ...props,
497
+ "data-slot": "message-scroller-button",
498
+ "data-active": active ? "true" : "false",
499
+ "data-direction": direction,
500
+ tabIndex: active ? tabIndex : -1,
501
+ className: utils_cn.cn(
502
+ "absolute inset-s-1/2 -translate-x-1/2 rounded-full border border-border1 bg-surface3 text-neutral6 transition-[translate,scale,opacity] duration-200 hover:bg-surface4 data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full rtl:translate-x-1/2 data-[direction=start]:[&_svg]:rotate-180",
503
+ className
504
+ ),
505
+ onClick: (event) => {
506
+ if (!active) return;
507
+ onClick?.(event);
508
+ if (event.defaultPrevented) return;
509
+ event.currentTarget.blur();
510
+ if (direction === "start") {
511
+ scrollToStart({ behavior });
512
+ } else {
513
+ scrollToEnd({ behavior });
514
+ }
515
+ },
516
+ children: children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
517
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownIcon, { className: "size-4", "aria-hidden": true }),
518
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: direction === "end" ? "Scroll to end" : "Scroll to start" })
519
+ ] })
520
+ }
521
+ );
522
+ }
523
+ );
524
+ MessageScrollerButton.displayName = "MessageScrollerButton";
525
+
526
+ exports.useMessageScroller = messageScrollerHooks.useMessageScroller;
527
+ exports.useMessageScrollerScrollable = messageScrollerHooks.useMessageScrollerScrollable;
528
+ exports.useMessageScrollerVisibility = messageScrollerHooks.useMessageScrollerVisibility;
529
+ exports.useOptionalMessageScroller = messageScrollerHooks.useOptionalMessageScroller;
530
+ exports.useOptionalMessageScrollerScrollable = messageScrollerHooks.useOptionalMessageScrollerScrollable;
531
+ exports.useOptionalMessageScrollerVisibility = messageScrollerHooks.useOptionalMessageScrollerVisibility;
532
+ exports.MessageScroller = MessageScroller;
533
+ exports.MessageScrollerButton = MessageScrollerButton;
534
+ exports.MessageScrollerContent = MessageScrollerContent;
535
+ exports.MessageScrollerItem = MessageScrollerItem;
536
+ exports.MessageScrollerProvider = MessageScrollerProvider;
537
+ exports.MessageScrollerViewport = MessageScrollerViewport;
538
+ //# sourceMappingURL=MessageScroller.cjs.js.map