@agents24/chat-react 0.1.7 → 0.1.9

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 (42) hide show
  1. package/README.md +25 -12
  2. package/dist/chunk-E3TJ3I6G.js +358 -0
  3. package/dist/chunk-E3TJ3I6G.js.map +1 -0
  4. package/dist/chunk-EWZO4QJI.js +140 -0
  5. package/dist/chunk-EWZO4QJI.js.map +1 -0
  6. package/dist/chunk-UU7OXHL3.js +11 -0
  7. package/dist/chunk-UU7OXHL3.js.map +1 -0
  8. package/dist/context-window.d.ts +38 -0
  9. package/dist/controller.d.ts +22 -0
  10. package/dist/index.cjs +392 -314
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.ts +10 -530
  13. package/dist/index.js +41 -439
  14. package/dist/index.js.map +1 -1
  15. package/dist/latest-thread-scroller.d.ts +54 -0
  16. package/dist/model.d.ts +29 -0
  17. package/dist/renderers.d.ts +10 -0
  18. package/dist/sse.d.ts +3 -0
  19. package/dist/streaming-text.d.ts +24 -0
  20. package/dist/templates/agent-chat-shell.d.ts +25 -0
  21. package/dist/templates/index.cjs +523 -0
  22. package/dist/templates/index.cjs.map +1 -0
  23. package/dist/templates/index.d.ts +1 -0
  24. package/dist/templates/index.js +140 -0
  25. package/dist/templates/index.js.map +1 -0
  26. package/dist/transport.d.ts +29 -0
  27. package/dist/types.d.ts +312 -0
  28. package/dist/ui/adapters.d.ts +36 -0
  29. package/dist/ui/attachment.d.ts +23 -0
  30. package/dist/ui/bubble.d.ts +17 -0
  31. package/dist/ui/index.cjs +943 -0
  32. package/dist/ui/index.cjs.map +1 -0
  33. package/dist/ui/index.d.ts +7 -0
  34. package/dist/ui/index.js +734 -0
  35. package/dist/ui/index.js.map +1 -0
  36. package/dist/ui/marker.d.ts +11 -0
  37. package/dist/ui/message-response.d.ts +7 -0
  38. package/dist/ui/message.d.ts +10 -0
  39. package/dist/ui/utils.d.ts +2 -0
  40. package/dist/viewport.d.ts +2 -0
  41. package/package.json +22 -4
  42. package/dist/index.d.cts +0 -530
