@assistant-ui/react 0.0.6 → 0.0.8

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.js CHANGED
@@ -35,7 +35,8 @@ __export(src_exports, {
35
35
  ComposerPrimitive: () => composer_exports,
36
36
  MessagePrimitive: () => message_exports,
37
37
  ThreadPrimitive: () => thread_exports,
38
- VercelAIAssistantProvider: () => VercelAIChatAssistantProvider,
38
+ VercelAIAssistantProvider: () => VercelAIAssistantProvider,
39
+ unstable_VercelRSCAssistantProvider: () => VercelRSCAssistantProvider,
39
40
  unstable_useMessageContext: () => useMessageContext,
40
41
  useBeginMessageEdit: () => useBeginMessageEdit,
41
42
  useCopyMessage: () => useCopyMessage,
@@ -52,6 +53,7 @@ __export(thread_exports, {
52
53
  If: () => ThreadIf,
53
54
  Messages: () => ThreadMessages,
54
55
  Root: () => ThreadRoot,
56
+ ScrollToBottom: () => ThreadScrollToBottom,
55
57
  Viewport: () => ThreadViewport
56
58
  });
57
59
 
@@ -102,10 +104,10 @@ var ThreadEmpty = ({ children }) => {
102
104
  };
103
105
 
104
106
  // src/primitives/thread/ThreadViewport.tsx
105
- var import_react4 = require("react");
106
- var import_react_primitive2 = require("@radix-ui/react-primitive");
107
- var import_react_compose_refs = require("@radix-ui/react-compose-refs");
108
107
  var import_primitive = require("@radix-ui/primitive");
108
+ var import_react_compose_refs = require("@radix-ui/react-compose-refs");
109
+ var import_react_primitive2 = require("@radix-ui/react-primitive");
110
+ var import_react5 = require("react");
109
111
 
110
112
  // src/utils/hooks/useOnResizeContent.tsx
111
113
  var import_react3 = require("react");
@@ -138,34 +140,60 @@ var useOnResizeContent = (ref, callback) => {
138
140
  mutationObserver.observe(el, { childList: true });
139
141
  for (const child of el.children) {
140
142
  resizeObserver.observe(child);
141
- console.log("observing child", child);
142
143
  }
143
144
  return () => {
144
- console.log("disconnecting");
145
145
  resizeObserver.disconnect();
146
146
  mutationObserver.disconnect();
147
147
  };
148
148
  }, [ref.current]);
149
149
  };
150
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
+
151
164
  // src/primitives/thread/ThreadViewport.tsx
152
- var ThreadViewport = (0, import_react4.forwardRef)(({ onScroll, children, ...rest }, forwardedRef) => {
153
- 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);
154
168
  const ref = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, divRef);
155
- 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
+ };
156
179
  useOnResizeContent(divRef, () => {
157
- const div = divRef.current;
158
- if (!div || !isAtBottom)
180
+ if (!useThread.getState().isAtBottom)
159
181
  return;
160
- div.scrollTop = div.scrollHeight;
182
+ scrollToBottom();
183
+ });
184
+ useOnScrollToBottom(() => {
185
+ scrollToBottom();
161
186
  });
162
187
  const handleScroll = () => {
163
188
  const div = divRef.current;
164
189
  if (!div)
165
190
  return;
166
- 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
+ }
167
196
  };
