@mastra/playground-ui 45.0.0 → 45.1.0-alpha.1

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 (59) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/dist/CommandPalette.css +135 -0
  3. package/dist/command-Duq1xsiS.js +168 -0
  4. package/dist/command-Duq1xsiS.js.map +1 -0
  5. package/dist/command-qsEykgml.cjs +197 -0
  6. package/dist/command-qsEykgml.cjs.map +1 -0
  7. package/dist/components/ChatShell.cjs.js +96 -0
  8. package/dist/components/ChatShell.cjs.js.map +1 -0
  9. package/dist/components/ChatShell.d.ts +2 -0
  10. package/dist/components/ChatShell.es.js +92 -0
  11. package/dist/components/ChatShell.es.js.map +1 -0
  12. package/dist/components/Command.cjs.js +10 -191
  13. package/dist/components/Command.cjs.js.map +1 -1
  14. package/dist/components/Command.es.js +1 -167
  15. package/dist/components/Command.es.js.map +1 -1
  16. package/dist/components/CommandPalette.cjs.js +180 -0
  17. package/dist/components/CommandPalette.cjs.js.map +1 -0
  18. package/dist/components/CommandPalette.d.ts +2 -0
  19. package/dist/components/CommandPalette.es.js +169 -0
  20. package/dist/components/CommandPalette.es.js.map +1 -0
  21. package/dist/components/Kbd.cjs.js +3 -24
  22. package/dist/components/Kbd.cjs.js.map +1 -1
  23. package/dist/components/Kbd.es.js +1 -26
  24. package/dist/components/Kbd.es.js.map +1 -1
  25. package/dist/components/MessageScroller.cjs.js +8 -524
  26. package/dist/components/MessageScroller.cjs.js.map +1 -1
  27. package/dist/components/MessageScroller.es.js +2 -504
  28. package/dist/components/MessageScroller.es.js.map +1 -1
  29. package/dist/components/ThreadRail.cjs.js +1 -1
  30. package/dist/components/ThreadRail.es.js +1 -1
  31. package/dist/kbd-Br7kdwaB.cjs +34 -0
  32. package/dist/kbd-Br7kdwaB.cjs.map +1 -0
  33. package/dist/kbd-DE0nj4DL.js +32 -0
  34. package/dist/kbd-DE0nj4DL.js.map +1 -0
  35. package/dist/message-scroller-BQpwFsqj.js +563 -0
  36. package/dist/message-scroller-BQpwFsqj.js.map +1 -0
  37. package/dist/message-scroller-DIgCd5n3.cjs +589 -0
  38. package/dist/message-scroller-DIgCd5n3.cjs.map +1 -0
  39. package/dist/{message-scroller-hooks-DdXqrmC2.cjs → message-scroller-context-_VfCYFBU.cjs} +6 -48
  40. package/dist/message-scroller-context-_VfCYFBU.cjs.map +1 -0
  41. package/dist/message-scroller-context-tTA6jxho.js +23 -0
  42. package/dist/message-scroller-context-tTA6jxho.js.map +1 -0
  43. package/dist/message-scroller-hooks-BxAf8k0Y.cjs +72 -0
  44. package/dist/message-scroller-hooks-BxAf8k0Y.cjs.map +1 -0
  45. package/dist/{message-scroller-hooks-CdsHnth6.js → message-scroller-hooks-D5yxCon2.js} +3 -19
  46. package/dist/message-scroller-hooks-D5yxCon2.js.map +1 -0
  47. package/dist/src/ds/components/ChatShell/chat-shell.d.ts +46 -0
  48. package/dist/src/ds/components/ChatShell/chat-shell.stories.d.ts +8 -0
  49. package/dist/src/ds/components/ChatShell/index.d.ts +11 -0
  50. package/dist/src/ds/components/CommandPalette/command-palette.d.ts +37 -0
  51. package/dist/src/ds/components/CommandPalette/command-palette.stories.d.ts +12 -0
  52. package/dist/src/ds/components/CommandPalette/index.d.ts +1 -0
  53. package/dist/src/ds/components/Kbd/kbd.d.ts +2 -1
  54. package/dist/src/ds/components/MessageScroller/message-scroller-context.d.ts +4 -0
  55. package/dist/src/ds/components/MessageScroller/message-scroller.d.ts +8 -7
  56. package/dist/style.css +343 -2
  57. package/package.json +6 -6
  58. package/dist/message-scroller-hooks-CdsHnth6.js.map +0 -1
  59. package/dist/message-scroller-hooks-DdXqrmC2.cjs.map +0 -1
