@djangocfg/ui-tools 2.1.368 → 2.1.371

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.
@@ -0,0 +1,5 @@
1
+ export { ChatRoot } from './chunk-MVAT6OPZ.mjs';
2
+ import './chunk-NWUT327A.mjs';
3
+ import './chunk-N2XQF2OL.mjs';
4
+ //# sourceMappingURL=ChatRoot-DYMCNGOB.mjs.map
5
+ //# sourceMappingURL=ChatRoot-DYMCNGOB.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"ChatRoot-6U7633X3.mjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"ChatRoot-DYMCNGOB.mjs"}
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ var chunk2SKR4U5S_cjs = require('./chunk-2SKR4U5S.cjs');
4
+ require('./chunk-XACCHZH2.cjs');
5
+ require('./chunk-OLISEQHS.cjs');
6
+
7
+
8
+
9
+ Object.defineProperty(exports, "ChatRoot", {
10
+ enumerable: true,
11
+ get: function () { return chunk2SKR4U5S_cjs.ChatRoot; }
12
+ });
13
+ //# sourceMappingURL=ChatRoot-HOQ37WRE.cjs.map
14
+ //# sourceMappingURL=ChatRoot-HOQ37WRE.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"ChatRoot-HARTIAJ5.cjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"ChatRoot-HOQ37WRE.cjs"}
@@ -11,6 +11,7 @@ var middleware = require('zustand/middleware');
11
11
  var jsxRuntime = require('react/jsx-runtime');
12
12
  var lucideReact = require('lucide-react');
13
13
  var components = require('@djangocfg/ui-core/components');
14
+ var reactVirtuoso = require('react-virtuoso');
14
15
 
15
16
  // src/tools/Chat/types.ts
16
17
  var DEFAULT_LABELS = {
@@ -694,13 +695,21 @@ function useChat(config) {
694
695
  };
695
696
  dispatch({ type: "MESSAGE_USER_ADD", message: userMsg });
696
697
  config.onMessageSent?.(userMsg);
698
+ let outbound = content;
699
+ if (config.onBeforeSend) {
700
+ try {
701
+ outbound = await config.onBeforeSend(content);
702
+ } catch (err) {
703
+ log.error.error("onBeforeSend threw \u2014 falling back to original content", err);
704
+ }
705
+ }
697
706
  if (streaming) {
698
- await consumeStream(sessionId, content, attachments);
707
+ await consumeStream(sessionId, outbound, attachments);
699
708
  } else {
700
- await consumeBuffered(sessionId, content, attachments);
709
+ await consumeBuffered(sessionId, outbound, attachments);
701
710
  }
702
711
  },
703
- [streaming, consumeStream, consumeBuffered, config, awaitSession]
712
+ [streaming, consumeStream, consumeBuffered, config, awaitSession, log]
704
713
  );
