@assistant-ui/react 0.0.18 → 0.0.19

Sign up to get free protection for your applications and to get access to all the features.
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
  ".": {