@@ -0,0 +1,589 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+ const lucideReact = require('lucide-react');
5
+ const React = require('react');
6
+ const messageScrollerContext = require('./message-scroller-context-_VfCYFBU.cjs');
7
+ const utils_cn = require('./cn-vA5sWo9W.cjs');
8
+
9
+ function _interopNamespaceDefault(e) {
10
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
11
+ if (e) {
12
+ for (const k in e) {
13
+ if (k !== 'default') {
14
+ const d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: () => e[k]
18
+ });
19
+ }
20
+ }
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ const React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
27
+
28
+ const VISIBILITY_EPSILON = 0.5;
29
+ const mergeRefs = (...refs) => (element) => {
30
+ refs.forEach((ref) => {
31
+ if (!ref) return;
32
+ if (typeof ref === "function") {
33
+ ref(element);
34
+ return;
35
+ }
36
+ ref.current = element;
37
+ });
38
+ };
39
+ const scrollableMatches = (left, right) => left.start === right.start && left.end === right.end;
40
+ const visibilityMatches = (left, right) => left.currentAnchorId === right.currentAnchorId && left.visibleMessageIds.length === right.visibleMessageIds.length && left.visibleMessageIds.every((messageId, index) => messageId === right.visibleMessageIds[index]);
41
+ const getContentPadding = (contentElement) => {
42
+ if (!contentElement) return { start: 0, end: 0 };
43
+ const styles = window.getComputedStyle(contentElement);
44
+ return {
45
+ start: Number.parseFloat(styles.paddingBlockStart || styles.paddingTop || "0") || 0,
46
+ end: Number.parseFloat(styles.paddingBlockEnd || styles.paddingBottom || "0") || 0
47
+ };
48
+ };
49
+ const getRelativeTop = (element, viewportElement) => {
50
+ const elementRect = element.getBoundingClientRect();
51
+ const viewportRect = viewportElement.getBoundingClientRect();
52
+ return elementRect.top - viewportRect.top + viewportElement.scrollTop;
53
+ };
54
+ const scrollViewportTo = (viewportElement, top, behavior) => {
55
+ if (typeof viewportElement.scrollTo === "function") {
56
+ viewportElement.scrollTo({ top, behavior });
57
+ return;
58
+ }
59
+ viewportElement.scrollTop = top;
60
+ };
61
+ const scheduleScrollSync = (callback) => {
62
+ if (typeof window.requestAnimationFrame === "function") {
63
+ window.requestAnimationFrame(callback);
64
+ return;
65
+ }
66
+ window.setTimeout(callback, 0);
67
+ };
68
+ const getScrollTarget = ({
69
+ align,
70
+ element,
71
+ scrollMargin,
72
+ viewportElement
73
+ }) => {
74
+ const contentPadding = getContentPadding(element.parentElement);
75
+ const elementTop = getRelativeTop(element, viewportElement);
76
+ const elementHeight = element.getBoundingClientRect().height;
77
+ const visibleHeight = Math.max(0, viewportElement.clientHeight - contentPadding.start - contentPadding.end);
78
+ if (align === "center") return elementTop - contentPadding.start - (visibleHeight - elementHeight) / 2 - scrollMargin;
79
+ if (align === "end")
80
+ return elementTop - viewportElement.clientHeight + elementHeight + contentPadding.end + scrollMargin;
81
+ if (align === "nearest") {
82
+ const elementBottom = elementTop + elementHeight;
83
+ const viewportTop = viewportElement.scrollTop + contentPadding.start;
84
+ const viewportBottom = viewportElement.scrollTop + viewportElement.clientHeight - contentPadding.end;
85
+ if (elementTop >= viewportTop && elementBottom <= viewportBottom) return viewportElement.scrollTop;
86
+ return elementTop < viewportTop ? elementTop - contentPadding.start - scrollMargin : elementBottom - viewportElement.clientHeight + contentPadding.end + scrollMargin;
87
+ }
88
+ return elementTop - contentPadding.start - scrollMargin;
89
+ };
90
+ const getCurrentAnchorId = ({
91
+ fallbackAnchorId,
92
+ items,
93
+ scrollMargin,
94
+ scrollPreviousItemPeek,
95
+ visibleMessageIds,
96
+ viewportElement
97
+ }) => {
98
+ const anchorLine = viewportElement.getBoundingClientRect().top + scrollMargin + scrollPreviousItemPeek;
99
+ const anchors = items.filter(([, item]) => item.scrollAnchor);
100
+ let anchoredAboveViewport;
101
+ for (const [messageId, item] of anchors) {
102
+ if (item.element.getBoundingClientRect().top <= anchorLine + VISIBILITY_EPSILON) {
103
+ anchoredAboveViewport = messageId;
104
+ }
105
+ }
106
+ if (anchoredAboveViewport) return anchoredAboveViewport;
107
+ return anchors.find(([messageId]) => visibleMessageIds.has(messageId))?.[0] ?? fallbackAnchorId;
108
+ };
109
+ function MessageScrollerProvider({
110
+ autoScroll = false,
111
+ children,
112
+ defaultScrollPosition = "end",
113
+ onReachStart,
114
+ preserveScrollOnPrepend = false,
115
+ reachStartThreshold = messageScrollerContext.DEFAULT_REACH_START_THRESHOLD,
116
+ scrollEdgeThreshold = messageScrollerContext.DEFAULT_SCROLL_EDGE_THRESHOLD,
117
+ scrollMargin = messageScrollerContext.DEFAULT_SCROLL_MARGIN,
118
+ scrollPreviousItemPeek = messageScrollerContext.DEFAULT_SCROLL_PREVIOUS_ITEM_PEEK
119
+ }) {
120
+ const itemsRef = React__namespace.useRef(null);
121
+ itemsRef.current ??= /* @__PURE__ */ new Map();
122
+ const itemsRegistry = itemsRef.current;
123
+ const visibleMessageIdsRef = React__namespace.useRef(null);
124
+ visibleMessageIdsRef.current ??= /* @__PURE__ */ new Set();
125
+ const intersectingMessageIds = visibleMessageIdsRef.current;
126
+ const [itemsVersion, setItemsVersion] = React__namespace.useState(0);
127
+ const [rootElement, setRootElement] = React__namespace.useState(null);
128
+ const [viewportElement, setViewportElement] = React__namespace.useState(null);
129
+ const [contentElement, setContentElement] = React__namespace.useState(null);
130
+ const defaultScrollAppliedRef = React__namespace.useRef(false);
131
+ const [scrollable, setScrollable] = React__namespace.useState(messageScrollerContext.DEFAULT_SCROLLABLE);
132
+ const [visibility, setVisibility] = React__namespace.useState(messageScrollerContext.DEFAULT_VISIBILITY);
133
+ const atEndRef = React__namespace.useRef(true);
134
+ const reachStartArmedRef = React__namespace.useRef(false);
135
+ const reachStartFiredRef = React__namespace.useRef(false);
136
+ const prependAnchorRef = React__namespace.useRef(null);
137
+ const firstItemIdRef = React__namespace.useRef(void 0);
138
+ const onReachStartRef = React__namespace.useRef(onReachStart);
139
+ React__namespace.useEffect(() => {
140
+ onReachStartRef.current = onReachStart;
141
+ }, [onReachStart]);
142
+ const publishScrollable = React__namespace.useCallback(
143
+ (nextScrollable) => {
144
+ const scrollableValue = [nextScrollable.start && "start", nextScrollable.end && "end"].filter(Boolean).join(" ");
145
+ for (const element of [rootElement, viewportElement]) {
146
+ if (!element) continue;
147
+ if (scrollableValue) {
148
+ element.setAttribute("data-scrollable", scrollableValue);
149
+ } else {
150
+ element.removeAttribute("data-scrollable");
151
+ }
152
+ }
153
+ setScrollable((current) => scrollableMatches(current, nextScrollable) ? current : nextScrollable);
154
+ },
155
+ [rootElement, viewportElement]
156
+ );
157
+ const publishVisibility = React__namespace.useCallback((nextVisibility) => {
158
+ setVisibility((current) => visibilityMatches(current, nextVisibility) ? current : nextVisibility);
159
+ }, []);
160
+ const updateScrollable = React__namespace.useCallback(() => {
161
+ if (!viewportElement) {
162
+ atEndRef.current = true;
163
+ publishScrollable(messageScrollerContext.DEFAULT_SCROLLABLE);
164
+ return;
165
+ }
166
+ const remainingScroll = viewportElement.scrollHeight - viewportElement.scrollTop - viewportElement.clientHeight;
167
+ atEndRef.current = remainingScroll < messageScrollerContext.AUTO_SCROLL_ATTACH_THRESHOLD;
168
+ publishScrollable({
169
+ start: viewportElement.scrollTop > scrollEdgeThreshold,
170
+ end: remainingScroll > scrollEdgeThreshold
171
+ });
172
+ }, [publishScrollable, scrollEdgeThreshold, viewportElement]);
173
+ const updateVisibility = React__namespace.useCallback(() => {
174
+ const items = Array.from(itemsRegistry.entries());
175
+ const fallbackAnchorId = items.filter(([, item]) => item.scrollAnchor).at(-1)?.[0] ?? items.at(-1)?.[0];
176
+ if (items.length === 0) {
177
+ publishVisibility(messageScrollerContext.DEFAULT_VISIBILITY);
178
+ return;
179
+ }
180
+ if (!viewportElement) {
181
+ publishVisibility({
182
+ currentAnchorId: fallbackAnchorId,
183
+ visibleMessageIds: fallbackAnchorId ? [fallbackAnchorId] : []
184
+ });
185
+ return;
186
+ }
187
+ const viewportRect = viewportElement.getBoundingClientRect();
188
+ const visibleMessageIds = /* @__PURE__ */ new Set();
189
+ if (typeof IntersectionObserver === "undefined") {
190
+ const visibilityTop = viewportRect.top + scrollMargin + scrollPreviousItemPeek;
191
+ items.forEach(([messageId, item]) => {
192
+ const rect = item.element.getBoundingClientRect();
193
+ if (rect.bottom > visibilityTop && rect.top < viewportRect.bottom) visibleMessageIds.add(messageId);
194
+ });
195
+ } else {
196
+ intersectingMessageIds.forEach((messageId) => visibleMessageIds.add(messageId));
197
+ }
198
+ const orderedVisibleMessageIds = items.flatMap(
199
+ ([messageId]) => visibleMessageIds.has(messageId) ? [messageId] : []
200
+ );
201
+ publishVisibility({
202
+ currentAnchorId: getCurrentAnchorId({
203
+ fallbackAnchorId,
204
+ items,
205
+ scrollMargin,
206
+ scrollPreviousItemPeek,
207
+ visibleMessageIds,
208
+ viewportElement
209
+ }),
210
+ visibleMessageIds: orderedVisibleMessageIds.length > 0 ? orderedVisibleMessageIds : fallbackAnchorId ? [fallbackAnchorId] : []
211
+ });
212
+ }, [intersectingMessageIds, itemsRegistry, publishVisibility, scrollMargin, scrollPreviousItemPeek, viewportElement]);
213
+ const syncAfterScroll = React__namespace.useCallback(() => {
214
+ updateScrollable();
215
+ updateVisibility();
216
+ }, [updateScrollable, updateVisibility]);
217
+ const notifyScroll = React__namespace.useCallback(() => {
218
+ const wasScrollable = Boolean(viewportElement && viewportElement.scrollHeight > viewportElement.clientHeight);
219
+ syncAfterScroll();
220
+ if (!viewportElement) return;
221
+ if (atEndRef.current && wasScrollable) reachStartArmedRef.current = true;
222
+ if (!reachStartArmedRef.current) return;
223
+ if (!wasScrollable) return;
224
+ if (viewportElement.scrollTop > reachStartThreshold) {
225
+ reachStartFiredRef.current = false;
226
+ return;
227
+ }
228
+ if (reachStartFiredRef.current) return;
229
+ if (!onReachStartRef.current) return;
230
+ reachStartFiredRef.current = true;
231
+ if (preserveScrollOnPrepend) {
232
+ prependAnchorRef.current = {
233
+ scrollHeight: viewportElement.scrollHeight,
234
+ scrollTop: viewportElement.scrollTop
235
+ };
236
+ }
237
+ onReachStartRef.current();
238
+ }, [preserveScrollOnPrepend, reachStartThreshold, syncAfterScroll, viewportElement]);
239
+ const notifyContentResize = React__namespace.useCallback(() => {
240
+ const followEnd = autoScroll && defaultScrollAppliedRef.current && atEndRef.current && viewportElement;
241
+ if (followEnd) {
242
+ scrollViewportTo(
243
+ viewportElement,
244
+ Math.max(0, viewportElement.scrollHeight - viewportElement.clientHeight),
245
+ "auto"
246
+ );
247
+ }
248
+ syncAfterScroll();
249
+ }, [autoScroll, syncAfterScroll, viewportElement]);
250
+ const scrollToElement = React__namespace.useCallback(
251
+ (element, {
252
+ align = "start",
253
+ behavior = "auto",
254
+ scrollMargin: optionScrollMargin = scrollMargin
255
+ } = {}) => {
256
+ if (!viewportElement || !contentElement?.contains(element)) return false;
257
+ const nextScrollTop = Math.max(
258
+ 0,
259
+ getScrollTarget({ align, element, scrollMargin: optionScrollMargin, viewportElement })
260
+ );
261
+ if (Math.abs(viewportElement.scrollTop - nextScrollTop) <= VISIBILITY_EPSILON) {
262
+ scrollViewportTo(viewportElement, nextScrollTop, "auto");
263
+ syncAfterScroll();
264
+ return true;
265
+ }
266
+ scrollViewportTo(viewportElement, nextScrollTop, behavior);
267
+ scheduleScrollSync(syncAfterScroll);
268
+ return true;
269
+ },
270
+ [contentElement, scrollMargin, syncAfterScroll, viewportElement]
271
+ );
272
+ const scrollToStart = React__namespace.useCallback(
273
+ ({ behavior = "auto" } = {}) => {
274
+ if (!viewportElement) return false;
275
+ scrollViewportTo(viewportElement, 0, behavior);
276
+ scheduleScrollSync(syncAfterScroll);
277
+ return true;
278
+ },
279
+ [syncAfterScroll, viewportElement]
280
+ );
281
+ const scrollToEnd = React__namespace.useCallback(
282
+ ({ behavior = "auto" } = {}) => {
283
+ if (!viewportElement) return false;
284
+ scrollViewportTo(
285
+ viewportElement,
286
+ Math.max(0, viewportElement.scrollHeight - viewportElement.clientHeight),
287
+ behavior
288
+ );
289
+ scheduleScrollSync(syncAfterScroll);
290
+ return true;
291
+ },
292
+ [syncAfterScroll, viewportElement]
293
+ );
294
+ const scrollToMessage = React__namespace.useCallback(
295
+ (messageId, options) => {
296
+ const item = itemsRegistry.get(messageId);
297
+ if (!item) return false;
298
+ return scrollToElement(item.element, options);
299
+ },
300
+ [itemsRegistry, scrollToElement]
301
+ );
302
+ const registerItem = React__namespace.useCallback(
303
+ (messageId, element, scrollAnchor) => {
304
+ itemsRegistry.set(messageId, { element, scrollAnchor });
305
+ setItemsVersion((version) => version + 1);
306
+ return () => {
307
+ const current = itemsRegistry.get(messageId);
308
+ if (current?.element !== element) return;
309
+ itemsRegistry.delete(messageId);
310
+ intersectingMessageIds.delete(messageId);
311
+ setItemsVersion((version) => version + 1);
312
+ };
313
+ },
314
+ [intersectingMessageIds, itemsRegistry]
315
+ );
316
+ React__namespace.useEffect(() => {
317
+ if (!contentElement || !viewportElement || typeof IntersectionObserver === "undefined") {
318
+ updateVisibility();
319
+ return void 0;
320
+ }
321
+ const messageIdByElement = /* @__PURE__ */ new Map();
322
+ for (const [messageId, item] of itemsRegistry) {
323
+ messageIdByElement.set(item.element, messageId);
324
+ }
325
+ const observer = new IntersectionObserver(
326
+ (entries) => {
327
+ for (const entry of entries) {
328
+ const messageId = messageIdByElement.get(entry.target);
329
+ if (!messageId) continue;
330
+ if (entry.isIntersecting) {
331
+ intersectingMessageIds.add(messageId);
332
+ } else {
333
+ intersectingMessageIds.delete(messageId);
334
+ }
335
+ }
336
+ updateVisibility();
337
+ },
338
+ {
339
+ root: viewportElement,
340
+ rootMargin: `${-(scrollMargin + scrollPreviousItemPeek)}px 0px 0px 0px`,
341
+ threshold: [0, 0.01, 0.5, 1]
342
+ }
343
+ );
344
+ for (const [, item] of itemsRegistry) observer.observe(item.element);
345
+ updateVisibility();
346
+ return () => observer.disconnect();
347
+ }, [
348
+ contentElement,
349
+ intersectingMessageIds,
350
+ itemsRegistry,
351
+ itemsVersion,
352
+ scrollMargin,
353
+ scrollPreviousItemPeek,
354
+ updateVisibility,
355
+ viewportElement
356
+ ]);
357
+ React__namespace.useEffect(() => {
358
+ if (!viewportElement || typeof ResizeObserver === "undefined") return void 0;
359
+ const observer = new ResizeObserver(syncAfterScroll);
360
+ observer.observe(viewportElement);
361
+ return () => observer.disconnect();
362
+ }, [syncAfterScroll, viewportElement]);
363
+ React__namespace.useLayoutEffect(() => {
364
+ updateScrollable();
365
+ updateVisibility();
366
+ }, [itemsVersion, updateScrollable, updateVisibility]);
367
+ React__namespace.useLayoutEffect(() => {
368
+ if (defaultScrollAppliedRef.current || !viewportElement || itemsRegistry.size === 0) return;
369
+ let didScroll = false;
370
+ if (defaultScrollPosition === "start") {
371
+ didScroll = scrollToStart({ behavior: "auto" });
372
+ } else if (defaultScrollPosition === "last-anchor") {
373
+ const lastAnchorId = Array.from(itemsRegistry.entries()).filter(([, item]) => item.scrollAnchor).at(-1)?.[0];
374
+ didScroll = lastAnchorId ? scrollToMessage(lastAnchorId, { align: "start", behavior: "auto" }) : scrollToEnd({ behavior: "auto" });
375
+ } else {
376
+ didScroll = scrollToEnd({ behavior: "auto" });
377
+ }
378
+ if (didScroll) defaultScrollAppliedRef.current = true;
379
+ }, [
380
+ defaultScrollPosition,
381
+ itemsRegistry,
382
+ itemsVersion,
383
+ scrollToEnd,
384
+ scrollToMessage,
385
+ scrollToStart,
386
+ viewportElement
387
+ ]);
388
+ React__namespace.useLayoutEffect(() => {
389
+ const previousFirstItemId = firstItemIdRef.current;
390
+ const firstItemId = contentElement?.querySelector('[data-slot="message-scroller-item"]')?.dataset.messageId;
391
+ firstItemIdRef.current = firstItemId;
392
+ const anchor = prependAnchorRef.current;
393
+ prependAnchorRef.current = null;
394
+ if (!anchor || !viewportElement || firstItemId === previousFirstItemId) return;
395
+ reachStartFiredRef.current = false;
396
+ const grownBy = viewportElement.scrollHeight - anchor.scrollHeight;
397
+ if (grownBy > 0) viewportElement.scrollTop = anchor.scrollTop + grownBy;
398
+ }, [contentElement, itemsVersion, viewportElement]);
399
+ React__namespace.useLayoutEffect(() => {
400
+ if (!autoScroll || !defaultScrollAppliedRef.current || !atEndRef.current) return;
401
+ scrollToEnd({ behavior: "auto" });
402
+ }, [autoScroll, itemsVersion, scrollToEnd]);
403
+ const actionsContextValue = React__namespace.useMemo(
404
+ () => ({
405
+ notifyContentResize,
406
+ notifyScroll,
407
+ registerItem,
408
+ scrollToEnd,
409
+ scrollToMessage,
410
+ scrollToStart,
411
+ setContentElement,
412
+ setRootElement,
413
+ setViewportElement,
414
+ syncAfterScroll
415
+ }),
416
+ [notifyContentResize, notifyScroll, registerItem, scrollToEnd, scrollToMessage, scrollToStart, syncAfterScroll]
417
+ );
418
+ const scrollableContextValue = React__namespace.useMemo(
419
+ () => ({
420
+ end: scrollable.end,
421
+ start: scrollable.start
422
+ }),
423
+ [scrollable.end, scrollable.start]
424
+ );
425
+ const visibilityContextValue = React__namespace.useMemo(
426
+ () => ({
427
+ currentAnchorId: visibility.currentAnchorId,
428
+ visibleMessageIds: visibility.visibleMessageIds
429
+ }),
430
+ [visibility.currentAnchorId, visibility.visibleMessageIds]
431
+ );
432
+ return /* @__PURE__ */ jsxRuntime.jsx(messageScrollerContext.MessageScrollerActionsContext.Provider, { value: actionsContextValue, children: /* @__PURE__ */ jsxRuntime.jsx(messageScrollerContext.MessageScrollerScrollableContext.Provider, { value: scrollableContextValue, children: /* @__PURE__ */ jsxRuntime.jsx(messageScrollerContext.MessageScrollerVisibilityContext.Provider, { value: visibilityContextValue, children }) }) });
433
+ }
434
+ const MessageScroller = React__namespace.forwardRef(
435
+ ({ className, ...props }, ref) => {
436
+ const { setRootElement } = messageScrollerContext.useRequiredMessageScrollerActionsContext("MessageScroller");
437
+ return /* @__PURE__ */ jsxRuntime.jsx(
438
+ "div",
439
+ {
440
+ ref: mergeRefs(setRootElement, ref),
441
+ "data-slot": "message-scroller",
442
+ className: utils_cn.cn("group/message-scroller relative flex size-full min-h-0 flex-col overflow-hidden", className),
443
+ ...props
444
+ }
445
+ );
446
+ }
447
+ );
448
+ MessageScroller.displayName = "MessageScroller";
449
+ const MessageScrollerViewport = React__namespace.forwardRef(
450
+ ({ className, onScroll, role, tabIndex, ...props }, ref) => {
451
+ const { setViewportElement, notifyScroll } = messageScrollerContext.useRequiredMessageScrollerActionsContext("MessageScrollerViewport");
452
+ const viewportRef = React__namespace.useMemo(() => mergeRefs(setViewportElement, ref), [ref, setViewportElement]);
453
+ return /* @__PURE__ */ jsxRuntime.jsx(
454
+ "div",
455
+ {
456
+ ref: viewportRef,
457
+ role: role ?? "region",
458
+ tabIndex: tabIndex ?? 0,
459
+ "data-slot": "message-scroller-viewport",
460
+ className: utils_cn.cn(
461
+ "size-full min-h-0 min-w-0 overflow-y-auto overscroll-contain data-autoscrolling:scrollbar-thumb-transparent data-autoscrolling:scrollbar-track-transparent",
462
+ className
463
+ ),
464
+ onScroll: (event) => {
465
+ notifyScroll();
466
+ onScroll?.(event);
467
+ },
468
+ ...props
469
+ }
470
+ );
471
+ }
472
+ );
473
+ MessageScrollerViewport.displayName = "MessageScrollerViewport";
474
+ const MessageScrollerContent = React__namespace.forwardRef(
475
+ ({ children, className, spacerClassName, role, "aria-relevant": ariaRelevant = "additions", ...props }, ref) => {
476
+ const { setContentElement, notifyContentResize, syncAfterScroll } = messageScrollerContext.useRequiredMessageScrollerActionsContext("MessageScrollerContent");
477
+ const [contentElement, setLocalContentElement] = React__namespace.useState(null);
478
+ const contentRef = React__namespace.useMemo(
479
+ () => mergeRefs(setContentElement, setLocalContentElement, ref),
480
+ [ref, setContentElement]
481
+ );
482
+ React__namespace.useLayoutEffect(() => {
483
+ syncAfterScroll();
484
+ }, [syncAfterScroll]);
485
+ React__namespace.useEffect(() => {
486
+ if (!contentElement || typeof MutationObserver === "undefined") return void 0;
487
+ const observer = new MutationObserver(notifyContentResize);
488
+ observer.observe(contentElement, { childList: true, subtree: false });
489
+ return () => observer.disconnect();
490
+ }, [contentElement, notifyContentResize]);
491
+ React__namespace.useEffect(() => {
492
+ if (!contentElement || typeof ResizeObserver === "undefined") return void 0;
493
+ const observer = new ResizeObserver(notifyContentResize);
494
+ observer.observe(contentElement);
495
+ return () => observer.disconnect();
496
+ }, [contentElement, notifyContentResize]);
497
+ return /* @__PURE__ */ jsxRuntime.jsxs(
498
+ "div",
499
+ {
500
+ ref: contentRef,
501
+ role: role ?? "log",
502
+ "aria-relevant": ariaRelevant,
503
+ "data-slot": "message-scroller-content",
504
+ className: utils_cn.cn("flex h-max min-h-full flex-col gap-6", className),
505
+ ...props,
506
+ children: [
507
+ children,
508
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", "data-message-scroller-spacer": "", hidden: true, className: spacerClassName })
509
+ ]
510
+ }
511
+ );
512
+ }
513
+ );
514
+ MessageScrollerContent.displayName = "MessageScrollerContent";
515
+ const MessageScrollerItem = React__namespace.forwardRef(
516
+ ({ className, messageId, scrollAnchor = false, ...props }, ref) => {
517
+ const registerItem = React__namespace.useContext(messageScrollerContext.MessageScrollerActionsContext)?.registerItem;
518
+ const unregisterRef = React__namespace.useRef(void 0);
519
+ const itemRef = React__namespace.useCallback(
520
+ (element) => {
521
+ unregisterRef.current?.();
522
+ unregisterRef.current = void 0;
523
+ mergeRefs(ref)(element);
524
+ if (!element || !messageId || !registerItem) return;
525
+ unregisterRef.current = registerItem(messageId, element, scrollAnchor);
526
+ },
527
+ [messageId, ref, registerItem, scrollAnchor]
528
+ );
529
+ return /* @__PURE__ */ jsxRuntime.jsx(
530
+ "div",
531
+ {
532
+ ref: itemRef,
533
+ "data-slot": "message-scroller-item",
534
+ "data-message-id": messageId,
535
+ "data-scroll-anchor": scrollAnchor ? "true" : "false",
536
+ className: utils_cn.cn("min-w-0 shrink-0 [contain-intrinsic-size:auto_10rem] [content-visibility:auto]", className),
537
+ ...props
538
+ }
539
+ );
540
+ }
541
+ );
542
+ MessageScrollerItem.displayName = "MessageScrollerItem";
543
+ const MessageScrollerButton = React__namespace.forwardRef(
544
+ ({ behavior = "smooth", direction = "end", className, children, onClick, tabIndex, type = "button", ...props }, ref) => {
545
+ const { scrollToEnd, scrollToStart } = messageScrollerContext.useRequiredMessageScrollerActionsContext("MessageScrollerButton");
546
+ const { start, end } = messageScrollerContext.useRequiredMessageScrollerScrollableContext("MessageScrollerButton");
547
+ const active = direction === "start" ? start : end;
548
+ return /* @__PURE__ */ jsxRuntime.jsx(
549
+ "button",
550
+ {
551
+ ref,
552
+ type,
553
+ ...props,
554
+ "data-slot": "message-scroller-button",
555
+ "data-active": active ? "true" : "false",
556
+ "data-direction": direction,
557
+ tabIndex: active ? tabIndex : -1,
558
+ className: utils_cn.cn(
559
+ "absolute inset-s-1/2 inline-flex min-h-5 min-w-7 -translate-x-1/2 items-center justify-center rounded-full border border-border1 bg-surface3 text-neutral6 shadow-[0_1px_2px_-1px_oklch(0%_0_0deg/10%),0_8px_20px_-12px_oklch(0%_0_0deg/25%)] 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",
560
+ className
561
+ ),
562
+ onClick: (event) => {
563
+ if (!active) return;
564
+ onClick?.(event);
565
+ if (event.defaultPrevented) return;
566
+ event.currentTarget.blur();
567
+ if (direction === "start") {
568
+ scrollToStart({ behavior });
569
+ } else {
570
+ scrollToEnd({ behavior });
571
+ }
572
+ },
573
+ children: children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
574
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownIcon, { className: "size-4", "aria-hidden": true }),
575
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: direction === "end" ? "Scroll to end" : "Scroll to start" })
576
+ ] })
577
+ }
578
+ );
579
+ }
580
+ );
581
+ MessageScrollerButton.displayName = "MessageScrollerButton";
582
+
583
+ exports.MessageScroller = MessageScroller;
584
+ exports.MessageScrollerButton = MessageScrollerButton;
585
+ exports.MessageScrollerContent = MessageScrollerContent;
586
+ exports.MessageScrollerItem = MessageScrollerItem;
587
+ exports.MessageScrollerProvider = MessageScrollerProvider;
588
+ exports.MessageScrollerViewport = MessageScrollerViewport;
589
+ //# sourceMappingURL=message-scroller-DIgCd5n3.cjs.map