package/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -23,7 +33,15 @@ __export(index_exports, {
23
33
  DEFAULT_THREAD_PAGE_SIZE: () => DEFAULT_THREAD_PAGE_SIZE,
24
34
  DefaultChatPart: () => DefaultChatPart,
25
35
  DefaultToolPart: () => DefaultToolPart,
26
- LatestThreadViewport: () => LatestThreadViewport,
36
+ LatestThreadScroller: () => LatestThreadScroller,
37
+ LatestThreadScrollerButton: () => LatestThreadScrollerButton,
38
+ LatestThreadScrollerContent: () => LatestThreadScrollerContent,
39
+ LatestThreadScrollerItem: () => LatestThreadScrollerItem,
40
+ LatestThreadScrollerOutline: () => LatestThreadScrollerOutline,
41
+ LatestThreadScrollerProvider: () => LatestThreadScrollerProvider,
42
+ LatestThreadScrollerRoot: () => LatestThreadScrollerRoot,
43
+ LatestThreadScrollerViewport: () => LatestThreadScrollerViewport,
44
+ MessageScroller: () => import_message_scroller2.MessageScroller,
27
45
  activeRunIdFromThread: () => activeRunIdFromThread,
28
46
  activeRunIdFromThreadDetail: () => activeRunIdFromThreadDetail,
29
47
  assistantTextFromParts: () => assistantTextFromParts,
@@ -33,27 +51,20 @@ __export(index_exports, {
33
51
  createChatId: () => createChatId,
34
52
  createFetchChatTransport: () => createFetchChatTransport,
35
53
  getActiveStreamingTextPartId: () => getActiveStreamingTextPartId,
36
- getLatestScrollTop: () => getLatestScrollTop,
37
54
  hasStaleUnfinishedAssistantCache: () => hasStaleUnfinishedAssistantCache,
38
55
  hasUnfinishedAssistantMessage: () => hasUnfinishedAssistantMessage,
39
56
  isActiveStreamingTextPart: () => isActiveStreamingTextPart,
40
- isAtTimelineLatestEdge: () => isAtTimelineLatestEdge,
41
57
  isRunningThreadStatus: () => isRunningThreadStatus,
42
- isScrollable: () => isScrollable,
43
- isUserScrollIntentAwayFromLatest: () => isUserScrollIntentAwayFromLatest,
44
58
  latestContextWindowFromThread: () => latestContextWindowFromThread,
45
59
  mergeContextWindow: () => mergeContextWindow,
46
60
  mergeContextWindowUpdate: () => mergeContextWindowUpdate,
47
61
  mergeReasoningSteps: () => mergeReasoningSteps,
48
- nextLatestFollowStateOnScroll: () => nextLatestFollowStateOnScroll,
49
62
  normalizeContextCompression: () => normalizeContextCompression,
50
63
  normalizeContextWindow: () => normalizeContextWindow,
51
64
  parseSseBlock: () => parseSseBlock,
52
65
  partsFromResponseBlocks: () => partsFromResponseBlocks,
53
66
  reasoningStepsFromParts: () => reasoningStepsFromParts,
54
67
  renderChatPart: () => renderChatPart,
55
- shouldPrefetchOlder: () => shouldPrefetchOlder,
56
- shouldUseTopOriginTimeline: () => shouldUseTopOriginTimeline,
57
68
  textFromFinalOutput: () => textFromFinalOutput,
58
69
  threadActivityDate: () => threadActivityDate,
59
70
  threadDetailToMessages: () => threadDetailToMessages,
@@ -61,7 +72,9 @@ __export(index_exports, {
61
72
  titleFromMessage: () => titleFromMessage,
62
73
  toolStateFromStatus: () => toolStateFromStatus,
63
74
  useAgents24ChatController: () => useAgents24ChatController,
64
- useLatestThreadViewport: () => useLatestThreadViewport,
75
+ useMessageScroller: () => import_message_scroller2.useMessageScroller,
76
+ useMessageScrollerScrollable: () => import_message_scroller2.useMessageScrollerScrollable,
77
+ useMessageScrollerVisibility: () => import_message_scroller2.useMessageScrollerVisibility,
65
78
  useStreamingText: () => useStreamingText
66
79
  });
67
80
  module.exports = __toCommonJS(index_exports);
@@ -1228,14 +1241,357 @@ function useAgents24ChatController({
1228
1241
  ]);
1229
1242
  }
1230
1243
 
1231
- // src/renderers.tsx
1244
+ // src/latest-thread-scroller.tsx
1245
+ var React = __toESM(require("react"), 1);
1246
+ var import_message_scroller = require("@shadcn/react/message-scroller");
1232
1247
  var import_jsx_runtime = require("react/jsx-runtime");
1248
+ var cx = (...values) => values.filter(Boolean).join(" ");
1249
+ var LatestThreadScrollerLayoutContext = React.createContext(null);
1250
+ var useLatestThreadScrollerLayout = () => React.useContext(LatestThreadScrollerLayoutContext);
1251
+ function LatestThreadScrollerProvider({
1252
+ autoScroll = true,
1253
+ defaultScrollPosition = "last-anchor",
1254
+ scrollPreviousItemPeek = 64,
1255
+ ...props
1256
+ }) {
1257
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1258
+ import_message_scroller.MessageScroller.Provider,
1259
+ {
1260
+ autoScroll,
1261
+ defaultScrollPosition,
1262
+ scrollPreviousItemPeek,
1263
+ ...props
1264
+ }
1265
+ );
1266
+ }
1267
+ function LatestThreadScrollerRoot({
1268
+ className,
1269
+ ref,
1270
+ ...props
1271
+ }) {
1272
+ const [rootElement, setRootElement] = React.useState(null);
1273
+ const [contentElement, setContentElement] = React.useState(null);
1274
+ const layoutContext = React.useMemo(
1275
+ () => ({
1276
+ contentElement,
1277
+ rootElement,
1278
+ setContentElement,
1279
+ setRootElement
1280
+ }),
1281
+ [contentElement, rootElement]
1282
+ );
1283
+ const setRef = React.useCallback(
1284
+ (node) => {
1285
+ setRootElement(node);
1286
+ if (typeof ref === "function") {
1287
+ ref(node);
1288
+ } else if (ref) {
1289
+ ref.current = node;
1290
+ }
1291
+ },
1292
+ [ref]
1293
+ );
1294
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LatestThreadScrollerLayoutContext.Provider, { value: layoutContext, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1295
+ import_message_scroller.MessageScroller.Root,
1296
+ {
1297
+ ref: setRef,
1298
+ className: cx(
1299
+ "relative flex size-full min-h-0 flex-1 flex-col overflow-hidden",
1300
+ className
1301
+ ),
1302
+ ...props
1303
+ }
1304
+ ) });
1305
+ }
1306
+ function LatestThreadScrollerViewport({
1307
+ className,
1308
+ onViewportChange,
1309
+ preserveScrollOnPrepend = true,
1310
+ ref,
1311
+ ...props
1312
+ }) {
1313
+ const setRef = React.useCallback(
1314
+ (node) => {
1315
+ if (typeof ref === "function") {
1316
+ ref(node);
1317
+ } else if (ref) {
1318
+ ref.current = node;
1319
+ }
1320
+ onViewportChange?.(node);
1321
+ },
1322
+ [onViewportChange, ref]
1323
+ );
1324
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1325
+ import_message_scroller.MessageScroller.Viewport,
1326
+ {
1327
+ ref: setRef,
1328
+ className: cx(
1329
+ "size-full min-h-0 min-w-0 flex-1 overflow-y-auto overscroll-contain",
1330
+ className
1331
+ ),
1332
+ preserveScrollOnPrepend,
1333
+ ...props
1334
+ }
1335
+ );
1336
+ }
1337
+ function LatestThreadScrollerContent({
1338
+ className,
1339
+ ref,
1340
+ ...props
1341
+ }) {
1342
+ const layout = useLatestThreadScrollerLayout();
1343
+ const setRef = React.useCallback(
1344
+ (node) => {
1345
+ layout?.setContentElement(node);
1346
+ if (typeof ref === "function") {
1347
+ ref(node);
1348
+ } else if (ref) {
1349
+ ref.current = node;
1350
+ }
1351
+ },
1352
+ [layout, ref]
1353
+ );
1354
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1355
+ import_message_scroller.MessageScroller.Content,
1356
+ {
1357
+ ref: setRef,
1358
+ "data-agents24-latest-thread-content": "",
1359
+ className: cx("flex h-max min-h-full flex-col gap-8 p-4", className),
1360
+ ...props
1361
+ }
1362
+ );
1363
+ }
1364
+ function LatestThreadScrollerItem({
1365
+ className,
1366
+ messageId,
1367
+ ...props
1368
+ }) {
1369
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1370
+ import_message_scroller.MessageScroller.Item,
1371
+ {
1372
+ className: cx(
1373
+ "min-w-0 shrink-0 overflow-visible",
1374
+ className
1375
+ ),
1376
+ messageId,
1377
+ ...props
1378
+ }
1379
+ );
1380
+ }
1381
+ function LatestThreadScrollerButton({
1382
+ children,
1383
+ className,
1384
+ direction = "end",
1385
+ render,
1386
+ ...props
1387
+ }) {
1388
+ if (render) {
1389
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1390
+ import_message_scroller.MessageScroller.Button,
1391
+ {
1392
+ direction,
1393
+ render,
1394
+ ...props
1395
+ }
1396
+ );
1397
+ }
1398
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1399
+ import_message_scroller.MessageScroller.Button,
1400
+ {
1401
+ direction,
1402
+ render: (buttonProps) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1403
+ "button",
1404
+ {
1405
+ ...buttonProps,
1406
+ "aria-label": direction === "end" ? "Scroll to latest message" : "Scroll to first message",
1407
+ className: cx(
1408
+ "absolute bottom-4 left-1/2 z-50 flex size-9 -translate-x-1/2 items-center justify-center rounded-full border border-border bg-background text-foreground shadow-md transition-all duration-200 data-[active=false]:pointer-events-none data-[active=false]:translate-y-2 data-[active=false]:opacity-0 data-[active=true]:translate-y-0 data-[active=true]:opacity-100",
1409
+ className
1410
+ ),
1411
+ type: "button",
1412
+ children: children ?? (direction === "end" ? "\u2193" : "\u2191")
1413
+ }
1414
+ ),
1415
+ ...props
1416
+ }
1417
+ );
1418
+ }
1419
+ function LatestThreadScrollerOutline({
1420
+ cardClassName,
1421
+ className,
1422
+ contentMaxWidth = 768,
1423
+ dir = "ltr",
1424
+ items,
1425
+ minContainerWidthForRail = 0,
1426
+ minSideGutterForRail = 48,
1427
+ side
1428
+ }) {
1429
+ const navRef = React.useRef(null);
1430
+ const layout = useLatestThreadScrollerLayout();
1431
+ const [hasSideRoom, setHasSideRoom] = React.useState(false);
1432
+ const [hoveredIndex, setHoveredIndex] = React.useState(null);
1433
+ const { scrollToMessage } = (0, import_message_scroller.useMessageScroller)();
1434
+ const { currentAnchorId, visibleMessageIds } = (0, import_message_scroller.useMessageScrollerVisibility)();
1435
+ const visibleMessageIdSet = React.useMemo(
1436
+ () => new Set(visibleMessageIds),
1437
+ [visibleMessageIds]
1438
+ );
1439
+ const focusIndex = hoveredIndex ?? -1;
1440
+ const railSide = side ?? (dir === "rtl" ? "right" : "left");
1441
+ React.useEffect(() => {
1442
+ const root = layout?.rootElement ?? navRef.current?.parentElement;
1443
+ if (!root) return;
1444
+ const updateSideRoom = () => {
1445
+ const rootRect = root.getBoundingClientRect();
1446
+ const rootWidth = rootRect.width;
1447
+ const content2 = layout?.contentElement;
1448
+ const sideGutter = content2 ? Math.max(
1449
+ 0,
1450
+ railSide === "left" ? content2.getBoundingClientRect().left - rootRect.left : rootRect.right - content2.getBoundingClientRect().right
1451
+ ) : Math.max(0, (rootWidth - contentMaxWidth) / 2);
1452
+ const nextHasSideRoom = rootWidth >= minContainerWidthForRail && sideGutter >= minSideGutterForRail;
1453
+ setHasSideRoom(
1454
+ (current) => current === nextHasSideRoom ? current : nextHasSideRoom
1455
+ );
1456
+ };
1457
+ updateSideRoom();
1458
+ const resizeObserver = new ResizeObserver(updateSideRoom);
1459
+ resizeObserver.observe(root);
1460
+ const content = layout?.contentElement;
1461
+ if (content) {
1462
+ resizeObserver.observe(content);
1463
+ }
1464
+ return () => resizeObserver.disconnect();
1465
+ }, [
1466
+ contentMaxWidth,
1467
+ layout?.contentElement,
1468
+ layout?.rootElement,
1469
+ minContainerWidthForRail,
1470
+ minSideGutterForRail,
1471
+ railSide
1472
+ ]);
1473
+ if (items.length < 2) {
1474
+ return null;
1475
+ }
1476
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1477
+ "nav",
1478
+ {
1479
+ ref: navRef,
1480
+ "aria-label": "Transcript outline",
1481
+ className: cx(
1482
+ "pointer-events-none absolute top-[46%] z-10 max-h-[min(18rem,calc(100%-10rem))] w-16 -translate-y-1/2 overflow-visible",
1483
+ hasSideRoom ? "hidden md:block" : "hidden",
1484
+ railSide === "left" ? "left-1" : "right-1",
1485
+ className
1486
+ ),
1487
+ dir,
1488
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pointer-events-auto flex flex-col gap-0 py-1", children: items.map((item, index) => {
1489
+ const itemMessageIds = item.messageIds && item.messageIds.length > 0 ? item.messageIds : [item.id];
1490
+ const active = itemMessageIds.some(
1491
+ (messageId) => visibleMessageIdSet.has(messageId) || messageId === currentAnchorId
1492
+ );
1493
+ const distance = focusIndex >= 0 ? Math.abs(index - focusIndex) : Number.POSITIVE_INFINITY;
1494
+ const isFocused = distance === 0;
1495
+ const isNear = distance === 1;
1496
+ const isNextNear = distance === 2;
1497
+ const barWidth = isFocused ? 50 : isNear ? 36 : isNextNear ? 23 : 10;
1498
+ const barOpacity = isFocused ? 1 : isNear ? 0.78 : isNextNear ? 0.58 : 0.36;
1499
+ const fallbackLabel = `Turn ${index + 1}`;
1500
+ const previewLabel = item.label && item.label !== fallbackLabel ? item.label : "";
1501
+ const responsePreview = (item.responsePreview || "").trim();
1502
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1503
+ "button",
1504
+ {
1505
+ type: "button",
1506
+ "data-active": active,
1507
+ dir: "ltr",
1508
+ className: cx(
1509
+ "group relative flex h-2 w-16 items-center rounded-sm outline-none",
1510
+ railSide === "left" ? "justify-start" : "justify-end"
1511
+ ),
1512
+ "aria-label": `Jump to ${item.label || fallbackLabel}`,
1513
+ onBlur: () => setHoveredIndex(null),
1514
+ onFocus: () => setHoveredIndex(index),
1515
+ onMouseEnter: () => setHoveredIndex(index),
1516
+ onMouseLeave: () => setHoveredIndex(null),
1517
+ onClick: () => scrollToMessage(item.id, {
1518
+ align: "start",
1519
+ behavior: "smooth"
1520
+ }),
1521
+ children: [
1522
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1523
+ "span",
1524
+ {
1525
+ className: "h-0.5 rounded-full bg-muted-foreground/55 transition-[width,background-color,opacity] duration-150 ease-out group-hover:bg-foreground group-focus-visible:bg-foreground group-data-[active=true]:bg-foreground",
1526
+ style: {
1527
+ opacity: barOpacity,
1528
+ width: barWidth
1529
+ }
1530
+ }
1531
+ ),
1532
+ previewLabel ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1533
+ "span",
1534
+ {
1535
+ dir,
1536
+ className: cx(
1537
+ "pointer-events-none absolute top-1/2 z-20 max-h-32 w-64 -translate-y-1/2 overflow-hidden rounded-lg border border-border/80 bg-background p-3 text-start opacity-0 shadow-lg transition-all duration-200 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-visible:pointer-events-auto group-focus-visible:opacity-100",
1538
+ railSide === "left" ? "left-full ml-2 -translate-x-1 group-hover:translate-x-0 group-focus-visible:translate-x-0" : "right-full mr-2 translate-x-1 group-hover:translate-x-0 group-focus-visible:translate-x-0",
1539
+ cardClassName
1540
+ ),
1541
+ children: [
1542
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1543
+ "span",
1544
+ {
1545
+ className: "block overflow-hidden text-sm leading-5 text-foreground",
1546
+ style: {
1547
+ display: "-webkit-box",
1548
+ WebkitBoxOrient: "vertical",
1549
+ WebkitLineClamp: responsePreview ? 2 : 4
1550
+ },
1551
+ children: previewLabel
1552
+ }
1553
+ ),
1554
+ responsePreview ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1555
+ "span",
1556
+ {
1557
+ className: "mt-1 block overflow-hidden text-sm leading-5 text-muted-foreground",
1558
+ style: {
1559
+ display: "-webkit-box",
1560
+ WebkitBoxOrient: "vertical",
1561
+ WebkitLineClamp: 2
1562
+ },
1563
+ children: responsePreview
1564
+ }
1565
+ ) : null
1566
+ ]
1567
+ }
1568
+ ) : null
1569
+ ]
1570
+ },
1571
+ item.id
1572
+ );
1573
+ }) })
1574
+ }
1575
+ );
1576
+ }
1577
+ var LatestThreadScroller = {
1578
+ Provider: LatestThreadScrollerProvider,
1579
+ Root: LatestThreadScrollerRoot,
1580
+ Viewport: LatestThreadScrollerViewport,
1581
+ Content: LatestThreadScrollerContent,
1582
+ Item: LatestThreadScrollerItem,
1583
+ Button: LatestThreadScrollerButton,
1584
+ Outline: LatestThreadScrollerOutline
1585
+ };
1586
+
1587
+ // src/renderers.tsx
1588
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1233
1589
  var DefaultToolPart = ({ part }) => {
1234
1590
  const label = typeof part.presentation?.activity === "string" ? part.presentation.activity : typeof part.presentation?.title === "string" ? part.presentation.title : part.toolName;
1235
1591
  const detail = typeof part.presentation?.detail === "string" ? part.presentation.detail : part.errorText || part.state;
1236
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-agents24-tool-part": part.type, "data-state": part.state, children: [
1237
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: label }),
1238
- detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: detail }) : null
1592
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { "data-agents24-tool-part": part.type, "data-state": part.state, children: [
1593
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: label }),
1594
+ detail ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: detail }) : null
1239
1595
  ] });