705
714
  const cancelStream = react.useCallback(() => {
706
715
  abortRef.current?.abort();
@@ -1200,6 +1209,7 @@ function ChatProvider({
1200
1209
  streaming,
1201
1210
  audio,
1202
1211
  debug,
1212
+ onBeforeSend,
1203
1213
  children
1204
1214
  }) {
1205
1215
  const audioApi = useChatAudio(audio ?? {});
@@ -1223,7 +1233,8 @@ function ChatProvider({
1223
1233
  onMessageSent,
1224
1234
  onMessageEnd,
1225
1235
  onStreamStart,
1226
- onError
1236
+ onError,
1237
+ onBeforeSend
1227
1238
  });
1228
1239
  const layout = useChatLayout({ defaultMode: "embedded" });
1229
1240
  const rootRef = react.useRef(null);
@@ -1279,9 +1290,28 @@ function useChatComposer(options) {
1279
1290
  disabled = false,
1280
1291
  submitOn = "enter",
1281
1292
  history = { enabled: true, size: LIMITS.composerHistorySize },
1282
- onPasteFiles
1293
+ onPasteFiles,
1294
+ persistKey
1283
1295
  } = options;
1284
- const [value, setValueState] = react.useState(initialValue);
1296
+ const initialFromStorage = (() => {
1297
+ if (!persistKey || typeof window === "undefined") return initialValue;
1298
+ try {
1299
+ const stored = window.sessionStorage.getItem(`chat:draft:${persistKey}`);
1300
+ return stored && stored.length > 0 ? stored : initialValue;
1301
+ } catch {
1302
+ return initialValue;
1303
+ }
1304
+ })();
1305
+ const [value, setValueState] = react.useState(initialFromStorage);
1306
+ react.useEffect(() => {
1307
+ if (!persistKey || typeof window === "undefined") return;
1308
+ try {
1309
+ const k = `chat:draft:${persistKey}`;
1310
+ if (value.length > 0) window.sessionStorage.setItem(k, value);
1311
+ else window.sessionStorage.removeItem(k);
1312
+ } catch {
1313
+ }
1314
+ }, [value, persistKey]);
1285
1315
  const [attachments, setAttachments] = react.useState([]);
1286
1316
  const [isSubmitting, setIsSubmitting] = react.useState(false);
1287
1317
  const textareaRef = react.useRef(null);
@@ -1426,138 +1456,6 @@ function useChatComposer(options) {
1426
1456
  };
1427
1457
  }
1428
1458
  chunkOLISEQHS_cjs.__name(useChatComposer, "useChatComposer");
1429
- function useChatScroll(options) {
1430
- const {
1431
- containerRef,
1432
- bottomRef,
1433
- isStreaming = false,
1434
- bottomThresholdPx = 80,
1435
- messagesCount = 0
1436
- } = options;
1437
- const [isAtBottom, setIsAtBottom] = react.useState(true);
1438
- const [unreadCount, setUnreadCount] = react.useState(0);
1439
- const lastCountRef = react.useRef(messagesCount);
1440
- const stickyRef = react.useRef(true);
1441
- const wasStreamingRef = react.useRef(isStreaming);
1442
- const scrollToBottom = react.useCallback(
1443
- (smooth = false) => {
1444
- const el = containerRef.current;
1445
- if (!el) return;
1446
- el.scrollTo({
1447
- top: el.scrollHeight,
1448
- behavior: smooth ? "smooth" : "auto"
1449
- });
1450
- stickyRef.current = true;
1451
- setIsAtBottom(true);
1452
- setUnreadCount(0);
1453
- },
1454
- [containerRef]
1455
- );
1456
- const resetUnread = react.useCallback(() => setUnreadCount(0), []);
1457
- react.useEffect(() => {
1458
- const el = containerRef.current;
1459
- if (!el) return;
1460
- const onScroll = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
1461
- const distance = el.scrollHeight - el.scrollTop - el.clientHeight;
1462
- const atBottom = distance <= bottomThresholdPx;
1463
- stickyRef.current = atBottom;
1464
- setIsAtBottom(atBottom);
1465
- if (atBottom) setUnreadCount(0);
1466
- }, "onScroll");
1467
- onScroll();
1468
- el.addEventListener("scroll", onScroll, { passive: true });
1469
- return () => {
1470
- el.removeEventListener("scroll", onScroll);
1471
- };
1472
- }, [containerRef, bottomThresholdPx]);
1473
- react.useEffect(() => {
1474
- const el = containerRef.current;
1475
- if (!el) return;
1476
- if (isStreaming) {
1477
- wasStreamingRef.current = true;
1478
- if (!stickyRef.current) return;
1479
- let raf = 0;
1480
- const tick = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
1481
- if (!stickyRef.current) return;
1482
- el.scrollTop = el.scrollHeight;
1483
- raf = requestAnimationFrame(tick);
1484
- }, "tick");
1485
- raf = requestAnimationFrame(tick);
1486
- return () => cancelAnimationFrame(raf);
1487
- }
1488
- if (wasStreamingRef.current && stickyRef.current) {
1489
- wasStreamingRef.current = false;
1490
- let raf1 = 0;
1491
- let raf2 = 0;
1492
- raf1 = requestAnimationFrame(() => {
1493
- el.scrollTop = el.scrollHeight;
1494
- raf2 = requestAnimationFrame(() => {
1495
- el.scrollTop = el.scrollHeight;
1496
- });
1497
- });
1498
- return () => {
1499
- cancelAnimationFrame(raf1);
1500
- cancelAnimationFrame(raf2);
1501
- };
1502
- }
1503
- wasStreamingRef.current = false;
1504
- return;
1505
- }, [containerRef, isStreaming]);
1506
- react.useEffect(() => {
1507
- if (messagesCount > lastCountRef.current) {
1508
- if (stickyRef.current) {
1509
- const el = containerRef.current;
1510
- if (el) el.scrollTop = el.scrollHeight;
1511
- } else {
1512
- setUnreadCount((n) => n + (messagesCount - lastCountRef.current));
1513
- }
1514
- }
1515
- lastCountRef.current = messagesCount;
1516
- }, [containerRef, messagesCount]);
1517
- react.useEffect(() => {
1518
- }, [bottomRef]);
1519
- return { isAtBottom, unreadCount, scrollToBottom, resetUnread };
1520
- }
1521
- chunkOLISEQHS_cjs.__name(useChatScroll, "useChatScroll");
1522
- function useChatHistory(options) {
1523
- const { enabled = true, containerRef, topSentinelRef, hasMore, isLoadingMore, loadMore } = options;
1524
- const heightBeforeRef = react.useRef(null);
1525
- react.useEffect(() => {
1526
- if (heightBeforeRef.current == null) return;
1527
- const el = containerRef.current;
1528
- if (!el) {
1529
- heightBeforeRef.current = null;
1530
- return;
1531
- }
1532
- if (!isLoadingMore) {
1533
- const delta = el.scrollHeight - heightBeforeRef.current;
1534
- if (delta > 0) {
1535
- el.scrollTop += delta;
1536
- }
1537
- heightBeforeRef.current = null;
1538
- }
1539
- }, [containerRef, isLoadingMore]);
1540
- react.useEffect(() => {
1541
- if (!enabled || !hasMore) return;
1542
- const sentinel = topSentinelRef.current;
1543
- const root = containerRef.current;
1544
- if (!sentinel || !root) return;
1545
- const observer = new IntersectionObserver(
1546
- (entries) => {
1547
- const entry = entries[0];
1548
- if (!entry?.isIntersecting) return;
1549
- if (isLoadingMore) return;
1550
- const el = containerRef.current;
1551
- if (el) heightBeforeRef.current = el.scrollHeight;
1552
- void loadMore();
1553
- },
1554
- { root, threshold: 0, rootMargin: "200px 0px 0px 0px" }
1555
- );
1556
- observer.observe(sentinel);
1557
- return () => observer.disconnect();
1558
- }, [enabled, hasMore, isLoadingMore, containerRef, topSentinelRef, loadMore]);
1559
- }
1560
- chunkOLISEQHS_cjs.__name(useChatHistory, "useChatHistory");
1561
1459
  function AttachmentsGrid({
1562
1460
  attachments,
1563
1461
  maxVisible,
@@ -2137,7 +2035,9 @@ var MessageBubbleInner = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(({
2137
2035
  onCopy,
2138
2036
  onRegenerate,
2139
2037
  onEdit,
2140
- onDelete
2038
+ onDelete,
2039
+ messageActionsExtra,
2040
+ streamingIndicator
2141
2041
  }) => {
2142
2042
  const isUser = isUserProp ?? message.role === "user";
2143
2043
  const isStreaming = !!message.isStreaming;
@@ -2200,7 +2100,7 @@ var MessageBubbleInner = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(({
2200
2100
  isUser ? "bg-primary text-primary-foreground rounded-tr-md" : isErr ? "bg-destructive/10 text-destructive rounded-tl-md border border-destructive/30" : "bg-muted text-foreground rounded-tl-md"
2201
2101
  ),
2202
2102
  children: [
2203
- isStreaming && message.toolActivity ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1.5", children: /* @__PURE__ */ jsxRuntime.jsx(StreamingIndicator, { label: message.toolActivity }) }) : null,
2103
+ isStreaming && message.toolActivity ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1.5", children: streamingIndicator ? streamingIndicator(message) : /* @__PURE__ */ jsxRuntime.jsx(StreamingIndicator, { label: message.toolActivity }) }) : null,
2204
2104
  message.content || !isStreaming ? /* @__PURE__ */ jsxRuntime.jsx(
2205
2105
  chunkXACCHZH2_cjs.MarkdownMessage,
2206
2106
  {
@@ -2209,22 +2109,25 @@ var MessageBubbleInner = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(({
2209
2109
  isCompact,
2210
2110
  plainText: isStreaming
2211
2111
  }
2212
- ) : /* @__PURE__ */ jsxRuntime.jsx(StreamingIndicator, {})
2112
+ ) : streamingIndicator ? streamingIndicator(message) : /* @__PURE__ */ jsxRuntime.jsx(StreamingIndicator, {})
2213
2113
  ]
2214
2114
  }
2215
2115
  ),
2216
2116
  message.toolCalls?.length ? toolCallsRenderer ? toolCallsRenderer(message.toolCalls) : /* @__PURE__ */ jsxRuntime.jsx(ToolCalls, { calls: message.toolCalls, ...toolCallsProps }) : null,
2217
2117
  message.sources?.length && !isStreaming ? sourcesRenderer ? sourcesRenderer(message.sources) : /* @__PURE__ */ jsxRuntime.jsx(Sources, { sources: message.sources }) : null,
2218
- showActions && !isStreaming ? /* @__PURE__ */ jsxRuntime.jsx(
2219
- MessageActions,
2220
- {
2221
- role: message.role,
2222
- onCopy,
2223
- onRegenerate,
2224
- onEdit,
2225
- onDelete
2226
- }
2227
- ) : null,
2118
+ showActions && !isStreaming ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
2119
+ /* @__PURE__ */ jsxRuntime.jsx(
2120
+ MessageActions,
2121
+ {
2122
+ role: message.role,
2123
+ onCopy,
2124
+ onRegenerate,
2125
+ onEdit,
2126
+ onDelete
2127
+ }
2128
+ ),
2129
+ messageActionsExtra ? messageActionsExtra(message) : null
2130
+ ] }) : null,
2228
2131
  showTimestamp ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-[10px] text-muted-foreground", children: new Date(message.createdAt).toLocaleTimeString() }) : null,
2229
2132
  afterContent
2230
2133
  ] })
