@assistant-ui/react 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.js CHANGED
@@ -53,6 +53,7 @@ __export(thread_exports, {
53
53
  If: () => ThreadIf,
54
54
  Messages: () => ThreadMessages,
55
55
  Root: () => ThreadRoot,
56
+ ScrollToBottom: () => ThreadScrollToBottom,
56
57
  Viewport: () => ThreadViewport
57
58
  });
58
59
 
@@ -106,7 +107,7 @@ var ThreadEmpty = ({ children }) => {
106
107
  var import_primitive = require("@radix-ui/primitive");
107
108
  var import_react_compose_refs = require("@radix-ui/react-compose-refs");
108
109
  var import_react_primitive2 = require("@radix-ui/react-primitive");
109
- var import_react4 = require("react");
110
+ var import_react5 = require("react");
110
111
 
111
112
  // src/utils/hooks/useOnResizeContent.tsx
112
113
  var import_react3 = require("react");
@@ -147,22 +148,51 @@ var useOnResizeContent = (ref, callback) => {
147
148
  }, [ref.current]);
148
149
  };
149
150
 
151
+ // src/utils/hooks/useOnScrollToBottom.tsx
152
+ var import_react4 = require("react");
153
+ var useOnScrollToBottom = (callback) => {
154
+ const callbackRef = (0, import_react4.useRef)(callback);
155
+ callbackRef.current = callback;
156
+ const { useThread } = useAssistantContext();
157
+ (0, import_react4.useEffect)(() => {
158
+ return useThread.getState().onScrollToBottom(() => {
159
+ callbackRef.current();
160
+ });
161
+ }, [useThread]);
162
+ };
163
+
150
164
  // src/primitives/thread/ThreadViewport.tsx
151
- var ThreadViewport = (0, import_react4.forwardRef)(({ onScroll, children, ...rest }, forwardedRef) => {
152
- const divRef = (0, import_react4.useRef)(null);
165
+ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScroll, children, ...rest }, forwardedRef) => {
166
+ const messagesEndRef = (0, import_react5.useRef)(null);
167
+ const divRef = (0, import_react5.useRef)(null);
153
168
  const ref = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, divRef);
154
- const [isAtBottom, setIsAtBottom] = (0, import_react4.useState)(true);
169
+ const { useThread } = useAssistantContext();
170
+ const firstRenderRef = (0, import_react5.useRef)(true);
171
+ const scrollToBottom = () => {
172
+ const div = messagesEndRef.current;
173
+ if (!div || !autoScroll)
174
+ return;
175
+ const behavior = firstRenderRef.current ? "instant" : "auto";
176
+ firstRenderRef.current = false;
177
+ div.scrollIntoView({ behavior });
178
+ };
155
179
  useOnResizeContent(divRef, () => {
156
- const div = divRef.current;
157
- if (!div || !isAtBottom)
180
+ if (!useThread.getState().isAtBottom)
158
181
  return;
159
- div.scrollTop = div.scrollHeight;
182
+ scrollToBottom();
183
+ });
184
+ useOnScrollToBottom(() => {
185
+ scrollToBottom();
160
186
  });
161
187
  const handleScroll = () => {
162
188
  const div = divRef.current;
163
189
  if (!div)
164
190
  return;
165
- setIsAtBottom(div.scrollHeight - div.scrollTop <= div.clientHeight + 50);
191
+ const isAtBottom = useThread.getState().isAtBottom;
192
+ const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight + 50;
193
+ if (newIsAtBottom !== isAtBottom) {
194
+ useThread.setState({ isAtBottom: newIsAtBottom });
195
+ }
166
196
  };
167
197
  return /* @__PURE__ */ React.createElement(
168
198
  import_react_primitive2.Primitive.div,
@@ -171,12 +201,13 @@ var ThreadViewport = (0, import_react4.forwardRef)(({ onScroll, children, ...res
171
201
  onScroll: (0, import_primitive.composeEventHandlers)(onScroll, handleScroll),
172
202
  ref
173
203
  },
174
- children
204
+ children,
205
+ /* @__PURE__ */ React.createElement("div", { ref: messagesEndRef })
175
206
  );
176
207
  });
177
208
 
178
209
  // src/vercel/useVercelAIBranches.tsx
179
- var import_react5 = require("react");
210
+ var import_react6 = require("react");
180
211
  var ROOT_ID = "__ROOT_ID__";
181
212
  var UPCOMING_MESSAGE_ID = "__UPCOMING_MESSAGE_ID__";