1240
1596
  };
1241
1597
  var DefaultChatPart = ({
@@ -1244,28 +1600,28 @@ var DefaultChatPart = ({
1244
1600
  renderOptions
1245
1601
  }) => {
1246
1602
  if (renderOptions?.renderPart) {
1247
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: renderOptions.renderPart(part, message) });
1603
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: renderOptions.renderPart(part, message) });
1248
1604
  }
1249
- if (part.kind === "text") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: part.text });
1605
+ if (part.kind === "text") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: part.text });
1250
1606
  if (part.kind === "tool") {
1251
1607
  const ToolRenderer = renderOptions?.toolRenderers?.[part.type] || renderOptions?.fallbackToolRenderer || DefaultToolPart;
1252
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: ToolRenderer({ part, message }) });
1608
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: ToolRenderer({ part, message }) });
1253
1609
  }
1254
1610
  if (part.kind === "reasoning") {
1255
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-agents24-reasoning-part": true, children: part.text || part.label });
1611
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-reasoning-part": true, children: part.text || part.label });
1256
1612
  }
1257
1613
  if (part.kind === "ui-blocks") {
1258
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-agents24-ui-blocks-part": true, "data-state": part.state });
1614
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-ui-blocks-part": true, "data-state": part.state });
1259
1615
  }