@@ -2243,50 +2146,120 @@ var MessageList = react.forwardRef(/* @__PURE__ */ chunkOLISEQHS_cjs.__name(func
2243
2146
  renderItem,
2244
2147
  renderEmpty,
2245
2148
  isLoadingMore: isLoadingMoreProp,
2246
- topSentinelRef,
2247
- bottomRef,
2149
+ onStartReached,
2248
2150
  className,
2249
- itemClassName
2151
+ itemClassName,
2152
+ noVirtualize = false,
2153
+ defaultItemHeight = 120,
2154
+ onAtBottomChange
2250
2155
  }, ref) {
2251
2156
  const ctx = useChatContextOptional();
2252
2157
  const messages = messagesProp ?? ctx?.messages ?? [];
2253
2158
  const isLoadingMore = isLoadingMoreProp ?? ctx?.isLoadingMore ?? false;
2159
+ const { copyToClipboard } = hooks.useCopy();
2160
+ const virtuosoRef = react.useRef(null);
2161
+ react.useImperativeHandle(
2162
+ ref,
2163
+ () => ({
2164
+ scrollToBottom: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((smooth = false) => {
2165
+ virtuosoRef.current?.scrollToIndex({
2166
+ index: "LAST",
2167
+ behavior: smooth ? "smooth" : "auto",
2168
+ align: "end"
2169
+ });
2170
+ }, "scrollToBottom"),
2171
+ scrollToIndex: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((index, smooth = false) => {
2172
+ virtuosoRef.current?.scrollToIndex({
2173
+ index,
2174
+ behavior: smooth ? "smooth" : "auto"
2175
+ });
2176
+ }, "scrollToIndex")
2177
+ }),
2178
+ []
2179
+ );
2254
2180
  const defaultRenderItem = react.useCallback(
2255
2181
  (m) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: itemClassName, children: /* @__PURE__ */ jsxRuntime.jsx(
2256
2182
  MessageBubble,
2257
2183
  {
2258
2184
  message: m,
2259
- onCopy: () => copy(m.content),
2185
+ onCopy: () => void copyToClipboard(m.content),
2260
2186
  onRegenerate: ctx ? () => void ctx.regenerate(m.id) : void 0,
2261
2187
  onDelete: ctx ? () => ctx.deleteMessage(m.id) : void 0
2262
2188
  }
2263
- ) }, m.id),
2264
- [itemClassName, ctx]
2189
+ ) }),
2190
+ [itemClassName, ctx, copyToClipboard]
2265
2191
  );
