@assistant-ui/react 0.0.18 → 0.0.19

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.
package/dist/index.d.mts CHANGED
@@ -103,16 +103,16 @@ declare namespace index$4 {
103
103
  export { ComposerCancel as Cancel, ComposerIf as If, ComposerInput as Input, ComposerRoot as Root, ComposerSend as Send };
104
104
  }
105
105
 
106
- type BaseComposerState = {
106
+ type BaseComposerState = Readonly<{
107
107
  value: string;
108
108
  setValue: (value: string) => void;
109
- };
110
- type MessageComposerState = BaseComposerState & {
109
+ }>;
110
+ type MessageComposerState = BaseComposerState & Readonly<{
111
111
  isEditing: boolean;
112
112
  edit: () => void;
113
113
  send: () => void;
114
114
  cancel: () => boolean;
115
- };
115
+ }>;
116
116
 
117
117
  type TextContentPart = {
118
118
  type: "text";
@@ -379,10 +379,10 @@ declare class VercelModelAdapter implements ChatModelAdapter {
379
379
  declare const getVercelMessage: (message: ThreadMessage) => Message | undefined;
380
380
  declare const getVercelRSCMessage: <T>(message: ThreadMessage) => T | undefined;
381
381
 
382
- type MessageState = {
383
- message: ThreadMessage;
382
+ type MessageState = Readonly<{
383
+ message: Readonly<ThreadMessage>;
384
384
  parentId: string | null;
385
- branches: string[];
385
+ branches: readonly string[];
386
386
  isLast: boolean;
387
387
  inProgressIndicator: ReactNode | null;
388
388
  setInProgressIndicator: (value: ReactNode | null) => void;
@@ -390,7 +390,7 @@ type MessageState = {
390
390
  setIsCopied: (value: boolean) => void;
391
391
  isHovering: boolean;
392
392
  setIsHovering: (value: boolean) => void;
393
- };
393
+ }>;
394
394
  type MessageStore = {
395
395
  useMessage: UseBoundStore<StoreApi<MessageState>>;
396
396
  useComposer: UseBoundStore<StoreApi<MessageComposerState>>;
package/dist/index.d.ts CHANGED
@@ -103,16 +103,16 @@ declare namespace index$4 {
103
103
  export { ComposerCancel as Cancel, ComposerIf as If, ComposerInput as Input, ComposerRoot as Root, ComposerSend as Send };
104
104
  }
105
105
 
106
- type BaseComposerState = {
106
+ type BaseComposerState = Readonly<{
107
107
  value: string;
108
108
  setValue: (value: string) => void;
109
- };
110
- type MessageComposerState = BaseComposerState & {
109
+ }>;
110
+ type MessageComposerState = BaseComposerState & Readonly<{
111
111
  isEditing: boolean;
112
112
  edit: () => void;
113
113
  send: () => void;
114
114
  cancel: () => boolean;
115
- };
115
+ }>;
116
116
 
117
117
  type TextContentPart = {
118
118
  type: "text";
@@ -379,10 +379,10 @@ declare class VercelModelAdapter implements ChatModelAdapter {
379
379
  declare const getVercelMessage: (message: ThreadMessage) => Message | undefined;
380
380
  declare const getVercelRSCMessage: <T>(message: ThreadMessage) => T | undefined;
381
381
 
382
- type MessageState = {
383
- message: ThreadMessage;
382
+ type MessageState = Readonly<{
383
+ message: Readonly<ThreadMessage>;
384
384
  parentId: string | null;
385
- branches: string[];
385
+ branches: readonly string[];
386
386
  isLast: boolean;
387
387
  inProgressIndicator: ReactNode | null;
388
388
  setInProgressIndicator: (value: ReactNode | null) => void;
@@ -390,7 +390,7 @@ type MessageState = {
390
390
  setIsCopied: (value: boolean) => void;
391
391
  isHovering: boolean;
392
392
  setIsHovering: (value: boolean) => void;
393
- };
393
+ }>;
394
394
  type MessageStore = {
395
395
  useMessage: UseBoundStore<StoreApi<MessageState>>;
396
396
  useComposer: UseBoundStore<StoreApi<MessageComposerState>>;
package/dist/index.js CHANGED
@@ -173,18 +173,22 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
173
173
  const ref = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, divRef);
174
174
  const { useViewport } = useAssistantContext();
175
175
  const firstRenderRef = (0, import_react5.useRef)(true);
176
+ const isScrollingToBottomRef = (0, import_react5.useRef)(false);
176
177
  const lastScrollTop = (0, import_react5.useRef)(0);
177
178
  const scrollToBottom = () => {
178
179
  const div = messagesEndRef.current;
179
180
  if (!div || !autoScroll) return;
180
181
  const behavior = firstRenderRef.current ? "instant" : "auto";
181
182
  firstRenderRef.current = false;
182
- useViewport.setState({ isAtBottom: true });
183
+ isScrollingToBottomRef.current = true;
183
184
  div.scrollIntoView({ behavior });
184
185
  };
185
186
  useOnResizeContent(divRef, () => {
186
- if (!useViewport.getState().isAtBottom) return;
187
- scrollToBottom();
187
+ if (!isScrollingToBottomRef.current && !useViewport.getState().isAtBottom) {
188
+ handleScroll();
189
+ } else {
190
+ scrollToBottom();
191
+ }
188
192
  });
189
193
  useOnScrollToBottom(() => {
190
194
  scrollToBottom();
@@ -196,6 +200,7 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
196
200
  const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
197
201
  if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
198
202
  } else if (newIsAtBottom !== isAtBottom) {
203
+ isScrollingToBottomRef.current = false;
199
204
  useViewport.setState({ isAtBottom: newIsAtBottom });
200
205
  }
201
206
  lastScrollTop.current = div.scrollTop;
@@ -841,6 +846,7 @@ __export(branchPicker_exports, {
841
846
  });
842
847
 
843
848
  // src/actions/useGoToNextBranch.tsx
849
+ var import_react21 = require("react");
844
850
  var useGoToNextBranch = () => {
845
851
  const { useThread } = useAssistantContext();
846
852
  const { useMessage, useComposer } = useMessageContext();
@@ -848,20 +854,21 @@ var useGoToNextBranch = () => {
848
854
  [useMessage, useComposer],
849
855
  (m, c) => c.isEditing || m.branches.indexOf(m.message.id) + 1 >= m.branches.length
850
856
  );
851
- if (disabled) return null;
852
- return () => {
857
+ const callback = (0, import_react21.useCallback)(() => {
853
858
  const { message, branches } = useMessage.getState();
854
859
  useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
855
- };
860
+ }, [useMessage, useThread]);
861
+ if (disabled) return null;
862
+ return callback;
856
863
  };
857
864
 
858
865
  // src/utils/createActionButton.tsx
859
866
  var import_primitive8 = require("@radix-ui/primitive");
860
867
  var import_react_primitive10 = require("@radix-ui/react-primitive");
861
- var import_react21 = require("react");
868
+ var import_react22 = require("react");
862
869
  var import_jsx_runtime16 = require("react/jsx-runtime");
863
870
  var createActionButton = (useActionButton) => {
864
- return (0, import_react21.forwardRef)(
871
+ return (0, import_react22.forwardRef)(
865
872
  (props, forwardedRef) => {
866
873
  const onClick = useActionButton(props);
867
874
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
@@ -882,6 +889,7 @@ var createActionButton = (useActionButton) => {
882
889
  var BranchPickerNext = createActionButton(useGoToNextBranch);
883
890
 
884
891
  // src/actions/useGoToPreviousBranch.tsx
892
+ var import_react23 = require("react");
885
893
  var useGoToPreviousBranch = () => {
886
894
  const { useThread } = useAssistantContext();
887
895
  const { useMessage, useComposer } = useMessageContext();
@@ -889,13 +897,12 @@ var useGoToPreviousBranch = () => {
889
897
  [useMessage, useComposer],
890
898
  (m, c) => c.isEditing || m.branches.indexOf(m.message.id) <= 0
891
899
  );
892
- if (disabled) return null;
893
- return () => {
900
+ const callback = (0, import_react23.useCallback)(() => {
894
901
  const { message, branches } = useMessage.getState();
895
- useThread.getState().switchToBranch(
896
- branches[branches.indexOf(message.id) - 1]
897
- );
898
- };
902
+ useThread.getState().switchToBranch(branches[branches.indexOf(message.id) - 1]);
903
+ }, [useMessage, useThread]);
904
+ if (disabled) return null;
905
+ return callback;
899
906
  };
900
907
 
901
908
  // src/primitives/branchPicker/BranchPickerPrevious.tsx
@@ -919,9 +926,9 @@ var BranchPickerNumber = () => {
919
926
 
920
927
  // src/primitives/branchPicker/BranchPickerRoot.tsx
921
928
  var import_react_primitive11 = require("@radix-ui/react-primitive");
922
- var import_react22 = require("react");
929
+ var import_react24 = require("react");
923
930
  var import_jsx_runtime19 = require("react/jsx-runtime");
924
- var BranchPickerRoot = (0, import_react22.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
931
+ var BranchPickerRoot = (0, import_react24.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
925
932
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_primitive11.Primitive.div, { ...rest, ref }) });
926
933
  });
927
934
 
@@ -936,9 +943,9 @@ __export(actionBar_exports, {
936
943
 
937
944
  // src/primitives/actionBar/ActionBarRoot.tsx
938
945
  var import_react_primitive12 = require("@radix-ui/react-primitive");
939
- var import_react23 = require("react");
946
+ var import_react25 = require("react");
940
947
  var import_jsx_runtime20 = require("react/jsx-runtime");
941
- var ActionBarRoot = (0, import_react23.forwardRef)(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
948
+ var ActionBarRoot = (0, import_react25.forwardRef)(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
942
949
  const { useThread } = useAssistantContext();
943
950
  const { useMessage } = useMessageContext();
944
951
  const hideAndfloatStatus = useCombinedStore(
@@ -965,6 +972,7 @@ var ActionBarRoot = (0, import_react23.forwardRef)(({ hideWhenRunning, autohide,
965
972
  });
966
973
 
967
974
  // src/actions/useCopyMessage.tsx
975
+ var import_react26 = require("react");
968
976
  var useCopyMessage = ({ copiedDuration = 3e3 }) => {
969
977
  const { useMessage, useComposer } = useMessageContext();
970
978
  const hasCopyableContent = useCombinedStore(
@@ -973,21 +981,23 @@ var useCopyMessage = ({ copiedDuration = 3e3 }) => {
973
981
  return c.isEditing || m.message.content.some((c2) => c2.type === "text");
974
982
  }
975
983
  );
976
- if (!hasCopyableContent) return null;
977
- return () => {
984
+ const callback = (0, import_react26.useCallback)(() => {
978
985
  const { isEditing, value: composerValue } = useComposer.getState();
979
986
  const { message, setIsCopied } = useMessage.getState();
980
987
  const valueToCopy = isEditing ? composerValue : getMessageText(message);
981
988
  navigator.clipboard.writeText(valueToCopy);
982
989
  setIsCopied(true);
983
990
  setTimeout(() => setIsCopied(false), copiedDuration);
984
- };
991
+ }, [useComposer, useMessage, copiedDuration]);
992
+ if (!hasCopyableContent) return null;
993
+ return callback;
985
994
  };
986
995
 
987
996
  // src/primitives/actionBar/ActionBarCopy.tsx
988
997
  var ActionBarCopy = createActionButton(useCopyMessage);
989
998
 
990
999
  // src/actions/useReloadMessage.tsx
1000
+ var import_react27 = require("react");
991
1001
  var useReloadMessage = () => {
992
1002
  const { useThread, useViewport } = useAssistantContext();
993
1003
  const { useMessage } = useMessageContext();
@@ -995,29 +1005,32 @@ var useReloadMessage = () => {
995
1005
  [useThread, useMessage],
996
1006
  (t, m) => t.isRunning || m.message.role !== "assistant"
997
1007
  );
998
- if (disabled) return null;
999
- return () => {
1008
+ const callback = (0, import_react27.useCallback)(() => {
1000
1009
  const { parentId } = useMessage.getState();
1001
1010
  useThread.getState().startRun(parentId);
1002
1011
  useViewport.getState().scrollToBottom();
1003
- };
1012
+ }, [useMessage, useThread, useViewport]);
1013
+ if (disabled) return null;
1014
+ return callback;
1004
1015
  };
1005
1016
 
1006
1017
  // src/primitives/actionBar/ActionBarReload.tsx
1007
1018
  var ActionBarReload = createActionButton(useReloadMessage);
1008
1019
 
1009
1020
  // src/actions/useBeginMessageEdit.tsx
1021
+ var import_react28 = require("react");
1010
1022
  var useBeginMessageEdit = () => {
1011
1023
  const { useMessage, useComposer } = useMessageContext();
1012
1024
  const disabled = useCombinedStore(
1013
1025
  [useMessage, useComposer],
1014
1026
  (m, c) => m.message.role !== "user" || c.isEditing
1015
1027
  );
1016
- if (disabled) return null;
1017
- return () => {
1028
+ const callback = (0, import_react28.useCallback)(() => {
1018
1029
  const { edit } = useComposer.getState();
1019
1030
  edit();
1020
- };
1031
+ }, [useComposer]);
1032
+ if (disabled) return null;
1033
+ return callback;
1021
1034
  };
1022
1035
 
1023
1036
  // src/primitives/actionBar/ActionBarEdit.tsx
@@ -1031,10 +1044,10 @@ __export(contentPart_exports, {
1031
1044
  });
1032
1045
 
1033
1046
  // src/adapters/vercel/VercelAIAssistantProvider.tsx
1034
- var import_react26 = require("react");
1047
+ var import_react31 = require("react");
1035
1048
 
1036
1049
  // src/adapters/vercel/useDummyAIAssistantContext.tsx
1037
- var import_react24 = require("react");
1050
+ var import_react29 = require("react");
1038
1051
  var import_zustand5 = require("zustand");
1039
1052
 
1040
1053
  // src/utils/context/stores/ViewportStore.tsx
@@ -1080,7 +1093,7 @@ var makeDummyThreadStore = () => {
1080
1093
  }));
1081
1094
  };
1082
1095
  var useDummyAIAssistantContext = () => {
1083
- const [context] = (0, import_react24.useState)(() => {
1096
+ const [context] = (0, import_react29.useState)(() => {
1084
1097
  const useThread = makeDummyThreadStore();
1085
1098
  const useViewport = makeViewportStore();
1086
1099
  const useComposer = makeThreadComposerStore(useThread);
@@ -1091,7 +1104,7 @@ var useDummyAIAssistantContext = () => {
1091
1104
 
1092
1105
  // src/adapters/vercel/useVercelAIThreadState.tsx
1093
1106
  var import_react_use_callback_ref3 = require("@radix-ui/react-use-callback-ref");
1094
- var import_react25 = require("react");
1107
+ var import_react30 = require("react");
1095
1108
 
1096
1109
  // src/adapters/idUtils.tsx
1097
1110
  var import_non_secure = require("nanoid/non-secure");
@@ -1330,11 +1343,11 @@ var getIsRunning = (vercel) => {
1330
1343
  return vercel.status === "in_progress";
1331
1344
  };
1332
1345
  var useVercelAIThreadState = (vercel) => {
1333
- const [data] = (0, import_react25.useState)(() => new MessageRepository());
1346
+ const [data] = (0, import_react30.useState)(() => new MessageRepository());
1334
1347
  const isRunning = getIsRunning(vercel);
1335
- const converter = (0, import_react25.useMemo)(() => new ThreadMessageConverter(), []);
1336
- const assistantOptimisticIdRef = (0, import_react25.useRef)(null);
1337
- const messages = (0, import_react25.useMemo)(() => {
1348
+ const converter = (0, import_react30.useMemo)(() => new ThreadMessageConverter(), []);
1349
+ const assistantOptimisticIdRef = (0, import_react30.useRef)(null);
1350
+ const messages = (0, import_react30.useMemo)(() => {
1338
1351
  const lastMessageId = vercel.messages.at(-1)?.id;
1339
1352
  const convertCallback = (message, cache) => {
1340
1353
  const status = lastMessageId === message.id && isRunning ? "in_progress" : "done";
@@ -1364,7 +1377,7 @@ var useVercelAIThreadState = (vercel) => {
1364
1377
  data.resetHead(assistantOptimisticIdRef.current ?? vm.at(-1)?.id ?? null);
1365
1378
  return data.getMessages();
1366
1379
  }, [converter, data, isRunning, vercel.messages]);
1367
- const getBranches2 = (0, import_react25.useCallback)(
1380
+ const getBranches2 = (0, import_react30.useCallback)(
1368
1381
  (messageId) => {
1369
1382
  return data.getBranches(messageId);
1370
1383
  },
@@ -1403,7 +1416,7 @@ var useVercelAIThreadState = (vercel) => {
1403
1416
  vercel.setInput(lastMessage.content);
1404
1417
  }
1405
1418
  });
1406
- return (0, import_react25.useMemo)(
1419
+ return (0, import_react30.useMemo)(
1407
1420
  () => ({
1408
1421
  isRunning,
1409
1422
  messages,
@@ -1434,10 +1447,10 @@ var VercelAIAssistantProvider = ({
1434
1447
  const context = useDummyAIAssistantContext();
1435
1448
  const vercel = "chat" in rest ? rest.chat : rest.assistant;
1436
1449
  const threadState = useVercelAIThreadState(vercel);
1437
- (0, import_react26.useMemo)(() => {
1450
+ (0, import_react31.useMemo)(() => {
1438
1451
  context.useThread.setState(threadState, true);
1439
1452
  }, [context, threadState]);
1440
- (0, import_react26.useMemo)(() => {
1453
+ (0, import_react31.useMemo)(() => {
1441
1454
  context.useComposer.setState({
1442
1455
  value: vercel.input,
1443
1456
  setValue: vercel.setInput
@@ -1447,7 +1460,7 @@ var VercelAIAssistantProvider = ({
1447
1460
  };
1448
1461
 
1449
1462
  // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1450
- var import_react27 = require("react");
1463
+ var import_react32 = require("react");
1451
1464
  var import_jsx_runtime22 = require("react/jsx-runtime");
1452
1465
  var vercelToThreadMessage2 = (converter, rawMessage) => {
1453
1466
  const message = converter(rawMessage);
@@ -1485,12 +1498,12 @@ var VercelRSCAssistantProvider = ({
1485
1498
  reload
1486
1499
  }) => {
1487
1500
  const context = useDummyAIAssistantContext();
1488
- const [isRunning, setIsRunning] = (0, import_react27.useState)(false);
1489
- const withRunning = (0, import_react27.useCallback)((callback) => {
1501
+ const [isRunning, setIsRunning] = (0, import_react32.useState)(false);
1502
+ const withRunning = (0, import_react32.useCallback)((callback) => {
1490
1503
  setIsRunning(true);
1491
1504
  return callback.finally(() => setIsRunning(false));
1492
1505
  }, []);
1493
- const [converter, convertCallback] = (0, import_react27.useMemo)(() => {
1506
+ const [converter, convertCallback] = (0, import_react32.useMemo)(() => {
1494
1507
  const rscConverter = convertMessage ?? ((m) => m);
1495
1508
  const convertCallback2 = (m, cache) => {
1496
1509
  if (cache) return cache;
@@ -1498,10 +1511,10 @@ var VercelRSCAssistantProvider = ({
1498
1511
  };
1499
1512
  return [new ThreadMessageConverter(), convertCallback2];
1500
1513
  }, [convertMessage]);
1501
- const messages = (0, import_react27.useMemo)(() => {
1514
+ const messages = (0, import_react32.useMemo)(() => {
1502
1515
  return converter.convertMessages(convertCallback, vercelMessages);
1503
1516
  }, [converter, convertCallback, vercelMessages]);
1504
- const append = (0, import_react27.useCallback)(
1517
+ const append = (0, import_react32.useCallback)(
1505
1518
  async (message) => {
1506
1519
  if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? null)) {
1507
1520
  if (!edit)
@@ -1515,7 +1528,7 @@ var VercelRSCAssistantProvider = ({
1515
1528
  },
1516
1529
  [context, withRunning, appendCallback, edit]
1517
1530
  );
1518
- const startRun = (0, import_react27.useCallback)(
1531
+ const startRun = (0, import_react32.useCallback)(
1519
1532
  async (parentId) => {
1520
1533
  if (!reload)
1521
1534
  throw new Error(
@@ -1525,7 +1538,7 @@ var VercelRSCAssistantProvider = ({
1525
1538
  },
1526
1539
  [withRunning, reload]
1527
1540
  );
1528
- (0, import_react27.useMemo)(() => {
1541
+ (0, import_react32.useMemo)(() => {
1529
1542
  context.useThread.setState(
1530
1543
  {
1531
1544
  messages,
@@ -1543,7 +1556,7 @@ var VercelRSCAssistantProvider = ({
1543
1556
  };
1544
1557
 
1545
1558
  // src/adapters/core/utils/useAssistantContext.tsx
1546
- var import_react28 = require("react");
1559
+ var import_react33 = require("react");
1547
1560
  var import_zustand6 = require("zustand");
1548
1561
 
1549
1562
  // src/adapters/core/utils/AssistantMessageRepository.tsx
@@ -1634,9 +1647,11 @@ var makeThreadStore = (runtimeRef) => {
1634
1647
  };
1635
1648
  };
1636
1649
  var useAssistantContext2 = (runtime) => {
1637
- const runtimeRef = (0, import_react28.useRef)(runtime);
1638
- runtimeRef.current = runtime;
1639
- const [{ context, onNewMessage, onRunningChange }] = (0, import_react28.useState)(() => {
1650
+ const runtimeRef = (0, import_react33.useRef)(runtime);
1651
+ (0, import_react33.useInsertionEffect)(() => {
1652
+ runtimeRef.current = runtime;
1653
+ });
1654
+ const [{ context, onNewMessage, onRunningChange }] = (0, import_react33.useState)(() => {
1640
1655
  const { useThread, onNewMessage: onNewMessage2, onRunningChange: onRunningChange2 } = makeThreadStore(runtimeRef);
1641
1656
  const useViewport = makeViewportStore();
1642
1657
  const useComposer = makeThreadComposerStore(useThread);
@@ -1646,10 +1661,10 @@ var useAssistantContext2 = (runtime) => {
1646
1661
  onRunningChange: onRunningChange2
1647
1662
  };
1648
1663
  });
1649
- (0, import_react28.useEffect)(() => {
1664
+ (0, import_react33.useEffect)(() => {
1650
1665
  return runtime.subscribeToMessageUpdates(onNewMessage);
1651
1666
  }, [runtime, onNewMessage]);
1652
- (0, import_react28.useEffect)(() => {
1667
+ (0, import_react33.useEffect)(() => {
1653
1668
  return runtime.subscribeToStatusUpdates(onRunningChange);
1654
1669
  }, [runtime, onRunningChange]);
1655
1670
  return context;
@@ -1663,7 +1678,7 @@ var AssistantProvider = ({ children, runtime }) => {
1663
1678
  };
1664
1679
 
1665
1680
  // src/adapters/core/local/useLocalRuntime.tsx
1666
- var import_react29 = require("react");
1681
+ var import_react34 = require("react");
1667
1682
 
1668
1683
  // src/adapters/core/local/LocalRuntime.tsx
1669
1684
  var LocalRuntime = class {
@@ -1748,7 +1763,7 @@ var LocalRuntime = class {
1748
1763
 
1749
1764
  // src/adapters/core/local/useLocalRuntime.tsx
1750
1765
  var useLocalRuntime = (adapter) => {
1751
- const [runtime] = (0, import_react29.useState)(() => new LocalRuntime(adapter));
1766
+ const [runtime] = (0, import_react34.useState)(() => new LocalRuntime(adapter));
1752
1767
  runtime.adapter = adapter;
1753
1768
  return runtime;
1754
1769
  };
package/dist/index.mjs CHANGED
@@ -129,18 +129,22 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
129
129
  const ref = useComposedRefs(forwardedRef, divRef);
130
130
  const { useViewport } = useAssistantContext();
131
131
  const firstRenderRef = useRef(true);
132
+ const isScrollingToBottomRef = useRef(false);
132
133
  const lastScrollTop = useRef(0);
133
134
  const scrollToBottom = () => {
134
135
  const div = messagesEndRef.current;
135
136
  if (!div || !autoScroll) return;
136
137
  const behavior = firstRenderRef.current ? "instant" : "auto";
137
138
  firstRenderRef.current = false;
138
- useViewport.setState({ isAtBottom: true });
139
+ isScrollingToBottomRef.current = true;
139
140
  div.scrollIntoView({ behavior });
140
141
  };
141
142
  useOnResizeContent(divRef, () => {
142
- if (!useViewport.getState().isAtBottom) return;
143
- scrollToBottom();
143
+ if (!isScrollingToBottomRef.current && !useViewport.getState().isAtBottom) {
144
+ handleScroll();
145
+ } else {
146
+ scrollToBottom();
147
+ }
144
148
  });
145
149
  useOnScrollToBottom(() => {
146
150
  scrollToBottom();
@@ -152,6 +156,7 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
152
156
  const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
153
157
  if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
154
158
  } else if (newIsAtBottom !== isAtBottom) {
159
+ isScrollingToBottomRef.current = false;
155
160
  useViewport.setState({ isAtBottom: newIsAtBottom });
156
161
  }
157
162
  lastScrollTop.current = div.scrollTop;
@@ -818,6 +823,7 @@ __export(branchPicker_exports, {
818
823
  });
819
824
 
820
825
  // src/actions/useGoToNextBranch.tsx
826
+ import { useCallback as useCallback2 } from "react";
821
827
  var useGoToNextBranch = () => {
822
828
  const { useThread } = useAssistantContext();
823
829
  const { useMessage, useComposer } = useMessageContext();
@@ -825,11 +831,12 @@ var useGoToNextBranch = () => {
825
831
  [useMessage, useComposer],
826
832
  (m, c) => c.isEditing || m.branches.indexOf(m.message.id) + 1 >= m.branches.length
827
833
  );
828
- if (disabled) return null;
829
- return () => {
834
+ const callback = useCallback2(() => {
830
835
  const { message, branches } = useMessage.getState();
831
836
  useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
832
- };
837
+ }, [useMessage, useThread]);
838
+ if (disabled) return null;
839
+ return callback;
833
840
  };
834
841
 
835
842
  // src/utils/createActionButton.tsx
@@ -861,6 +868,7 @@ var createActionButton = (useActionButton) => {
861
868
  var BranchPickerNext = createActionButton(useGoToNextBranch);
862
869
 
863
870
  // src/actions/useGoToPreviousBranch.tsx
871
+ import { useCallback as useCallback3 } from "react";
864
872
  var useGoToPreviousBranch = () => {
865
873
  const { useThread } = useAssistantContext();
866
874
  const { useMessage, useComposer } = useMessageContext();
@@ -868,13 +876,12 @@ var useGoToPreviousBranch = () => {
868
876
  [useMessage, useComposer],
869
877
  (m, c) => c.isEditing || m.branches.indexOf(m.message.id) <= 0
870
878
  );
871
- if (disabled) return null;
872
- return () => {
879
+ const callback = useCallback3(() => {
873
880
  const { message, branches } = useMessage.getState();
874
- useThread.getState().switchToBranch(
875
- branches[branches.indexOf(message.id) - 1]
876
- );
877
- };
881
+ useThread.getState().switchToBranch(branches[branches.indexOf(message.id) - 1]);
882
+ }, [useMessage, useThread]);
883
+ if (disabled) return null;
884
+ return callback;
878
885
  };
879
886
 
880
887
  // src/primitives/branchPicker/BranchPickerPrevious.tsx
@@ -948,6 +955,7 @@ var ActionBarRoot = forwardRef13(({ hideWhenRunning, autohide, autohideFloat, ..
948
955
  });
949
956
 
950
957
  // src/actions/useCopyMessage.tsx
958
+ import { useCallback as useCallback4 } from "react";
951
959
  var useCopyMessage = ({ copiedDuration = 3e3 }) => {
952
960
  const { useMessage, useComposer } = useMessageContext();
953
961
  const hasCopyableContent = useCombinedStore(
@@ -956,21 +964,23 @@ var useCopyMessage = ({ copiedDuration = 3e3 }) => {
956
964
  return c.isEditing || m.message.content.some((c2) => c2.type === "text");
957
965
  }
958
966
  );
959
- if (!hasCopyableContent) return null;
960
- return () => {
967
+ const callback = useCallback4(() => {
961
968
  const { isEditing, value: composerValue } = useComposer.getState();
962
969
  const { message, setIsCopied } = useMessage.getState();
963
970
  const valueToCopy = isEditing ? composerValue : getMessageText(message);
964
971
  navigator.clipboard.writeText(valueToCopy);
965
972
  setIsCopied(true);
966
973
  setTimeout(() => setIsCopied(false), copiedDuration);
967
- };
974
+ }, [useComposer, useMessage, copiedDuration]);
975
+ if (!hasCopyableContent) return null;
976
+ return callback;
968
977
  };
969
978
 
970
979
  // src/primitives/actionBar/ActionBarCopy.tsx
971
980
  var ActionBarCopy = createActionButton(useCopyMessage);
972
981
 
973
982
  // src/actions/useReloadMessage.tsx
983
+ import { useCallback as useCallback5 } from "react";
974
984
  var useReloadMessage = () => {
975
985
  const { useThread, useViewport } = useAssistantContext();
976
986
  const { useMessage } = useMessageContext();
@@ -978,29 +988,32 @@ var useReloadMessage = () => {
978
988
  [useThread, useMessage],
979
989
  (t, m) => t.isRunning || m.message.role !== "assistant"
980
990
  );
981
- if (disabled) return null;
982
- return () => {
991
+ const callback = useCallback5(() => {
983
992
  const { parentId } = useMessage.getState();
984
993
  useThread.getState().startRun(parentId);
985
994
  useViewport.getState().scrollToBottom();
986
- };
995
+ }, [useMessage, useThread, useViewport]);
996
+ if (disabled) return null;
997
+ return callback;
987
998
  };
988
999
 
989
1000
  // src/primitives/actionBar/ActionBarReload.tsx
990
1001
  var ActionBarReload = createActionButton(useReloadMessage);
991
1002
 
992
1003
  // src/actions/useBeginMessageEdit.tsx
1004
+ import { useCallback as useCallback6 } from "react";
993
1005
  var useBeginMessageEdit = () => {
994
1006
  const { useMessage, useComposer } = useMessageContext();
995
1007
  const disabled = useCombinedStore(
996
1008
  [useMessage, useComposer],
997
1009
  (m, c) => m.message.role !== "user" || c.isEditing
998
1010
  );
999
- if (disabled) return null;
1000
- return () => {
1011
+ const callback = useCallback6(() => {
1001
1012
  const { edit } = useComposer.getState();
1002
1013
  edit();
1003
- };
1014
+ }, [useComposer]);
1015
+ if (disabled) return null;
1016
+ return callback;
1004
1017
  };
1005
1018
 
1006
1019
  // src/primitives/actionBar/ActionBarEdit.tsx
@@ -1074,7 +1087,7 @@ var useDummyAIAssistantContext = () => {
1074
1087
 
1075
1088
  // src/adapters/vercel/useVercelAIThreadState.tsx
1076
1089
  import { useCallbackRef as useCallbackRef3 } from "@radix-ui/react-use-callback-ref";
1077
- import { useCallback as useCallback2, useMemo as useMemo5, useRef as useRef4, useState as useState4 } from "react";
1090
+ import { useCallback as useCallback7, useMemo as useMemo5, useRef as useRef4, useState as useState4 } from "react";
1078
1091
 
1079
1092
  // src/adapters/idUtils.tsx
1080
1093
  import { customAlphabet } from "nanoid/non-secure";
@@ -1347,7 +1360,7 @@ var useVercelAIThreadState = (vercel) => {
1347
1360
  data.resetHead(assistantOptimisticIdRef.current ?? vm.at(-1)?.id ?? null);
1348
1361
  return data.getMessages();
1349
1362
  }, [converter, data, isRunning, vercel.messages]);
1350
- const getBranches2 = useCallback2(
1363
+ const getBranches2 = useCallback7(
1351
1364
  (messageId) => {
1352
1365
  return data.getBranches(messageId);
1353
1366
  },
@@ -1431,7 +1444,7 @@ var VercelAIAssistantProvider = ({
1431
1444
 
1432
1445
  // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1433
1446
  import {
1434
- useCallback as useCallback3,
1447
+ useCallback as useCallback8,
1435
1448
  useMemo as useMemo7,
1436
1449
  useState as useState5
1437
1450
  } from "react";
@@ -1473,7 +1486,7 @@ var VercelRSCAssistantProvider = ({
1473
1486
  }) => {
1474
1487
  const context = useDummyAIAssistantContext();
1475
1488
  const [isRunning, setIsRunning] = useState5(false);
1476
- const withRunning = useCallback3((callback) => {
1489
+ const withRunning = useCallback8((callback) => {
1477
1490
  setIsRunning(true);
1478
1491
  return callback.finally(() => setIsRunning(false));
1479
1492
  }, []);
@@ -1488,7 +1501,7 @@ var VercelRSCAssistantProvider = ({
1488
1501
  const messages = useMemo7(() => {
1489
1502
  return converter.convertMessages(convertCallback, vercelMessages);
1490
1503
  }, [converter, convertCallback, vercelMessages]);
1491
- const append = useCallback3(
1504
+ const append = useCallback8(
1492
1505
  async (message) => {
1493
1506
  if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? null)) {
1494
1507
  if (!edit)
@@ -1502,7 +1515,7 @@ var VercelRSCAssistantProvider = ({
1502
1515
  },
1503
1516
  [context, withRunning, appendCallback, edit]
1504
1517
  );
1505
- const startRun = useCallback3(
1518
+ const startRun = useCallback8(
1506
1519
  async (parentId) => {
1507
1520
  if (!reload)
1508
1521
  throw new Error(
@@ -1530,7 +1543,12 @@ var VercelRSCAssistantProvider = ({
1530
1543
  };
1531
1544
 
1532
1545
  // src/adapters/core/utils/useAssistantContext.tsx
1533
- import { useEffect as useEffect4, useRef as useRef5, useState as useState6 } from "react";
1546
+ import {
1547
+ useEffect as useEffect4,
1548
+ useInsertionEffect,
1549
+ useRef as useRef5,
1550
+ useState as useState6
1551
+ } from "react";
1534
1552
  import { create as create6 } from "zustand";
1535
1553
 
1536
1554
  // src/adapters/core/utils/AssistantMessageRepository.tsx
@@ -1622,7 +1640,9 @@ var makeThreadStore = (runtimeRef) => {
1622
1640
  };
1623
1641
  var useAssistantContext2 = (runtime) => {
1624
1642
  const runtimeRef = useRef5(runtime);
1625
- runtimeRef.current = runtime;
1643
+ useInsertionEffect(() => {
1644
+ runtimeRef.current = runtime;
1645
+ });
1626
1646
  const [{ context, onNewMessage, onRunningChange }] = useState6(() => {
1627
1647
  const { useThread, onNewMessage: onNewMessage2, onRunningChange: onRunningChange2 } = makeThreadStore(runtimeRef);
1628
1648
  const useViewport = makeViewportStore();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react",
3
- "version": "0.0.18",
3
+ "version": "0.0.19",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": {