168
- console.log(isAtBottom);
169
197
  return /* @__PURE__ */ React.createElement(
170
198
  import_react_primitive2.Primitive.div,
171
199
  {
@@ -173,12 +201,13 @@ var ThreadViewport = (0, import_react4.forwardRef)(({ onScroll, children, ...res
173
201
  onScroll: (0, import_primitive.composeEventHandlers)(onScroll, handleScroll),
174
202
  ref
175
203
  },
176
- children
204
+ children,
205
+ /* @__PURE__ */ React.createElement("div", { ref: messagesEndRef })
177
206
  );
178
207
  });
179
208
 
180
209
  // src/vercel/useVercelAIBranches.tsx
181
- var import_react5 = require("react");
210
+ var import_react6 = require("react");
182
211
  var ROOT_ID = "__ROOT_ID__";
183
212
  var UPCOMING_MESSAGE_ID = "__UPCOMING_MESSAGE_ID__";
184
213
  var updateBranchData = (data, messages) => {
@@ -245,20 +274,20 @@ var sliceMessagesUntil = (messages, message) => {
245
274
  throw new Error("Unexpected: Message not found");
246
275
  return messages.slice(0, messageIdx);
247
276
  };
248
- var useVercelAIBranches = (chat) => {
249
- const data = (0, import_react5.useRef)({
277
+ var useVercelAIBranches = (chat, context) => {
278
+ const data = (0, import_react6.useRef)({
250
279
  parentMap: /* @__PURE__ */ new Map(),
251
280
  branchMap: /* @__PURE__ */ new Map(),
252
281
  snapshots: /* @__PURE__ */ new Map()
253
282
  }).current;
254
283
  updateBranchData(data, chat.messages);
255
- const getBranchState = (0, import_react5.useCallback)(
284
+ const getBranchState = (0, import_react6.useCallback)(
256
285
  (message) => {
257
286
  return getBranchStateImpl(data, chat.messages, message);
258
287
  },
259
288
  [data, chat.messages]
260
289
  );
261
- const switchToBranch = (0, import_react5.useCallback)(
290
+ const switchToBranch = (0, import_react6.useCallback)(
262
291
  (message, branchId) => {
263
292
  const newMessages = switchToBranchImpl(
264
293
  data,
@@ -270,28 +299,33 @@ var useVercelAIBranches = (chat) => {
270
299
  },
271
300
  [data, chat.messages, chat.setMessages]
272
301
  );
273
- const reloadAt = (0, import_react5.useCallback)(
302
+ const reloadMaybe = "reload" in chat ? chat.reload : void 0;
303
+ const reloadAt = (0, import_react6.useCallback)(
274
304
  async (message) => {
305
+ if (!reloadMaybe)
306
+ throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
275
307
  const newMessages = sliceMessagesUntil(chat.messages, message);
276
308
  chat.setMessages(newMessages);
277
- await chat.reload();
309
+ context.useThread.getState().scrollToBottom();
310
+ await reloadMaybe();
278
311
  },
279
- [chat.messages, chat.setMessages, chat.reload]
312
+ [context, chat.messages, chat.setMessages, reloadMaybe]
280
313
  );
281
- const editAt = (0, import_react5.useCallback)(
314
+ const editAt = (0, import_react6.useCallback)(
282
315
  async (message, newMessage) => {
283
316
  const newMessages = sliceMessagesUntil(chat.messages, message);
284
317
  chat.setMessages(newMessages);
285
318
  if (newMessage.content[0]?.type !== "text")
286
319
  throw new Error("Only text content is currently supported");
320
+ context.useThread.getState().scrollToBottom();
287
321
  await chat.append({
288
322
  role: "user",
289
323
  content: newMessage.content[0].text
290
324
  });
291
325
  },
292
- [chat.messages, chat.setMessages, chat.append]
326
+ [context, chat.messages, chat.setMessages, chat.append]
293
327
  );
294
- return (0, import_react5.useMemo)(
328
+ return (0, import_react6.useMemo)(
295
329
  () => ({
296
330
  getBranchState,
297
331
  switchToBranch,
@@ -305,24 +339,27 @@ var hasUpcomingMessage = (thread) => {
305
339
  return thread.isLoading && thread.messages[thread.messages.length - 1]?.role !== "assistant";
306
340
  };
307
341
 
308
- // src/utils/context/ComposerState.ts
309
- var import_react7 = require("react");
342
+ // src/utils/context/useComposerContext.ts
343
+ var import_react8 = require("react");
310
344
 
311
- // src/utils/context/MessageContext.ts
312
- var import_react6 = require("react");
313
- 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);
314
348
  var useMessageContext = () => {
315
- const context = (0, import_react6.useContext)(MessageContext);
349
+ const context = (0, import_react7.useContext)(MessageContext);
316
350
  if (!context)
317
351
  throw new Error("useMessageContext must be used within a MessageProvider");
318
352
  return context;
319
353
  };
320
354
 
321
- // src/utils/context/ComposerState.ts
355
+ // src/utils/context/useComposerContext.ts
322
356
  var useComposerContext = () => {
323
357
  const { useComposer: useAssisstantComposer } = useAssistantContext();
324
- const { useComposer: useMessageComposer } = (0, import_react7.useContext)(MessageContext) ?? {};
325
- 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
+ };
326
363
  };
327
364
 
328
365
  // src/primitives/composer/ComposerIf.tsx
@@ -351,7 +388,7 @@ __export(message_exports, {
351
388
  });
352
389
 
353
390
  // src/primitives/message/MessageProvider.tsx
354
- var import_react8 = require("react");
391
+ var import_react9 = require("react");
355
392
  var import_zustand = require("zustand");
356
393
  var import_shallow = require("zustand/react/shallow");
357
394
  var getIsLast = (thread, message) => {
@@ -360,7 +397,7 @@ var getIsLast = (thread, message) => {
360
397
  };
361
398
  var useMessageContext2 = () => {
362
399
  const { useBranchObserver } = useAssistantContext();
363
- const [context] = (0, import_react8.useState)(() => {
400
+ const [context] = (0, import_react9.useState)(() => {
364
401
  const useMessage = (0, import_zustand.create)(() => ({
365
402
  message: null,
366
403
  isLast: false,
@@ -417,9 +454,9 @@ var MessageProvider = ({
417
454
  (0, import_shallow.useShallow)((b) => b.getBranchState(message))
418
455
  );
419
456
  const isLast = useThread((thread) => getIsLast(thread, message));
420
- const [isCopied, setIsCopied] = (0, import_react8.useState)(false);
421
- const [isHovering, setIsHovering] = (0, import_react8.useState)(false);
422
- (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)(() => {
423
460
  context.useMessage.setState(
424
461
  {
425
462
  message,
@@ -439,8 +476,8 @@ var MessageProvider = ({
439
476
  // src/primitives/message/MessageRoot.tsx
440
477
  var import_primitive2 = require("@radix-ui/primitive");
441
478
  var import_react_primitive3 = require("@radix-ui/react-primitive");
442
- var import_react9 = require("react");
443
- var MessageRoot = (0, import_react9.forwardRef)(
479
+ var import_react10 = require("react");
480
+ var MessageRoot = (0, import_react10.forwardRef)(
444
481
  ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
445
482
  const { useMessage } = useMessageContext();
446
483
  const setIsHovering = useMessage((s) => s.setIsHovering);
@@ -495,12 +532,41 @@ var MessageIf = ({ children, ...query }) => {
495
532
  };
496
533
 
497
534
  // src/primitives/message/MessageContent.tsx
498
- var MessageContent = () => {
535
+ var defaultComponents = {
536
+ Text: ({ part }) => /* @__PURE__ */ React.createElement(React.Fragment, null, part.text),
537
+ Image: () => null,
538
+ UI: ({ part }) => part.display,
539
+ tools: {
540
+ Fallback: () => null
541
+ }
542
+ };
543
+ var MessageContent = ({
544
+ components: {
545
+ Text = defaultComponents.Text,
546
+ Image = defaultComponents.Image,
547
+ UI = defaultComponents.UI,
548
+ tools: { by_name = {}, Fallback = defaultComponents.tools.Fallback } = {}
549
+ } = {}
550
+ }) => {
499
551
  const { useMessage } = useMessageContext();
500
552
  const content = useMessage((s) => s.message.content);
501
- if (content[0]?.type !== "text")
502
- throw new Error("Unsupported message content type");
503
- return /* @__PURE__ */ React.createElement(React.Fragment, null, content[0].text);
553
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, content.map((part, i) => {
554
+ const key = i;
555
+ switch (part.type) {
556
+ case "text":
557
+ return /* @__PURE__ */ React.createElement(Text, { key, part });
558
+ case "image":
559
+ return /* @__PURE__ */ React.createElement(Image, { key, part });
560
+ case "ui":
561
+ return /* @__PURE__ */ React.createElement(UI, { key, part });
562
+ case "tool-call": {
563
+ const Tool = by_name[part.name] || Fallback;
564
+ return /* @__PURE__ */ React.createElement(Tool, { key, part });
565
+ }
566
+ default:
567
+ return null;
568
+ }
569
+ }));
504
570
  };
505
571
 
506
572
  // src/primitives/thread/ThreadMessages.tsx
@@ -536,6 +602,29 @@ var ThreadMessages = ({ components }) => {
536
602
  ));
537
603
  };
538
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
+
539
628
  // src/primitives/composer/index.ts
540
629
  var composer_exports = {};
541
630
  __export(composer_exports, {
@@ -547,33 +636,15 @@ __export(composer_exports, {
547
636
  });
548
637
 
549
638
  // src/primitives/composer/ComposerRoot.tsx
550
- var import_primitive3 = require("@radix-ui/primitive");
639
+ var import_primitive4 = require("@radix-ui/primitive");
551
640
  var import_react_compose_refs2 = require("@radix-ui/react-compose-refs");
552
- var import_react_primitive4 = require("@radix-ui/react-primitive");
553
- var import_react10 = require("react");
554
- var ComposerFormContext = (0, import_react10.createContext)(null);
555
- var useComposerFormContext = () => {
556
- const context = (0, import_react10.useContext)(ComposerFormContext);
557
- if (!context) {
558
- throw new Error(
559
- "Composer compound components cannot be rendered outside the Composer component"
560
- );
561
- }
562
- return context;
563
- };
564
- 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)(
565
644
  ({ onSubmit, ...rest }, forwardedRef) => {
566
645
  const { useComposer } = useComposerContext();
567
- const formRef = (0, import_react10.useRef)(null);
646
+ const formRef = (0, import_react12.useRef)(null);
568
647
  const ref = (0, import_react_compose_refs2.useComposedRefs)(forwardedRef, formRef);
569
- const composerContextValue = (0, import_react10.useMemo)(
570
- () => ({
571
- submit: () => formRef.current?.dispatchEvent(
572
- new Event("submit", { cancelable: true, bubbles: true })
573
- )
574
- }),
575
- []
576
- );
577
648
  const handleSubmit = (e) => {
578
649
  const composerState = useComposer.getState();
579
650
  if (!composerState.isEditing)
@@ -581,73 +652,96 @@ var ComposerRoot = (0, import_react10.forwardRef)(
581
652
  e.preventDefault();
582
653
  composerState.send();
583
654
  };
584
- return /* @__PURE__ */ React.createElement(ComposerFormContext.Provider, { value: composerContextValue }, /* @__PURE__ */ React.createElement(
585
- import_react_primitive4.Primitive.form,
655
+ return /* @__PURE__ */ React.createElement(
656
+ import_react_primitive5.Primitive.form,
586
657
  {
587
658
  ...rest,
588
659
  ref,
589
- onSubmit: (0, import_primitive3.composeEventHandlers)(onSubmit, handleSubmit)
660
+ onSubmit: (0, import_primitive4.composeEventHandlers)(onSubmit, handleSubmit)
590
661
  }
591
- ));
662
+ );
592
663
  }
593
664
  );
594
665
 
595
666
  // src/primitives/composer/ComposerInput.tsx
596
- 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");
597
669
  var import_react_slot = require("@radix-ui/react-slot");
598
- var import_react11 = require("react");
670
+ var import_react13 = require("react");
599
671
  var import_react_textarea_autosize = __toESM(require("react-textarea-autosize"));
600
- var ComposerInput = (0, import_react11.forwardRef)(({ asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
601
- const { useThread } = useAssistantContext();
602
- const isLoading = useThread((t) => t.isLoading);
603
- const { useComposer } = useComposerContext();
604
- const value = useComposer((c) => {
605
- if (!c.isEditing)
606
- return "";
607
- return c.value;
608
- });
609
- const Component = asChild ? import_react_slot.Slot : import_react_textarea_autosize.default;
610
- const composerForm = useComposerFormContext();
611
- const handleKeyPress = (e) => {
612
- if (disabled)
613
- return;
614
- if (e.key === "Escape") {
615
- useComposer.getState().cancel();
616
- }
617
- if (isLoading)
618
- return;
619
- if (e.key === "Enter" && e.shiftKey === false) {
620
- e.preventDefault();
621
- composerForm.submit();
622
- }
623
- };
624
- return /* @__PURE__ */ React.createElement(
625
- Component,
626
- {
627
- value,
628
- ...rest,
629
- ref: forwardedRef,
630
- disabled,
631
- onChange: (0, import_primitive4.composeEventHandlers)(onChange, (e) => {
632
- const composerState = useComposer.getState();
633
- if (!composerState.isEditing)
634
- return;
635
- return composerState.setValue(e.target.value);
636
- }),
637
- onKeyDown: (0, import_primitive4.composeEventHandlers)(onKeyDown, handleKeyPress)
638
- }
639
- );
640
- });
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
+ );
641
735
 
642
736
  // src/primitives/composer/ComposerSend.tsx
643
- var import_react_primitive5 = require("@radix-ui/react-primitive");
644
- var import_react12 = require("react");
645
- 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)(
646
740
  ({ disabled, ...rest }, ref) => {
647
741
  const { useComposer } = useComposerContext();
648
742
  const hasValue = useComposer((c) => c.isEditing && c.value.length > 0);
649
743
  return /* @__PURE__ */ React.createElement(
650
- import_react_primitive5.Primitive.button,
744
+ import_react_primitive6.Primitive.button,
651
745
  {
652
746
  type: "submit",
653
747
  ...rest,
@@ -659,22 +753,22 @@ var ComposerSend = (0, import_react12.forwardRef)(
659
753
  );
660
754
 
661
755
  // src/primitives/composer/ComposerCancel.tsx
662
- var import_primitive5 = require("@radix-ui/primitive");
663
- var import_react_primitive6 = require("@radix-ui/react-primitive");
664
- var import_react13 = require("react");
665
- 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) => {
666
760
  const { useComposer } = useComposerContext();
667
761
  const hasValue = useComposer((c) => c.canCancel);
668
762
  const handleClose = () => {
669
763
  useComposer.getState().cancel();
670
764
  };
671
765
  return /* @__PURE__ */ React.createElement(
672
- import_react_primitive6.Primitive.button,
766
+ import_react_primitive7.Primitive.button,
673
767
  {
674
768
  type: "button",
675
769
  ...rest,
676
770
  ref,
677
- onClick: (0, import_primitive5.composeEventHandlers)(onClick, handleClose),
771
+ onClick: (0, import_primitive6.composeEventHandlers)(onClick, handleClose),
678
772
  disabled: disabled || !hasValue
679
773
  }
680
774
  );
@@ -690,16 +784,41 @@ __export(branchPicker_exports, {
690
784
  Root: () => BranchPickerRoot
691
785
  });
692
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
+
693
813
  // src/actions/useGoToNextBranch.tsx
694
814
  var useGoToNextBranch = () => {
695
815
  const { useThread, useBranchObserver } = useAssistantContext();
696
816
  const { useComposer, useMessage } = useMessageContext();
697
- const isLoading = useThread((s) => s.isLoading);
698
- const isEditing = useComposer((s) => s.isEditing);
699
- const hasNext = useMessage(
700
- ({ 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
701
820
  );
702
- if (isLoading || isEditing || !hasNext)
821
+ if (disabled)
703
822
  return null;
704
823
  return () => {
705
824
  const {
@@ -711,21 +830,21 @@ var useGoToNextBranch = () => {
711
830
  };
712
831
 
713
832
  // src/utils/createActionButton.tsx
714
- var import_react14 = require("react");
715
- var import_react_primitive7 = require("@radix-ui/react-primitive");
716
- 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");
717
836
  var createActionButton = (useActionButton) => {
718
- return (0, import_react14.forwardRef)(
837
+ return (0, import_react18.forwardRef)(
719
838
  (props, forwardedRef) => {
720
839
  const onClick = useActionButton(props);
721
840
  return /* @__PURE__ */ React.createElement(
722
- import_react_primitive7.Primitive.button,
841
+ import_react_primitive8.Primitive.button,
723
842
  {
724
843
  type: "button",
725
844
  disabled: !onClick,
726
845
  ...props,
727
846
  ref: forwardedRef,
728
- onClick: (0, import_primitive6.composeEventHandlers)(props.onClick, onClick ?? void 0)
847
+ onClick: (0, import_primitive7.composeEventHandlers)(props.onClick, onClick ?? void 0)
729
848
  }
730
849
  );
731
850
  }
@@ -739,10 +858,11 @@ var BranchPickerNext = createActionButton(useGoToNextBranch);
739
858
  var useGoToPreviousBranch = () => {
740
859
  const { useThread, useBranchObserver } = useAssistantContext();
741
860
  const { useComposer, useMessage } = useMessageContext();
742
- const isLoading = useThread((s) => s.isLoading);
743
- const isEditing = useComposer((s) => s.isEditing);
744
- const hasNext = useMessage(({ branchState: { branchId } }) => branchId > 0);
745
- 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)
746
866
  return null;
747
867
  return () => {
748
868
  const {
@@ -771,10 +891,10 @@ var BranchPickerNumber = () => {
771
891
  };
772
892
 
773
893
  // src/primitives/branchPicker/BranchPickerRoot.tsx
774
- var import_react_primitive8 = require("@radix-ui/react-primitive");
775
- var import_react15 = require("react");
776
- var BranchPickerRoot = (0, import_react15.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
777
- 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 }));
778
898
  });
779
899
 
780
900
  // src/primitives/actionBar/index.ts
@@ -787,28 +907,30 @@ __export(actionBar_exports, {
787
907
  });
788
908
 
789
909
  // src/primitives/actionBar/ActionBarRoot.tsx
790
- var import_react_primitive9 = require("@radix-ui/react-primitive");
791
- var import_react16 = require("react");
792
- 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) => {
793
913
  const { useThread } = useAssistantContext();
794
914
  const { useMessage } = useMessageContext();
795
- const hideAndfloatStatus = useMessage((m) => {
796
- const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
797
- 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 */;
798
927
  return "normal" /* Normal */;
799
- if (!m.isHovering)
800
- return "hidden" /* Hidden */;
801
- if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branchState.branchCount <= 1)
802
- return "floating" /* Floating */;
803
- return "normal" /* Normal */;
804
- });
805
- const busy = useThread((t) => t.isLoading);
806
- if (hideWhenBusy && busy)
807
- return null;
928
+ }
929
+ );
808
930
  if (hideAndfloatStatus === "hidden" /* Hidden */)
809
931
  return null;
810
932
  return /* @__PURE__ */ React.createElement(
811
- import_react_primitive9.Primitive.div,
933
+ import_react_primitive10.Primitive.div,
812
934
  {
813
935
  "data-floating": hideAndfloatStatus === "floating" /* Floating */,
814
936
  ...rest,
@@ -840,9 +962,11 @@ var ActionBarCopy = createActionButton(useCopyMessage);
840
962
  var useReloadMessage = () => {
841
963
  const { useThread, useBranchObserver } = useAssistantContext();
842
964
  const { useMessage } = useMessageContext();
843
- const isLoading = useThread((s) => s.isLoading);
844
- const isAssistant = useMessage((s) => s.message.role === "assistant");
845
- if (isLoading || !isAssistant)
965
+ const disabled = useCombinedStore(
966
+ [useThread, useMessage],
967
+ (t, m) => t.isLoading || m.message.role !== "assistant"
968
+ );
969
+ if (disabled)
846
970
  return null;
847
971
  return () => {
848
972
  const message = useMessage.getState().message;
@@ -858,9 +982,11 @@ var ActionBarReload = createActionButton(useReloadMessage);
858
982
  // src/actions/useBeginMessageEdit.tsx
859
983
  var useBeginMessageEdit = () => {
860
984
  const { useMessage, useComposer } = useMessageContext();
861
- const isUser = useMessage((s) => s.message.role === "user");
862
- const isEditing = useComposer((s) => s.isEditing);
863
- if (!isUser || isEditing)
985
+ const disabled = useCombinedStore(
986
+ [useMessage, useComposer],
987
+ (m, c) => m.message.role !== "user" || c.isEditing
988
+ );
989
+ if (disabled)
864
990
  return null;
865
991
  return () => {
866
992
  const { edit } = useComposer.getState();
@@ -872,25 +998,42 @@ var useBeginMessageEdit = () => {
872
998
  var ActionBarEdit = createActionButton(useBeginMessageEdit);
873
999
 
874
1000
  // src/vercel/VercelAIAssistantProvider.tsx
875
- var import_react17 = require("react");
1001
+ var import_react22 = require("react");
1002
+
1003
+ // src/vercel/useDummyAIAssistantContext.tsx
1004
+ var import_react21 = require("react");
876
1005
  var import_zustand2 = require("zustand");
877
- var useAIAssistantContext = () => {
878
- const [context] = (0, import_react17.useState)(() => {
1006
+ var useDummyAIAssistantContext = () => {
1007
+ const [context] = (0, import_react21.useState)(() => {
1008
+ const scrollToBottomListeners = /* @__PURE__ */ new Set();
879
1009
  const useThread = (0, import_zustand2.create)()(() => ({
880
1010
  messages: [],
881
1011
  isLoading: false,
882
- reload: async () => {
883
- },
884
1012
  append: async () => {
1013
+ throw new Error("Not implemented");
885
1014
  },
886
1015
  stop: () => {
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
+ };
887
1029
  }
888
1030
  }));
889
1031
  const useComposer = (0, import_zustand2.create)()(() => ({
890
1032
  isEditing: true,
891
1033
  canCancel: false,
892
1034
  value: "",
893
- setValue: () => {
1035
+ setValue: (value) => {
1036
+ useComposer.setState({ value });
894
1037
  },
895
1038
  edit: () => {
896
1039
  throw new Error("Not implemented");
@@ -909,19 +1052,24 @@ var useAIAssistantContext = () => {
909
1052
  const useBranchObserver = (0, import_zustand2.create)()(() => ({
910
1053
  getBranchState: () => ({
911
1054
  branchId: 0,
912
- branchCount: 0
1055
+ branchCount: 1
913
1056
  }),
914
1057
  switchToBranch: () => {
1058
+ throw new Error("Not implemented");
915
1059
  },
916
1060
  editAt: async () => {
1061
+ throw new Error("Not implemented");
917
1062
  },
918
1063
  reloadAt: async () => {
1064
+ throw new Error("Not implemented");
919
1065
  }
920
1066
  }));
921
1067
  return { useThread, useComposer, useBranchObserver };
922
1068
  });
923
1069
  return context;
924
1070
  };
1071
+
1072
+ // src/vercel/VercelAIAssistantProvider.tsx
925
1073
  var ThreadMessageCache = /* @__PURE__ */ new WeakMap();
926
1074
  var vercelToThreadMessage = (message) => {
927
1075
  if (message.role !== "user" && message.role !== "assistant")
@@ -942,58 +1090,110 @@ var vercelToCachedThreadMessages = (messages) => {
942
1090
  return newMessage;
943
1091
  });
944
1092
  };
945
- var VercelAIChatAssistantProvider = ({ chat, children }) => {
946
- const context = useAIAssistantContext();
947
- const messages = (0, import_react17.useMemo)(() => {
948
- return vercelToCachedThreadMessages(chat.messages);
949
- }, [chat.messages]);
950
- const reload = (0, import_react17.useCallback)(async () => {
951
- await chat.reload();
952
- }, [chat.reload]);
953
- const append = (0, import_react17.useCallback)(
1093
+ var VercelAIAssistantProvider = ({
1094
+ children,
1095
+ ...rest
1096
+ }) => {
1097
+ const context = useDummyAIAssistantContext();
1098
+ const vercel = "chat" in rest ? rest.chat : rest.assistant;
1099
+ const messages = (0, import_react22.useMemo)(() => {
1100
+ return vercelToCachedThreadMessages(vercel.messages);
1101
+ }, [vercel.messages]);
1102
+ const append = (0, import_react22.useCallback)(
954
1103
  async (message) => {
955
1104
  if (message.content[0]?.type !== "text") {
956
1105
  throw new Error("Only text content is currently supported");
957
1106
  }
958
- await chat.append({
1107
+ context.useThread.getState().scrollToBottom();
1108
+ await vercel.append({
959
1109
  role: message.role,
960
1110
  content: message.content[0].text
961
1111
  });
962
1112
  },
963
- [chat.append]
1113
+ [context, vercel.append]
964
1114
  );
965
- const stop = (0, import_react17.useCallback)(() => {
966
- const lastMessage = chat.messages.at(-1);
967
- chat.stop();
1115
+ const stop = (0, import_react22.useCallback)(() => {
1116
+ const lastMessage = vercel.messages.at(-1);
1117
+ vercel.stop();
968
1118
  if (lastMessage?.role === "user") {
969
- chat.setInput(lastMessage.content);
1119
+ vercel.setInput(lastMessage.content);
970
1120
  }
971
- }, [chat.messages, chat.stop, chat.setInput]);
972
- (0, import_react17.useMemo)(() => {
973
- context.useThread.setState(
974
- {
975
- messages,
976
- isLoading: chat.isLoading,
977
- reload,
978
- append,
979
- stop
980
- },
981
- true
982
- );
983
- }, [context, messages, reload, append, stop, chat.isLoading]);
984
- (0, import_react17.useMemo)(() => {
1121
+ }, [vercel.messages, vercel.stop, vercel.setInput]);
1122
+ const isLoading = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
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)(() => {
985
1132
  context.useComposer.setState({
986
- canCancel: chat.isLoading,
987
- value: chat.input,
988
- setValue: chat.setInput
1133
+ canCancel: isLoading,
1134
+ value: vercel.input,
1135
+ setValue: vercel.setInput
989
1136
  });
990
- }, [context, chat.isLoading, chat.input, chat.setInput]);
991
- const branches = useVercelAIBranches(chat);
992
- (0, import_react17.useMemo)(() => {
1137
+ }, [context, isLoading, vercel.input, vercel.setInput]);
1138
+ const branches = useVercelAIBranches(vercel, context);
1139
+ (0, import_react22.useMemo)(() => {
993
1140
  context.useBranchObserver.setState(branches, true);
994
1141
  }, [context, branches]);
995
1142
  return /* @__PURE__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
996
1143
  };
1144
+
1145
+ // src/vercel/VercelRSCAssistantProvider.tsx
1146
+ var import_react23 = require("react");
1147
+ var ThreadMessageCache2 = /* @__PURE__ */ new WeakMap();
1148
+ var vercelToThreadMessage2 = (message) => {
1149
+ if (message.role !== "user" && message.role !== "assistant")
1150
+ throw new Error("Unsupported role");
1151
+ return {
1152
+ id: message.id,
1153
+ role: message.role,
1154
+ content: [{ type: "ui", display: message.display }]
1155
+ };
1156
+ };
1157
+ var vercelToCachedThreadMessages2 = (messages) => {
1158
+ return messages.map((m) => {
1159
+ const cached = ThreadMessageCache2.get(m);
1160
+ if (cached)
1161
+ return cached;
1162
+ const newMessage = vercelToThreadMessage2(m);
1163
+ ThreadMessageCache2.set(m, newMessage);
1164
+ return newMessage;
1165
+ });
1166
+ };
1167
+ var VercelRSCAssistantProvider = ({
1168
+ children,
1169
+ messages: vercelMessages,
1170
+ append: vercelAppend
1171
+ }) => {
1172
+ const context = useDummyAIAssistantContext();
1173
+ const messages = (0, import_react23.useMemo)(() => {
1174
+ return vercelToCachedThreadMessages2(vercelMessages);
1175
+ }, [vercelMessages]);
1176
+ const append = (0, import_react23.useCallback)(
1177
+ async (message) => {
1178
+ if (message.content[0]?.type !== "text") {
1179
+ throw new Error("Only text content is currently supported");
1180
+ }
1181
+ context.useThread.getState().scrollToBottom();
1182
+ await vercelAppend({
1183
+ role: message.role,
1184
+ content: [{ type: "text", text: message.content[0].text }]
1185
+ });
1186
+ },
1187
+ [context, vercelAppend]
1188
+ );
1189
+ (0, import_react23.useMemo)(() => {
1190
+ context.useThread.setState({
1191
+ messages,
1192
+ append
1193
+ });
1194
+ }, [context, messages, append]);
1195
+ return /* @__PURE__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
1196
+ };
997
1197
  // Annotate the CommonJS export names for ESM import in node:
998
1198
  0 && (module.exports = {
999
1199
  ActionBarPrimitive,
@@ -1002,6 +1202,7 @@ var VercelAIChatAssistantProvider = ({ chat, children }) => {
1002
1202
  MessagePrimitive,
1003
1203
  ThreadPrimitive,
1004
1204
  VercelAIAssistantProvider,
1205
+ unstable_VercelRSCAssistantProvider,
1005
1206
  unstable_useMessageContext,
1006
1207
  useBeginMessageEdit,
1007
1208
  useCopyMessage,