2266
2192
  const itemRenderer = renderItem ?? defaultRenderItem;
2267
- return /* @__PURE__ */ jsxRuntime.jsxs(
2268
- "div",
2193
+ const computeItemKey = react.useCallback(
2194
+ (index, m) => m?.id ?? index,
2195
+ []
2196
+ );
2197
+ const startReachedHandler = react.useMemo(() => {
2198
+ if (!onStartReached) return void 0;
2199
+ let inFlight = false;
2200
+ return () => {
2201
+ if (inFlight || isLoadingMore) return;
2202
+ inFlight = true;
2203
+ try {
2204
+ onStartReached();
2205
+ } finally {
2206
+ queueMicrotask(() => {
2207
+ inFlight = false;
2208
+ });
2209
+ }
2210
+ };
2211
+ }, [onStartReached, isLoadingMore]);
2212
+ if (messages.length === 0) {
2213
+ return /* @__PURE__ */ jsxRuntime.jsx(
2214
+ "div",
2215
+ {
2216
+ role: "log",
2217
+ "aria-live": "polite",
2218
+ "aria-atomic": "false",
2219
+ className: lib.cn("flex-1 overflow-y-auto", className),
2220
+ children: renderEmpty?.() ?? null
2221
+ }
2222
+ );
2223
+ }
2224
+ if (noVirtualize) {
2225
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2226
+ "div",
2227
+ {
2228
+ role: "log",
2229
+ "aria-live": "polite",
2230
+ "aria-atomic": "false",
2231
+ className: lib.cn("flex-1 overflow-y-auto", className),
2232
+ children: [
2233
+ isLoadingMore ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsxRuntime.jsx(components.Spinner, { className: "size-4 text-muted-foreground" }) }) : null,
2234
+ messages.map((m, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: itemRenderer(m, i) }, m.id ?? i))
2235
+ ]
2236
+ }
2237
+ );
2238
+ }
2239
+ return /* @__PURE__ */ jsxRuntime.jsx(
2240
+ reactVirtuoso.Virtuoso,
2269
2241
  {
2270
- ref,
2242
+ ref: virtuosoRef,
2271
2243
  role: "log",
2272
2244
  "aria-live": "polite",
2273
2245
  "aria-atomic": "false",
2274
- className: lib.cn("flex-1 overflow-y-auto", className),
2275
- children: [
2276
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: topSentinelRef, "aria-hidden": true }),
2277
- isLoadingMore ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsxRuntime.jsx(components.Spinner, { className: "size-4 text-muted-foreground" }) }) : null,
2278
- messages.length === 0 ? renderEmpty?.() ?? null : messages.map((m, i) => itemRenderer(m, i)),
2279
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: bottomRef, "aria-hidden": true })
2280
- ]
2246
+ className: lib.cn("flex-1", className),
2247
+ data: messages,
2248
+ computeItemKey,
2249
+ itemContent: (index, m) => m ? itemRenderer(m, index) : null,
2250
+ defaultItemHeight,
2251
+ followOutput: (isAtBottom) => isAtBottom ? "auto" : false,
2252
+ atBottomStateChange: onAtBottomChange,
2253
+ alignToBottom: true,
2254
+ startReached: startReachedHandler,
2255
+ components: isLoadingMore ? {
2256
+ Header: /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsxRuntime.jsx(components.Spinner, { className: "size-4 text-muted-foreground" }) }), "Header")
2257
+ } : EMPTY_COMPONENTS,
2258
+ increaseViewportBy: { top: 200, bottom: 400 }
2281
2259
  }