182
213
  var updateBranchData = (data, messages) => {
@@ -243,20 +274,20 @@ var sliceMessagesUntil = (messages, message) => {
243
274
  throw new Error("Unexpected: Message not found");
244
275
  return messages.slice(0, messageIdx);
245
276
  };
246
- var useVercelAIBranches = (chat) => {
247
- const data = (0, import_react5.useRef)({
277
+ var useVercelAIBranches = (chat, context) => {
278
+ const data = (0, import_react6.useRef)({
248
279
  parentMap: /* @__PURE__ */ new Map(),
249
280
  branchMap: /* @__PURE__ */ new Map(),
250
281
  snapshots: /* @__PURE__ */ new Map()
251
282
  }).current;
252
283
  updateBranchData(data, chat.messages);
253
- const getBranchState = (0, import_react5.useCallback)(
284
+ const getBranchState = (0, import_react6.useCallback)(
254
285
  (message) => {
255
286
  return getBranchStateImpl(data, chat.messages, message);
256
287
  },
257
288
  [data, chat.messages]
258
289
  );
259
- const switchToBranch = (0, import_react5.useCallback)(
290
+ const switchToBranch = (0, import_react6.useCallback)(
260
291
  (message, branchId) => {
261
292
  const newMessages = switchToBranchImpl(
262
293
  data,
@@ -269,30 +300,32 @@ var useVercelAIBranches = (chat) => {
269
300
  [data, chat.messages, chat.setMessages]
270
301
  );
271
302
  const reloadMaybe = "reload" in chat ? chat.reload : void 0;
272
- const reloadAt = (0, import_react5.useCallback)(
303
+ const reloadAt = (0, import_react6.useCallback)(
273
304
  async (message) => {
274
305
  if (!reloadMaybe)
275
306
  throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
276
307
  const newMessages = sliceMessagesUntil(chat.messages, message);
277
308
  chat.setMessages(newMessages);
309
+ context.useThread.getState().scrollToBottom();
278
310
  await reloadMaybe();
279
311
  },
280
- [chat.messages, chat.setMessages, reloadMaybe]
312
+ [context, chat.messages, chat.setMessages, reloadMaybe]
281
313
  );
282
- const editAt = (0, import_react5.useCallback)(
314
+ const editAt = (0, import_react6.useCallback)(
283
315
  async (message, newMessage) => {
284
316
  const newMessages = sliceMessagesUntil(chat.messages, message);
285
317
  chat.setMessages(newMessages);
286
318
  if (newMessage.content[0]?.type !== "text")
287
319
  throw new Error("Only text content is currently supported");
320
+ context.useThread.getState().scrollToBottom();
288
321
  await chat.append({
289
322
  role: "user",
290
323
  content: newMessage.content[0].text
291
324
  });
292
325
  },
293
- [chat.messages, chat.setMessages, chat.append]
326
+ [context, chat.messages, chat.setMessages, chat.append]
294
327
  );
295
- return (0, import_react5.useMemo)(
328
+ return (0, import_react6.useMemo)(
296
329
  () => ({
297
330
  getBranchState,
298
331
  switchToBranch,
@@ -306,24 +339,27 @@ var hasUpcomingMessage = (thread) => {
306
339
  return thread.isLoading && thread.messages[thread.messages.length - 1]?.role !== "assistant";
307
340
  };
308
341
 
309
- // src/utils/context/ComposerState.ts
310
- var import_react7 = require("react");
342
+ // src/utils/context/useComposerContext.ts
343
+ var import_react8 = require("react");
311
344
 
312
- // src/utils/context/MessageContext.ts
313
- var import_react6 = require("react");
314
- var MessageContext = (0, import_react6.createContext)(null);
345
+ // src/utils/context/useMessageContext.ts
346
+ var import_react7 = require("react");
347
+ var MessageContext = (0, import_react7.createContext)(null);
315
348
  var useMessageContext = () => {
316
- const context = (0, import_react6.useContext)(MessageContext);
349
+ const context = (0, import_react7.useContext)(MessageContext);
317
350
  if (!context)
318
351
  throw new Error("useMessageContext must be used within a MessageProvider");
319
352
  return context;
320
353
  };
321
354
 
322
- // src/utils/context/ComposerState.ts
355
+ // src/utils/context/useComposerContext.ts
323
356
  var useComposerContext = () => {
324
357
  const { useComposer: useAssisstantComposer } = useAssistantContext();
325
- const { useComposer: useMessageComposer } = (0, import_react7.useContext)(MessageContext) ?? {};
326
- return { useComposer: useMessageComposer ?? useAssisstantComposer };
358
+ const { useComposer: useMessageComposer } = (0, import_react8.useContext)(MessageContext) ?? {};
359
+ return {
360
+ useComposer: useMessageComposer ?? useAssisstantComposer,
361
+ type: useMessageComposer ? "message" : "assistant"
362
+ };
327
363
  };
328
364
 
329
365
  // src/primitives/composer/ComposerIf.tsx
@@ -352,7 +388,7 @@ __export(message_exports, {
352
388
  });
353
389
 
354
390
  // src/primitives/message/MessageProvider.tsx
355
- var import_react8 = require("react");
391
+ var import_react9 = require("react");
356
392
  var import_zustand = require("zustand");
357
393
  var import_shallow = require("zustand/react/shallow");
358
394
  var getIsLast = (thread, message) => {
@@ -361,7 +397,7 @@ var getIsLast = (thread, message) => {
361
397
  };
362
398
  var useMessageContext2 = () => {
363
399
  const { useBranchObserver } = useAssistantContext();
364
- const [context] = (0, import_react8.useState)(() => {
400
+ const [context] = (0, import_react9.useState)(() => {
365
401
  const useMessage = (0, import_zustand.create)(() => ({
366
402
  message: null,
367
403
  isLast: false,
@@ -418,9 +454,9 @@ var MessageProvider = ({
418
454
  (0, import_shallow.useShallow)((b) => b.getBranchState(message))
419
455
  );
420
456
  const isLast = useThread((thread) => getIsLast(thread, message));
421
- const [isCopied, setIsCopied] = (0, import_react8.useState)(false);
422
- const [isHovering, setIsHovering] = (0, import_react8.useState)(false);
423
- (0, import_react8.useMemo)(() => {
457
+ const [isCopied, setIsCopied] = (0, import_react9.useState)(false);
458
+ const [isHovering, setIsHovering] = (0, import_react9.useState)(false);
459
+ (0, import_react9.useMemo)(() => {
424
460
  context.useMessage.setState(
425
461
  {
426
462
  message,
@@ -440,8 +476,8 @@ var MessageProvider = ({
440
476
  // src/primitives/message/MessageRoot.tsx
441
477
  var import_primitive2 = require("@radix-ui/primitive");
442
478
  var import_react_primitive3 = require("@radix-ui/react-primitive");
443
- var import_react9 = require("react");
444
- var MessageRoot = (0, import_react9.forwardRef)(
479
+ var import_react10 = require("react");
480
+ var MessageRoot = (0, import_react10.forwardRef)(
445
481
  ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
446
482
  const { useMessage } = useMessageContext();
447
483
  const setIsHovering = useMessage((s) => s.setIsHovering);
@@ -566,6 +602,29 @@ var ThreadMessages = ({ components }) => {
566
602
  ));
567
603
  };
568
604
 
605
+ // src/primitives/thread/ThreadScrollToBottom.tsx
606
+ var import_primitive3 = require("@radix-ui/primitive");
607
+ var import_react_primitive4 = require("@radix-ui/react-primitive");
608
+ var import_react11 = require("react");
609
+ var ThreadScrollToBottom = (0, import_react11.forwardRef)(({ onClick, ...rest }, ref) => {
610
+ const { useThread } = useAssistantContext();
611
+ const isAtBottom = useThread((s) => s.isAtBottom);
612
+ const handleScrollToBottom = () => {
613
+ const thread = useThread.getState();
614
+ thread.scrollToBottom();
615
+ };
616
+ if (isAtBottom)
617
+ return null;
618
+ return /* @__PURE__ */ React.createElement(
619
+ import_react_primitive4.Primitive.button,
620
+ {
621
+ ...rest,
622
+ ref,
623
+ onClick: (0, import_primitive3.composeEventHandlers)(onClick, handleScrollToBottom)
624
+ }
625
+ );
626
+ });
627
+
569
628
  // src/primitives/composer/index.ts
570
629
  var composer_exports = {};
571
630
  __export(composer_exports, {
@@ -577,33 +636,15 @@ __export(composer_exports, {
577
636
  });
578
637
 
579
638
  // src/primitives/composer/ComposerRoot.tsx
580
- var import_primitive3 = require("@radix-ui/primitive");
639
+ var import_primitive4 = require("@radix-ui/primitive");
581
640
  var import_react_compose_refs2 = require("@radix-ui/react-compose-refs");
582
- var import_react_primitive4 = require("@radix-ui/react-primitive");
583
- var import_react10 = require("react");
584
- var ComposerFormContext = (0, import_react10.createContext)(null);
585
- var useComposerFormContext = () => {
586
- const context = (0, import_react10.useContext)(ComposerFormContext);
587
- if (!context) {
588
- throw new Error(
589
- "Composer compound components cannot be rendered outside the Composer component"
590
- );
591
- }
592
- return context;
593
- };
594
- var ComposerRoot = (0, import_react10.forwardRef)(
641
+ var import_react_primitive5 = require("@radix-ui/react-primitive");
642
+ var import_react12 = require("react");
643
+ var ComposerRoot = (0, import_react12.forwardRef)(
595
644
  ({ onSubmit, ...rest }, forwardedRef) => {
596
645
  const { useComposer } = useComposerContext();
597
- const formRef = (0, import_react10.useRef)(null);
646
+ const formRef = (0, import_react12.useRef)(null);
598
647
  const ref = (0, import_react_compose_refs2.useComposedRefs)(forwardedRef, formRef);
599
- const composerContextValue = (0, import_react10.useMemo)(
600
- () => ({
601
- submit: () => formRef.current?.dispatchEvent(
602
- new Event("submit", { cancelable: true, bubbles: true })
603
- )
604
- }),
605
- []
606
- );
607
648
  const handleSubmit = (e) => {
608
649
  const composerState = useComposer.getState();
609
650
  if (!composerState.isEditing)
@@ -611,73 +652,96 @@ var ComposerRoot = (0, import_react10.forwardRef)(
611
652
  e.preventDefault();
612
653
  composerState.send();
613
654
  };
614
- return /* @__PURE__ */ React.createElement(ComposerFormContext.Provider, { value: composerContextValue }, /* @__PURE__ */ React.createElement(
615
- import_react_primitive4.Primitive.form,
655
+ return /* @__PURE__ */ React.createElement(
656
+ import_react_primitive5.Primitive.form,
616
657
  {
617
658
  ...rest,
618
659
  ref,
619
- onSubmit: (0, import_primitive3.composeEventHandlers)(onSubmit, handleSubmit)
660
+ onSubmit: (0, import_primitive4.composeEventHandlers)(onSubmit, handleSubmit)
620
661
  }
621
- ));
662
+ );
622
663
  }
623
664
  );
624
665
 
625
666
  // src/primitives/composer/ComposerInput.tsx
626
- var import_primitive4 = require("@radix-ui/primitive");
667
+ var import_primitive5 = require("@radix-ui/primitive");
668
+ var import_react_compose_refs3 = require("@radix-ui/react-compose-refs");
627
669
  var import_react_slot = require("@radix-ui/react-slot");
628
- var import_react11 = require("react");
670
+ var import_react13 = require("react");
629
671
  var import_react_textarea_autosize = __toESM(require("react-textarea-autosize"));
630
- var ComposerInput = (0, import_react11.forwardRef)(({ asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
631
- const { useThread } = useAssistantContext();
632
- const isLoading = useThread((t) => t.isLoading);
633
- const { useComposer } = useComposerContext();
634
- const value = useComposer((c) => {
635
- if (!c.isEditing)
636
- return "";
637
- return c.value;
638
- });
639
- const Component = asChild ? import_react_slot.Slot : import_react_textarea_autosize.default;
640
- const composerForm = useComposerFormContext();
641
- const handleKeyPress = (e) => {
642
- if (disabled)
643
- return;
644
- if (e.key === "Escape") {
645
- useComposer.getState().cancel();
646
- }
647
- if (isLoading)
648
- return;
649
- if (e.key === "Enter" && e.shiftKey === false) {
650
- e.preventDefault();
651
- composerForm.submit();
652
- }
653
- };
654
- return /* @__PURE__ */ React.createElement(
655
- Component,
656
- {
657
- value,
658
- ...rest,
659
- ref: forwardedRef,
660
- disabled,
661
- onChange: (0, import_primitive4.composeEventHandlers)(onChange, (e) => {
662
- const composerState = useComposer.getState();
663
- if (!composerState.isEditing)
664
- return;
665
- return composerState.setValue(e.target.value);
666
- }),
667
- onKeyDown: (0, import_primitive4.composeEventHandlers)(onKeyDown, handleKeyPress)
668
- }
669
- );
670
- });
672
+ var ComposerInput = (0, import_react13.forwardRef)(
673
+ ({ autoFocus, asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
674
+ const { useThread } = useAssistantContext();
675
+ const { useComposer, type } = useComposerContext();
676
+ const value = useComposer((c) => {
677
+ if (!c.isEditing)
678
+ return "";
679
+ return c.value;
680
+ });
681
+ const Component = asChild ? import_react_slot.Slot : import_react_textarea_autosize.default;
682
+ const handleKeyPress = (e) => {
683
+ if (disabled)
684
+ return;
685
+ const composer = useComposer.getState();
686
+ if (e.key === "Escape" && composer.canCancel) {
687
+ e.preventDefault();
688
+ useComposer.getState().cancel();
689
+ }
690
+ if (e.key === "Enter" && e.shiftKey === false) {
691
+ const isLoading = useThread.getState().isLoading;
692
+ if (!isLoading) {
693
+ e.preventDefault();
694
+ composer.send();
695
+ }
696
+ }
697
+ };
698
+ const textareaRef = (0, import_react13.useRef)(null);
699
+ const ref = (0, import_react_compose_refs3.useComposedRefs)(forwardedRef, textareaRef);
700
+ const autoFocusEnabled = autoFocus !== false && !disabled;
701
+ const focus = (0, import_react13.useCallback)(() => {
702
+ const textarea = textareaRef.current;
703
+ if (!textarea || !autoFocusEnabled)
704
+ return;
705
+ textarea.focus();
706
+ textarea.setSelectionRange(
707
+ textareaRef.current.value.length,
708
+ textareaRef.current.value.length
709
+ );
710
+ }, [autoFocusEnabled]);
711
+ (0, import_react13.useEffect)(() => focus(), [focus]);
712
+ useOnScrollToBottom(() => {
713
+ if (type === "assistant") {
714
+ focus();
715
+ }
716
+ });
717
+ return /* @__PURE__ */ React.createElement(
718
+ Component,
719
+ {
720
+ value,
721
+ ...rest,
722
+ ref,
723
+ disabled,
724
+ onChange: (0, import_primitive5.composeEventHandlers)(onChange, (e) => {
725
+ const composerState = useComposer.getState();
726
+ if (!composerState.isEditing)
727
+ return;
728
+ return composerState.setValue(e.target.value);
729
+ }),
730
+ onKeyDown: (0, import_primitive5.composeEventHandlers)(onKeyDown, handleKeyPress)
731
+ }
732
+ );
733
+ }
734
+ );
671
735
 
672
736
  // src/primitives/composer/ComposerSend.tsx
673
- var import_react_primitive5 = require("@radix-ui/react-primitive");
674
- var import_react12 = require("react");
675
- var ComposerSend = (0, import_react12.forwardRef)(
737
+ var import_react_primitive6 = require("@radix-ui/react-primitive");
738
+ var import_react14 = require("react");
739
+ var ComposerSend = (0, import_react14.forwardRef)(
676
740
  ({ disabled, ...rest }, ref) => {
677
741
  const { useComposer } = useComposerContext();
678
742
  const hasValue = useComposer((c) => c.isEditing && c.value.length > 0);
679
743
  return /* @__PURE__ */ React.createElement(
680
- import_react_primitive5.Primitive.button,
744
+ import_react_primitive6.Primitive.button,
681
745
  {
682
746
  type: "submit",
683
747
  ...rest,
@@ -689,22 +753,22 @@ var ComposerSend = (0, import_react12.forwardRef)(
689
753
  );
690
754
 
691
755
  // src/primitives/composer/ComposerCancel.tsx
692
- var import_primitive5 = require("@radix-ui/primitive");
693
- var import_react_primitive6 = require("@radix-ui/react-primitive");
694
- var import_react13 = require("react");
695
- var ComposerCancel = (0, import_react13.forwardRef)(({ disabled, onClick, ...rest }, ref) => {
756
+ var import_primitive6 = require("@radix-ui/primitive");
757
+ var import_react_primitive7 = require("@radix-ui/react-primitive");
758
+ var import_react15 = require("react");
759
+ var ComposerCancel = (0, import_react15.forwardRef)(({ disabled, onClick, ...rest }, ref) => {
696
760
  const { useComposer } = useComposerContext();
697
761
  const hasValue = useComposer((c) => c.canCancel);
698
762
  const handleClose = () => {
699
763
  useComposer.getState().cancel();
700
764
  };
701
765
  return /* @__PURE__ */ React.createElement(
702
- import_react_primitive6.Primitive.button,
766
+ import_react_primitive7.Primitive.button,
703
767
  {
704
768
  type: "button",
705
769
  ...rest,
706
770
  ref,
707
- onClick: (0, import_primitive5.composeEventHandlers)(onClick, handleClose),
771
+ onClick: (0, import_primitive6.composeEventHandlers)(onClick, handleClose),
708
772
  disabled: disabled || !hasValue
709
773
  }
710
774
  );
@@ -720,16 +784,41 @@ __export(branchPicker_exports, {
720
784
  Root: () => BranchPickerRoot
721
785
  });
722
786
 
787
+ // src/utils/context/combined/useCombinedStore.ts
788
+ var import_react17 = require("react");
789
+
790
+ // src/utils/context/combined/createCombinedStore.ts
791
+ var import_react16 = require("react");
792
+ var createCombinedStore = (stores) => {
793
+ const subscribe = (callback) => {
794
+ const unsubscribes = stores.map((store) => store.subscribe(callback));
795
+ return () => {
796
+ for (const unsub of unsubscribes) {
797
+ unsub();
798
+ }
799
+ };
800
+ };
801
+ return (selector) => {
802
+ const getSnapshot = () => selector(...stores.map((store) => store.getState()));
803
+ return (0, import_react16.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
804
+ };
805
+ };
806
+
807
+ // src/utils/context/combined/useCombinedStore.ts
808
+ var useCombinedStore = (stores, selector) => {
809
+ const useCombined = (0, import_react17.useMemo)(() => createCombinedStore(stores), stores);
810
+ return useCombined(selector);
811
+ };
812
+
723
813
  // src/actions/useGoToNextBranch.tsx
724
814
  var useGoToNextBranch = () => {
725
815
  const { useThread, useBranchObserver } = useAssistantContext();
726
816
  const { useComposer, useMessage } = useMessageContext();
727
- const isLoading = useThread((s) => s.isLoading);
728
- const isEditing = useComposer((s) => s.isEditing);
729
- const hasNext = useMessage(
730
- ({ branchState: { branchId, branchCount } }) => branchId + 1 < branchCount
817
+ const disabled = useCombinedStore(
818
+ [useThread, useComposer, useMessage],
819
+ (t, c, m) => t.isLoading || c.isEditing || m.branchState.branchId + 1 >= m.branchState.branchCount
731
820
  );
732
- if (isLoading || isEditing || !hasNext)
821
+ if (disabled)
733
822
  return null;
734
823
  return () => {
735
824
  const {
@@ -741,21 +830,21 @@ var useGoToNextBranch = () => {
741
830
  };
742
831
 
743
832
  // src/utils/createActionButton.tsx
744
- var import_react14 = require("react");
745
- var import_react_primitive7 = require("@radix-ui/react-primitive");
746
- var import_primitive6 = require("@radix-ui/primitive");
833
+ var import_react18 = require("react");
834
+ var import_react_primitive8 = require("@radix-ui/react-primitive");
835
+ var import_primitive7 = require("@radix-ui/primitive");
747
836
  var createActionButton = (useActionButton) => {
748
- return (0, import_react14.forwardRef)(
837
+ return (0, import_react18.forwardRef)(
749
838
  (props, forwardedRef) => {
750
839
  const onClick = useActionButton(props);
751
840
  return /* @__PURE__ */ React.createElement(
752
- import_react_primitive7.Primitive.button,
841
+ import_react_primitive8.Primitive.button,
753
842
  {
754
843
  type: "button",
755
844
  disabled: !onClick,
756
845
  ...props,
757
846
  ref: forwardedRef,
758
- onClick: (0, import_primitive6.composeEventHandlers)(props.onClick, onClick ?? void 0)
847
+ onClick: (0, import_primitive7.composeEventHandlers)(props.onClick, onClick ?? void 0)
759
848
  }
760
849
  );
761
850
  }
@@ -769,10 +858,11 @@ var BranchPickerNext = createActionButton(useGoToNextBranch);
769
858
  var useGoToPreviousBranch = () => {
770
859
  const { useThread, useBranchObserver } = useAssistantContext();
771
860
  const { useComposer, useMessage } = useMessageContext();
772
- const isLoading = useThread((s) => s.isLoading);
773
- const isEditing = useComposer((s) => s.isEditing);
774
- const hasNext = useMessage(({ branchState: { branchId } }) => branchId > 0);
775
- if (isLoading || isEditing || !hasNext)
861
+ const disabled = useCombinedStore(
862
+ [useThread, useComposer, useMessage],
863
+ (t, c, m) => t.isLoading || c.isEditing || m.branchState.branchId <= 0
864
+ );
865
+ if (disabled)
776
866
  return null;
777
867
  return () => {
778
868
  const {
@@ -801,10 +891,10 @@ var BranchPickerNumber = () => {
801
891
  };
802
892
 
803
893
  // src/primitives/branchPicker/BranchPickerRoot.tsx
804
- var import_react_primitive8 = require("@radix-ui/react-primitive");
805
- var import_react15 = require("react");
806
- var BranchPickerRoot = (0, import_react15.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
807
- return /* @__PURE__ */ React.createElement(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0 }, /* @__PURE__ */ React.createElement(import_react_primitive8.Primitive.div, { ...rest, ref }));
894
+ var import_react_primitive9 = require("@radix-ui/react-primitive");
895
+ var import_react19 = require("react");
896
+ var BranchPickerRoot = (0, import_react19.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
897
+ return /* @__PURE__ */ React.createElement(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0 }, /* @__PURE__ */ React.createElement(import_react_primitive9.Primitive.div, { ...rest, ref }));
808
898
  });
809
899
 
810
900
  // src/primitives/actionBar/index.ts
@@ -817,28 +907,30 @@ __export(actionBar_exports, {
817
907
  });
818
908
 
819
909
  // src/primitives/actionBar/ActionBarRoot.tsx
820
- var import_react_primitive9 = require("@radix-ui/react-primitive");
821
- var import_react16 = require("react");
822
- var ActionBarRoot = (0, import_react16.forwardRef)(({ hideWhenBusy, autohide, autohideFloat, ...rest }, ref) => {
910
+ var import_react_primitive10 = require("@radix-ui/react-primitive");
911
+ var import_react20 = require("react");
912
+ var ActionBarRoot = (0, import_react20.forwardRef)(({ hideWhenBusy, autohide, autohideFloat, ...rest }, ref) => {
823
913
  const { useThread } = useAssistantContext();
824
914
  const { useMessage } = useMessageContext();
825
- const hideAndfloatStatus = useMessage((m) => {
826
- const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
827
- if (!autohideEnabled)
915
+ const hideAndfloatStatus = useCombinedStore(
916
+ [useThread, useMessage],
917
+ (t, m) => {
918
+ if (hideWhenBusy && t.isLoading)
919
+ return "hidden" /* Hidden */;
920
+ const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
921
+ if (!autohideEnabled)
922
+ return "normal" /* Normal */;
923
+ if (!m.isHovering)
924
+ return "hidden" /* Hidden */;
925
+ if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branchState.branchCount <= 1)
926
+ return "floating" /* Floating */;
828
927
  return "normal" /* Normal */;
829
- if (!m.isHovering)
830
- return "hidden" /* Hidden */;
831
- if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branchState.branchCount <= 1)
832
- return "floating" /* Floating */;
833
- return "normal" /* Normal */;
834
- });
835
- const busy = useThread((t) => t.isLoading);
836
- if (hideWhenBusy && busy)
837
- return null;
928
+ }
929
+ );
838
930
  if (hideAndfloatStatus === "hidden" /* Hidden */)
839
931
  return null;
840
932
  return /* @__PURE__ */ React.createElement(
841
- import_react_primitive9.Primitive.div,
933
+ import_react_primitive10.Primitive.div,
842
934
  {
843
935
  "data-floating": hideAndfloatStatus === "floating" /* Floating */,
844
936
  ...rest,
@@ -870,9 +962,11 @@ var ActionBarCopy = createActionButton(useCopyMessage);
870
962
  var useReloadMessage = () => {
871
963
  const { useThread, useBranchObserver } = useAssistantContext();
872
964
  const { useMessage } = useMessageContext();
873
- const isLoading = useThread((s) => s.isLoading);
874
- const isAssistant = useMessage((s) => s.message.role === "assistant");
875
- if (isLoading || !isAssistant)
965
+ const disabled = useCombinedStore(
966
+ [useThread, useMessage],
967
+ (t, m) => t.isLoading || m.message.role !== "assistant"
968
+ );
969
+ if (disabled)
876
970
  return null;
877
971
  return () => {
878
972
  const message = useMessage.getState().message;
@@ -888,9 +982,11 @@ var ActionBarReload = createActionButton(useReloadMessage);
888
982
  // src/actions/useBeginMessageEdit.tsx
889
983
  var useBeginMessageEdit = () => {
890
984
  const { useMessage, useComposer } = useMessageContext();
891
- const isUser = useMessage((s) => s.message.role === "user");
892
- const isEditing = useComposer((s) => s.isEditing);
893
- if (!isUser || isEditing)
985
+ const disabled = useCombinedStore(
986
+ [useMessage, useComposer],
987
+ (m, c) => m.message.role !== "user" || c.isEditing
988
+ );
989
+ if (disabled)
894
990
  return null;
895
991
  return () => {
896
992
  const { edit } = useComposer.getState();
@@ -902,24 +998,34 @@ var useBeginMessageEdit = () => {
902
998
  var ActionBarEdit = createActionButton(useBeginMessageEdit);
903
999
 
904
1000
  // src/vercel/VercelAIAssistantProvider.tsx
905
- var import_react18 = require("react");
1001
+ var import_react22 = require("react");
906
1002
 
907
1003
  // src/vercel/useDummyAIAssistantContext.tsx
908
- var import_react17 = require("react");
1004
+ var import_react21 = require("react");
909
1005
  var import_zustand2 = require("zustand");
910
1006
  var useDummyAIAssistantContext = () => {
911
- const [context] = (0, import_react17.useState)(() => {
1007
+ const [context] = (0, import_react21.useState)(() => {
1008
+ const scrollToBottomListeners = /* @__PURE__ */ new Set();
912
1009
  const useThread = (0, import_zustand2.create)()(() => ({
913
1010
  messages: [],
914
1011
  isLoading: false,
915
- reload: async () => {
916
- throw new Error("Not implemented");
917
- },
918
1012
  append: async () => {
919
1013
  throw new Error("Not implemented");
920
1014
  },
921
1015
  stop: () => {
922
1016
  throw new Error("Not implemented");
1017
+ },
1018
+ isAtBottom: true,
1019
+ scrollToBottom: () => {
1020
+ for (const listener of scrollToBottomListeners) {
1021
+ listener();
1022
+ }
1023
+ },
1024
+ onScrollToBottom: (callback) => {
1025
+ scrollToBottomListeners.add(callback);
1026
+ return () => {
1027
+ scrollToBottomListeners.delete(callback);
1028
+ };
923
1029
  }
924
1030
  }));
925
1031
  const useComposer = (0, import_zustand2.create)()(() => ({
@@ -990,28 +1096,23 @@ var VercelAIAssistantProvider = ({
990
1096
  }) => {
991
1097
  const context = useDummyAIAssistantContext();
992
1098
  const vercel = "chat" in rest ? rest.chat : rest.assistant;
993
- const messages = (0, import_react18.useMemo)(() => {
1099
+ const messages = (0, import_react22.useMemo)(() => {
994
1100
  return vercelToCachedThreadMessages(vercel.messages);
995
1101
  }, [vercel.messages]);
996
- const maybeReload = "reload" in vercel ? vercel.reload : null;
997
- const reload = (0, import_react18.useCallback)(async () => {
998
- if (!maybeReload)
999
- throw new Error("Reload not supported");
1000
- await maybeReload();
1001
- }, [maybeReload]);
1002
- const append = (0, import_react18.useCallback)(
1102
+ const append = (0, import_react22.useCallback)(
1003
1103
  async (message) => {
1004
1104
  if (message.content[0]?.type !== "text") {
1005
1105
  throw new Error("Only text content is currently supported");
1006
1106
  }
1107
+ context.useThread.getState().scrollToBottom();
1007
1108
  await vercel.append({
1008
1109
  role: message.role,
1009
1110
  content: message.content[0].text
1010
1111
  });
1011
1112
  },
1012
- [vercel.append]
1113
+ [context, vercel.append]
1013
1114
  );
1014
- const stop = (0, import_react18.useCallback)(() => {
1115
+ const stop = (0, import_react22.useCallback)(() => {
1015
1116
  const lastMessage = vercel.messages.at(-1);
1016
1117
  vercel.stop();
1017
1118
  if (lastMessage?.role === "user") {
@@ -1019,34 +1120,30 @@ var VercelAIAssistantProvider = ({
1019
1120
  }
1020
1121
  }, [vercel.messages, vercel.stop, vercel.setInput]);
1021
1122
  const isLoading = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
1022
- (0, import_react18.useMemo)(() => {
1023
- context.useThread.setState(
1024
- {
1025
- messages,
1026
- isLoading,
1027
- reload,
1028
- append,
1029
- stop
1030
- },
1031
- true
1032
- );
1033
- }, [context, messages, reload, append, stop, isLoading]);
1034
- (0, import_react18.useMemo)(() => {
1123
+ (0, import_react22.useMemo)(() => {
1124
+ context.useThread.setState({
1125
+ messages,
1126
+ isLoading,
1127
+ append,
1128
+ stop
1129
+ });
1130
+ }, [context, messages, append, stop, isLoading]);
1131
+ (0, import_react22.useMemo)(() => {
1035
1132
  context.useComposer.setState({
1036
1133
  canCancel: isLoading,
1037
1134
  value: vercel.input,
1038
1135
  setValue: vercel.setInput
1039
1136
  });
1040
1137
  }, [context, isLoading, vercel.input, vercel.setInput]);
1041
- const branches = useVercelAIBranches(vercel);
1042
- (0, import_react18.useMemo)(() => {
1138
+ const branches = useVercelAIBranches(vercel, context);
1139
+ (0, import_react22.useMemo)(() => {
1043
1140
  context.useBranchObserver.setState(branches, true);
1044
1141
  }, [context, branches]);
1045
1142
  return /* @__PURE__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
1046
1143
  };
1047
1144
 
1048
1145
  // src/vercel/VercelRSCAssistantProvider.tsx
1049
- var import_react19 = require("react");
1146
+ var import_react23 = require("react");
1050
1147
  var ThreadMessageCache2 = /* @__PURE__ */ new WeakMap();
1051
1148
  var vercelToThreadMessage2 = (message) => {
1052
1149
  if (message.role !== "user" && message.role !== "assistant")
@@ -1073,22 +1170,23 @@ var VercelRSCAssistantProvider = ({
1073
1170
  append: vercelAppend
1074
1171
  }) => {
1075
1172
  const context = useDummyAIAssistantContext();
1076
- const messages = (0, import_react19.useMemo)(() => {
1173
+ const messages = (0, import_react23.useMemo)(() => {
1077
1174
  return vercelToCachedThreadMessages2(vercelMessages);
1078
1175
  }, [vercelMessages]);
1079
- const append = (0, import_react19.useCallback)(
1176
+ const append = (0, import_react23.useCallback)(
1080
1177
  async (message) => {
1081
1178
  if (message.content[0]?.type !== "text") {
1082
1179
  throw new Error("Only text content is currently supported");
1083
1180
  }
1181
+ context.useThread.getState().scrollToBottom();
1084
1182
  await vercelAppend({
1085
1183
  role: message.role,
1086
1184
  content: [{ type: "text", text: message.content[0].text }]
1087
1185
  });
1088
1186
  },
1089
- [vercelAppend]
1187
+ [context, vercelAppend]
1090
1188
  );
1091
- (0, import_react19.useMemo)(() => {
1189
+ (0, import_react23.useMemo)(() => {
1092
1190
  context.useThread.setState({
1093
1191
  messages,
1094
1192
  append