1260
1616
  if (part.kind === "approval") {
1261
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-agents24-approval-part": true });
1617
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-approval-part": true });
1262
1618
  }
1263
1619
  if (part.kind === "error") {
1264
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-agents24-error-part": true, children: part.errorText });
1620
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-error-part": true, children: part.errorText });
1265
1621
  }
1266
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-agents24-data-part": part.name });
1622
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-data-part": part.name });
1267
1623
  };
1268
- var renderChatPart = (part, message, renderOptions) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1624
+ var renderChatPart = (part, message, renderOptions) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1269
1625
  DefaultChatPart,
1270
1626
  {
1271
1627
  part,
@@ -1531,295 +1887,22 @@ var createFetchChatTransport = ({
1531
1887
  };
1532
1888
  };
1533
1889
 
1534
- // src/viewport.tsx
1535
- var import_react3 = require("react");
1536
- var import_jsx_runtime2 = require("react/jsx-runtime");
1537
- var LATEST_EDGE_THRESHOLD_PX = 2;
1538
- var MIN_OLDER_PREFETCH_PX = 320;
1539
- var MAX_OLDER_PREFETCH_PX = 900;
1540
- var isScrollable = (element) => element.scrollHeight - element.clientHeight > LATEST_EDGE_THRESHOLD_PX;
1541
- var isAtTimelineLatestEdge = (element, isTopOrigin) => {
1542
- if (!isScrollable(element)) return true;
1543
- return isTopOrigin ? element.scrollHeight - element.clientHeight - element.scrollTop <= LATEST_EDGE_THRESHOLD_PX : Math.abs(element.scrollTop) <= LATEST_EDGE_THRESHOLD_PX;
1544
- };
1545
- var shouldPrefetchOlder = (element) => Math.abs(element.scrollTop) + element.clientHeight >= element.scrollHeight - Math.min(MAX_OLDER_PREFETCH_PX, Math.max(MIN_OLDER_PREFETCH_PX, element.clientHeight * 0.75));
1546
- var getLatestScrollTop = (element, isTopOrigin) => isTopOrigin ? Math.max(0, element.scrollHeight - element.clientHeight) : 0;
1547
- var shouldUseTopOriginTimeline = ({
1548
- hasOlder,
1549
- itemCount,
1550
- topOriginMaxItems = 4
1551
- }) => !hasOlder && itemCount > 0 && itemCount <= topOriginMaxItems;
1552
- var nextLatestFollowStateOnScroll = ({
1553
- atLatest,
1554
- current,
1555
- isProgrammatic
1556
- }) => {
1557
- if (isProgrammatic) return current;
1558
- return atLatest ? "following" : "detached";
1559
- };
1560
- var isUserScrollIntentAwayFromLatest = (intent) => {
1561
- if (!intent) return false;
1562
- if ("deltaY" in intent && typeof intent.deltaY === "number") {
1563
- return intent.deltaY < 0;
1564
- }
1565
- if ("key" in intent && typeof intent.key === "string") {
1566
- if (intent.key === "ArrowUp" || intent.key === "PageUp" || intent.key === "Home") return true;
1567
- if ((intent.key === " " || intent.key === "Spacebar") && intent.shiftKey) return true;
1568
- return false;
1569
- }
1570
- return "touches" in intent;
1571
- };
1572
- function useLatestThreadViewport({
1573
- itemCount,
1574
- hasOlder,
1575
- isLoadingOlder,
1576
- onLoadOlder,
1577
- activeStreamKey,
1578
- shouldAutoFollow = true,
1579
- topOriginMaxItems = 4
1580
- }) {
1581
- const scrollContainerRef = (0, import_react3.useRef)(null);
1582
- const olderPagePreserveRef = (0, import_react3.useRef)(null);
1583
- const olderPageRequestInFlightRef = (0, import_react3.useRef)(false);
1584
- const intrinsicResizePreserveRef = (0, import_react3.useRef)(null);
1585
- const followStateRef = (0, import_react3.useRef)("following");
1586
- const programmaticScrollRef = (0, import_react3.useRef)(false);
1587
- const programmaticScrollTimeoutRef = (0, import_react3.useRef)(null);
1588
- const activeStreamKeyRef = (0, import_react3.useRef)(null);
1589
- const observedScrollHeightRef = (0, import_react3.useRef)(null);
1590
- const [isAtLatest, setIsAtLatest] = (0, import_react3.useState)(true);
1591
- const [followState, setFollowState] = (0, import_react3.useState)("following");
1592
- const isTopOrigin = shouldUseTopOriginTimeline({ hasOlder, itemCount, topOriginMaxItems });
1593
- const setLatestFollowState = (0, import_react3.useCallback)((next) => {
1594
- if (followStateRef.current === next) return;
1595
- followStateRef.current = next;
1596
- setFollowState(next);
1597
- }, []);
1598
- const markProgrammaticScroll = (0, import_react3.useCallback)(() => {
1599
- programmaticScrollRef.current = true;
1600
- if (programmaticScrollTimeoutRef.current) clearTimeout(programmaticScrollTimeoutRef.current);
1601
- programmaticScrollTimeoutRef.current = setTimeout(() => {
1602
- programmaticScrollRef.current = false;
1603
- programmaticScrollTimeoutRef.current = null;
1604
- }, 80);
1605
- }, []);
1606
- const scrollToLatest = (0, import_react3.useCallback)((options) => {
1607
- const element = scrollContainerRef.current;
1608
- if (!element) return;
1609
- if (options?.reattach !== false) setLatestFollowState("following");
1610
- markProgrammaticScroll();
1611
- element.scrollTop = getLatestScrollTop(element, isTopOrigin);
1612
- setIsAtLatest(true);
1613
- observedScrollHeightRef.current = element.scrollHeight;
1614
- }, [isTopOrigin, markProgrammaticScroll, setLatestFollowState]);
1615
- const detachFromLatest = (0, import_react3.useCallback)(() => {
1616
- setLatestFollowState("detached");
1617
- }, [setLatestFollowState]);
1618
- const reattachToLatest = (0, import_react3.useCallback)(() => {
1619
- scrollToLatest({ reattach: true });
1620
- }, [scrollToLatest]);
1621
- const handleUserScrollIntent = (0, import_react3.useCallback)((intent) => {
1622
- const element = scrollContainerRef.current;
1623
- if (!activeStreamKey || !element) return;
1624
- const atLatest = isAtTimelineLatestEdge(element, isTopOrigin);
1625
- if (!atLatest || isUserScrollIntentAwayFromLatest(intent)) detachFromLatest();
1626
- }, [activeStreamKey, detachFromLatest, isTopOrigin]);
1627
- (0, import_react3.useEffect)(() => {
1628
- return () => {
1629
- if (programmaticScrollTimeoutRef.current) clearTimeout(programmaticScrollTimeoutRef.current);
1630
- const preserve = intrinsicResizePreserveRef.current;
1631
- if (preserve?.frame !== null && preserve?.frame !== void 0) cancelAnimationFrame(preserve.frame);
1632
- };
1633
- }, []);
1634
- (0, import_react3.useLayoutEffect)(() => {
1635
- const element = scrollContainerRef.current;
1636
- const preserve = olderPagePreserveRef.current;
1637
- if (!element) return;
1638
- observedScrollHeightRef.current = element.scrollHeight;
1639
- if (!preserve) {
1640
- setIsAtLatest(isAtTimelineLatestEdge(element, isTopOrigin));
1641
- return;
1642
- }
1643
- const addedHeight = element.scrollHeight - preserve.previousHeight;
1644
- markProgrammaticScroll();
1645
- element.scrollTop = preserve.previousTop - addedHeight;
1646
- olderPagePreserveRef.current = null;
1647
- olderPageRequestInFlightRef.current = false;
1648
- setIsAtLatest(isAtTimelineLatestEdge(element, isTopOrigin));
1649
- }, [isTopOrigin, itemCount, markProgrammaticScroll]);
1650
- const handleScroll = (0, import_react3.useCallback)(
1651
- (event) => {
1652
- const element = event.currentTarget;
1653
- const atLatest = isAtTimelineLatestEdge(element, isTopOrigin);
1654
- setIsAtLatest(atLatest);
1655
- setLatestFollowState(
1656
- nextLatestFollowStateOnScroll({
1657
- atLatest,
1658
- current: followStateRef.current,
1659
- isProgrammatic: programmaticScrollRef.current
1660
- })
1661
- );
1662
- if (isTopOrigin || !hasOlder || isLoadingOlder || olderPageRequestInFlightRef.current || !shouldPrefetchOlder(element)) {
1663
- return;
1664
- }
1665
- olderPageRequestInFlightRef.current = true;
1666
- olderPagePreserveRef.current = {
1667
- previousHeight: element.scrollHeight,
1668
- previousTop: element.scrollTop
1669
- };
1670
- void Promise.resolve(onLoadOlder()).finally(() => {
1671
- requestAnimationFrame(() => {
1672
- if (!olderPagePreserveRef.current) return;
1673
- olderPagePreserveRef.current = null;
1674
- olderPageRequestInFlightRef.current = false;
1675
- });
1676
- });
1677
- },
1678
- [hasOlder, isLoadingOlder, isTopOrigin, onLoadOlder, setLatestFollowState]
1679
- );
1680
- const preserveIntrinsicResize = (0, import_react3.useCallback)(() => {
1681
- const element = scrollContainerRef.current;
1682
- if (!element || isTopOrigin) return;
1683
- const active = intrinsicResizePreserveRef.current;
1684
- if (active?.frame !== null && active?.frame !== void 0) cancelAnimationFrame(active.frame);
1685
- intrinsicResizePreserveRef.current = {
1686
- lastHeight: element.scrollHeight,
1687
- endAt: Date.now() + 260,
1688
- frame: null
1689
- };
1690
- const preserveFrame = () => {
1691
- const nextElement = scrollContainerRef.current;
1692
- const preserve = intrinsicResizePreserveRef.current;
1693
- if (!nextElement || !preserve) return;
1694
- const heightDelta = nextElement.scrollHeight - preserve.lastHeight;
1695
- if (Math.abs(heightDelta) >= 1) {
1696
- markProgrammaticScroll();
1697
- nextElement.scrollTop -= heightDelta;
1698
- preserve.lastHeight = nextElement.scrollHeight;
1699
- setIsAtLatest(isAtTimelineLatestEdge(nextElement, isTopOrigin));
1700
- }
1701
- if (Date.now() < preserve.endAt) {
1702
- preserve.frame = requestAnimationFrame(preserveFrame);
1703
- } else {
1704
- intrinsicResizePreserveRef.current = null;
1705
- }
1706
- };
1707
- intrinsicResizePreserveRef.current.frame = requestAnimationFrame(preserveFrame);
1708
- }, [isTopOrigin, markProgrammaticScroll]);
1709
- (0, import_react3.useLayoutEffect)(() => {
1710
- const key = activeStreamKey || null;
1711
- const previousKey = activeStreamKeyRef.current;
1712
- if (!key) {
1713
- activeStreamKeyRef.current = null;
1714
- if (previousKey && shouldAutoFollow && followStateRef.current === "following" && !isLoadingOlder && !olderPageRequestInFlightRef.current) {
1715
- const frame2 = requestAnimationFrame(() => {
1716
- if (followStateRef.current === "following" && !isLoadingOlder && !olderPageRequestInFlightRef.current) {
1717
- scrollToLatest({ reattach: false });
1718
- }
1719
- });
1720
- return () => cancelAnimationFrame(frame2);
1721
- }
1722
- return;
1723
- }
1724
- if (previousKey === key) {
1725
- activeStreamKeyRef.current = key;
1726
- return;
1727
- }
1728
- setLatestFollowState("following");
1729
- const frame = requestAnimationFrame(() => scrollToLatest());
1730
- activeStreamKeyRef.current = key;
1731
- return () => cancelAnimationFrame(frame);
1732
- }, [activeStreamKey, isLoadingOlder, scrollToLatest, setLatestFollowState, shouldAutoFollow]);
1733
- (0, import_react3.useLayoutEffect)(() => {
1734
- if (!activeStreamKey || !shouldAutoFollow || followStateRef.current !== "following") return;
1735
- if (isLoadingOlder || olderPageRequestInFlightRef.current) return;
1736
- const frame = requestAnimationFrame(() => {
1737
- if (followStateRef.current === "following" && !isLoadingOlder && !olderPageRequestInFlightRef.current) {
1738
- scrollToLatest({ reattach: false });
1739
- }
1740
- });
1741
- return () => cancelAnimationFrame(frame);
1742
- }, [activeStreamKey, isLoadingOlder, itemCount, scrollToLatest, shouldAutoFollow]);
1743
- (0, import_react3.useEffect)(() => {
1744
- if (!activeStreamKey) {
1745
- observedScrollHeightRef.current = scrollContainerRef.current?.scrollHeight ?? null;
1746
- return;
1747
- }
1748
- let frame = null;
1749
- const watchStreamResize = () => {
1750
- const element = scrollContainerRef.current;
1751
- if (!element) return;
1752
- const previousHeight = observedScrollHeightRef.current;
1753
- const nextHeight = element.scrollHeight;
1754
- observedScrollHeightRef.current = nextHeight;
1755
- if (previousHeight !== null && Math.abs(nextHeight - previousHeight) >= 1 && shouldAutoFollow && followStateRef.current === "following" && !isLoadingOlder && !olderPageRequestInFlightRef.current) {
1756
- scrollToLatest({ reattach: false });
1757
- }
1758
- frame = requestAnimationFrame(watchStreamResize);
1759
- };
1760
- frame = requestAnimationFrame(watchStreamResize);
1761
- return () => {
1762
- if (frame !== null) cancelAnimationFrame(frame);
1763
- };
1764
- }, [activeStreamKey, isLoadingOlder, scrollToLatest, shouldAutoFollow]);
1765
- return {
1766
- scrollContainerRef,
1767
- isAtLatest,
1768
- isFollowingLatest: followState === "following",
1769
- isTopOrigin,
1770
- shouldShowLatestButton: !isAtLatest,
1771
- handleScroll,
1772
- handleUserScrollIntent,
1773
- scrollToLatest,
1774
- detachFromLatest,
1775
- reattachToLatest,
1776
- preserveIntrinsicResize
1777
- };
1778
- }
1779
- function LatestThreadViewport({
1780
- items,
1781
- hasOlder,
1782
- isLoadingOlder,
1783
- onLoadOlder,
1784
- activeStreamKey,
1785
- className,
1786
- latestSpacer,
1787
- trailingSpacer,
1788
- renderItem
1789
- }) {
1790
- const viewport = useLatestThreadViewport({
1791
- itemCount: items.length,
1792
- hasOlder,
1793
- isLoadingOlder,
1794
- onLoadOlder,
1795
- activeStreamKey
1796
- });
1797
- const timelineItems = (0, import_react3.useMemo)(() => viewport.isTopOrigin ? items : [...items].reverse(), [items, viewport.isTopOrigin]);
1798
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1799
- "div",
1800
- {
1801
- ref: viewport.scrollContainerRef,
1802
- className,
1803
- onKeyDownCapture: viewport.handleUserScrollIntent,
1804
- onScroll: viewport.handleScroll,
1805
- onTouchMoveCapture: viewport.handleUserScrollIntent,
1806
- onWheelCapture: viewport.handleUserScrollIntent,
1807
- role: "log",
1808
- style: { overflowAnchor: "none" },
1809
- children: [
1810
- !viewport.isTopOrigin && (latestSpacer ?? null),
1811
- timelineItems.map((item) => renderItem(item, viewport)),
1812
- viewport.isTopOrigin && (trailingSpacer ?? latestSpacer ?? null)
1813
- ]
1814
- }
1815
- );
1816
- }
1890
+ // src/viewport.ts
1891
+ var import_message_scroller2 = require("@shadcn/react/message-scroller");
1817
1892
  // Annotate the CommonJS export names for ESM import in node:
1818
1893
  0 && (module.exports = {
1819
1894
  DEFAULT_THREAD_PAGE_SIZE,
1820
1895
  DefaultChatPart,
1821
1896
  DefaultToolPart,
1822
- LatestThreadViewport,
1897
+ LatestThreadScroller,
1898
+ LatestThreadScrollerButton,
1899
+ LatestThreadScrollerContent,
1900
+ LatestThreadScrollerItem,
1901
+ LatestThreadScrollerOutline,
1902
+ LatestThreadScrollerProvider,
1903
+ LatestThreadScrollerRoot,
1904
+ LatestThreadScrollerViewport,
1905
+ MessageScroller,
1823
1906
  activeRunIdFromThread,
1824
1907
  activeRunIdFromThreadDetail,
1825
1908
  assistantTextFromParts,
@@ -1829,27 +1912,20 @@ function LatestThreadViewport({
1829
1912
  createChatId,
1830
1913
  createFetchChatTransport,
1831
1914
  getActiveStreamingTextPartId,
1832
- getLatestScrollTop,
1833
1915
  hasStaleUnfinishedAssistantCache,
1834
1916
  hasUnfinishedAssistantMessage,
1835
1917
  isActiveStreamingTextPart,
1836
- isAtTimelineLatestEdge,
1837
1918
  isRunningThreadStatus,
1838
- isScrollable,
1839
- isUserScrollIntentAwayFromLatest,
1840
1919
  latestContextWindowFromThread,
1841
1920
  mergeContextWindow,
1842
1921
  mergeContextWindowUpdate,
1843
1922
  mergeReasoningSteps,
1844
- nextLatestFollowStateOnScroll,
1845
1923
  normalizeContextCompression,
1846
1924
  normalizeContextWindow,
1847
1925
  parseSseBlock,
1848
1926
  partsFromResponseBlocks,
1849
1927
  reasoningStepsFromParts,
1850
1928
  renderChatPart,
1851
- shouldPrefetchOlder,
1852
- shouldUseTopOriginTimeline,
1853
1929
  textFromFinalOutput,
1854
1930
  threadActivityDate,
1855
1931
  threadDetailToMessages,
@@ -1857,7 +1933,9 @@ function LatestThreadViewport({
1857
1933
  titleFromMessage,
1858
1934
  toolStateFromStatus,
1859
1935
  useAgents24ChatController,
1860
- useLatestThreadViewport,
1936
+ useMessageScroller,
1937
+ useMessageScrollerScrollable,
1938
+ useMessageScrollerVisibility,
1861
1939
  useStreamingText
1862
1940
  });
1863
1941
  //# sourceMappingURL=index.cjs.map