2282
2260
  );
2283
2261
  }, "MessageList"));
2284
- function copy(text) {
2285
- if (typeof navigator !== "undefined" && navigator.clipboard) {
2286
- void navigator.clipboard.writeText(text);
2287
- }
2288
- }
2289
- chunkOLISEQHS_cjs.__name(copy, "copy");
2262
+ var EMPTY_COMPONENTS = {};
2290
2263
  function ChatRoot(props) {
2291
2264
  const { transport, config, initialSessionId, autoCreateSession, streaming, audio, debug, className, listClassName, ...slots } = props;
2292
2265
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -2310,22 +2283,9 @@ function ChatRootShell({ className, listClassName, slots }) {
2310
2283
  onSubmit: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((content, attachments) => chat.sendMessage(content, attachments), "onSubmit"),
2311
2284
  disabled: chat.isStreaming
2312
2285
  });
2313
- const containerRef = react.useRef(null);
2314
- const bottomRef = react.useRef(null);
2315
- const topRef = react.useRef(null);
2316
- const scroll = useChatScroll({
2317
- containerRef,
2318
- bottomRef,
2319
- isStreaming: chat.isStreaming,
2320
- messagesCount: chat.messages.length
2321
- });
2322
- useChatHistory({
2323
- containerRef,
2324
- topSentinelRef: topRef,
2325
- hasMore: chat.hasMore,
2326
- isLoadingMore: chat.isLoadingMore,
2327
- loadMore: chat.loadMore
2328
- });
2286
+ const listRef = react.useRef(null);
2287
+ const [isAtBottom, setIsAtBottom] = react.useState(true);
2288
+ const handleStartReached = chat.hasMore && !chat.isLoadingMore ? () => void chat.loadMore() : void 0;
2329
2289
  const greeting = chat.config.greeting ?? "How can I help?";
2330
2290
  const description = chat.config.description;
2331
2291
  const suggestions = chat.config.suggestions;
@@ -2349,7 +2309,7 @@ function ChatRootShell({ className, listClassName, slots }) {
2349
2309
  toolCallsProps: slots.toolCallsProps,
2350
2310
  attachmentRenderers: slots.attachmentRenderers,
2351
2311
  onAttachmentOpen: slots.onAttachmentOpen,
2352
- onCopy: () => copy2(m.content),
2312
+ onCopy: () => copy(m.content),
2353
2313
  onRegenerate: () => void chat.regenerate(m.id),
2354
2314
  onDelete: () => chat.deleteMessage(m.id)
2355
2315
  },
@@ -2370,20 +2330,19 @@ function ChatRootShell({ className, listClassName, slots }) {
2370
2330
  /* @__PURE__ */ jsxRuntime.jsx(
2371
2331
  MessageList,
2372
2332
  {
2373
- ref: containerRef,
2374
- topSentinelRef: topRef,
2375
- bottomRef,
2333
+ ref: listRef,
2376
2334
  renderItem,
2377
2335
  renderEmpty: () => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: emptyNode }),
2378
- className: listClassName
2336
+ className: listClassName,
2337
+ onStartReached: handleStartReached,
2338
+ onAtBottomChange: setIsAtBottom
2379
2339
  }
2380
2340
  ),
2381
2341
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-2 flex justify-center", children: slots.jumpToLatest ?? /* @__PURE__ */ jsxRuntime.jsx(
2382
2342
  JumpToLatest,
2383
2343
  {
2384
- visible: !scroll.isAtBottom,
2385
- unreadCount: scroll.unreadCount,
2386
- onClick: () => scroll.scrollToBottom(true)
2344
+ visible: !isAtBottom,
2345
+ onClick: () => listRef.current?.scrollToBottom(true)
2387
2346
  }
2388
2347
  ) })
2389
2348
  ] }),
@@ -2404,12 +2363,12 @@ function ChatRootShell({ className, listClassName, slots }) {
2404
2363
  ] });
2405
2364
  }
2406
2365
  chunkOLISEQHS_cjs.__name(ChatRootShell, "ChatRootShell");
2407
- function copy2(text) {
2366
+ function copy(text) {
2408
2367
  if (typeof navigator !== "undefined" && navigator.clipboard) {
2409
2368
  void navigator.clipboard.writeText(text);
2410
2369
  }
2411
2370
  }
2412
- chunkOLISEQHS_cjs.__name(copy2, "copy");
2371
+ chunkOLISEQHS_cjs.__name(copy, "copy");
2413
2372
 
2414
2373
  exports.Attachments = Attachments;
2415
2374
  exports.AttachmentsGrid = AttachmentsGrid;
@@ -2447,8 +2406,6 @@ exports.useChatAudioPrefs = useChatAudioPrefs;
2447
2406
  exports.useChatComposer = useChatComposer;
2448
2407
  exports.useChatContext = useChatContext;
2449
2408
  exports.useChatContextOptional = useChatContextOptional;
2450
- exports.useChatHistory = useChatHistory;
2451
2409
  exports.useChatLayout = useChatLayout;
2452
- exports.useChatScroll = useChatScroll;
2453
- //# sourceMappingURL=chunk-OPKFKTIN.cjs.map
2454
- //# sourceMappingURL=chunk-OPKFKTIN.cjs.map
2410
+ //# sourceMappingURL=chunk-2SKR4U5S.cjs.map
2411
+ //# sourceMappingURL=chunk-2SKR4U5S.cjs.map