@assistant-ui/react 0.0.14 → 0.0.16

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.js CHANGED
@@ -33,6 +33,7 @@ __export(src_exports, {
33
33
  ActionBarPrimitive: () => actionBar_exports,
34
34
  BranchPickerPrimitive: () => branchPicker_exports,
35
35
  ComposerPrimitive: () => composer_exports,
36
+ ContentPartPrimitive: () => contentPart_exports,
36
37
  MessagePrimitive: () => message_exports,
37
38
  ThreadPrimitive: () => thread_exports,
38
39
  VercelAIAssistantProvider: () => VercelAIAssistantProvider,
@@ -75,7 +76,7 @@ var useAssistantContext = () => {
75
76
  const context = (0, import_react2.useContext)(AssistantContext);
76
77
  if (!context)
77
78
  throw new Error(
78
- "This component must be used within a AssistantProvider"
79
+ "This component must be used within a AssistantProvider."
79
80
  );
80
81
  return context;
81
82
  };
@@ -84,14 +85,10 @@ var useAssistantContext = () => {
84
85
  var useThreadIf = (props) => {
85
86
  const { useThread } = useAssistantContext();
86
87
  return useThread((thread) => {
87
- if (props.empty === true && thread.messages.length !== 0)
88
- return false;
89
- if (props.empty === false && thread.messages.length === 0)
90
- return false;
91
- if (props.running === true && !thread.isRunning)
92
- return false;
93
- if (props.running === false && thread.isRunning)
94
- return false;
88
+ if (props.empty === true && thread.messages.length !== 0) return false;
89
+ if (props.empty === false && thread.messages.length === 0) return false;
90
+ if (props.running === true && !thread.isRunning) return false;
91
+ if (props.running === false && thread.isRunning) return false;
95
92
  return true;
96
93
  });
97
94
  };
@@ -117,10 +114,9 @@ var import_react3 = require("react");
117
114
  var useOnResizeContent = (ref, callback) => {
118
115
  const callbackRef = (0, import_react3.useRef)(callback);
119
116
  callbackRef.current = callback;
120
- (0, import_react3.useLayoutEffect)(() => {
117
+ (0, import_react3.useEffect)(() => {
121
118
  const el = ref.current;
122
- if (!el)
123
- return;
119
+ if (!el) return;
124
120
  const resizeObserver = new ResizeObserver(() => {
125
121
  callbackRef.current();
126
122
  });
@@ -175,16 +171,14 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
175
171
  const lastScrollTop = (0, import_react5.useRef)(0);
176
172
  const scrollToBottom = () => {
177
173
  const div = messagesEndRef.current;
178
- if (!div || !autoScroll)
179
- return;
174
+ if (!div || !autoScroll) return;
180
175
  const behavior = firstRenderRef.current ? "instant" : "auto";
181
176
  firstRenderRef.current = false;
182
177
  useViewport.setState({ isAtBottom: true });
183
178
  div.scrollIntoView({ behavior });
184
179
  };
185
180
  useOnResizeContent(divRef, () => {
186
- if (!useViewport.getState().isAtBottom)
187
- return;
181
+ if (!useViewport.getState().isAtBottom) return;
188
182
  scrollToBottom();
189
183
  });
190
184
  useOnScrollToBottom(() => {
@@ -192,8 +186,7 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
192
186
  });
193
187
  const handleScroll = () => {
194
188
  const div = divRef.current;
195
- if (!div)
196
- return;
189
+ if (!div) return;
197
190
  const isAtBottom = useViewport.getState().isAtBottom;
198
191
  const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
199
192
  if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
@@ -225,7 +218,9 @@ var MessageContext = (0, import_react6.createContext)(null);
225
218
  var useMessageContext = () => {
226
219
  const context = (0, import_react6.useContext)(MessageContext);
227
220
  if (!context)
228
- throw new Error("This component must be used within a MessageProvider");
221
+ throw new Error(
222
+ "This component must be used within a MessagePrimitive.Provider."
223
+ );
229
224
  return context;
230
225
  };
231
226
 
@@ -243,10 +238,8 @@ var useComposerContext = () => {
243
238
  var useComposerIf = (props) => {
244
239
  const { useComposer } = useComposerContext();
245
240
  return useComposer((composer) => {
246
- if (props.editing === true && !composer.isEditing)
247
- return false;
248
- if (props.editing === false && composer.isEditing)
249
- return false;
241
+ if (props.editing === true && !composer.isEditing) return false;
242
+ if (props.editing === false && composer.isEditing) return false;
250
243
  return true;
251
244
  });
252
245
  };
@@ -260,6 +253,7 @@ var message_exports = {};
260
253
  __export(message_exports, {
261
254
  Content: () => MessageContent,
262
255
  If: () => MessageIf,
256
+ Loading: () => MessageLoading,
263
257
  Provider: () => MessageProvider,
264
258
  Root: () => MessageRoot
265
259
  });
@@ -268,6 +262,14 @@ __export(message_exports, {
268
262
  var import_react8 = require("react");
269
263
  var import_zustand2 = require("zustand");
270
264
 
265
+ // src/utils/context/getMessageText.tsx
266
+ var getMessageText = (message) => {
267
+ const textParts = message.content.filter(
268
+ (part) => part.type === "text"
269
+ );
270
+ return textParts.map((part) => part.text).join("\n\n");
271
+ };
272
+
271
273
  // src/utils/context/stores/ComposerStore.ts
272
274
  var import_zustand = require("zustand");
273
275
  var makeBaseComposer = (set) => ({
@@ -292,8 +294,7 @@ var makeMessageComposerStore = ({
292
294
  onSend(value);
293
295
  },
294
296
  cancel: () => {
295
- if (!get().isEditing)
296
- return false;
297
+ if (!get().isEditing) return false;
297
298
  set({ isEditing: false });
298
299
  return true;
299
300
  }
@@ -312,8 +313,7 @@ var makeThreadComposerStore = (useThread) => (0, import_zustand.create)()((set,
312
313
  },
313
314
  cancel: () => {
314
315
  const thread = useThread.getState();
315
- if (!thread.isRunning)
316
- return false;
316
+ if (!thread.isRunning) return false;
317
317
  useThread.getState().cancelRun();
318
318
  return true;
319
319
  }
@@ -328,30 +328,40 @@ var getIsLast = (thread, message) => {
328
328
  var useMessageContext2 = () => {
329
329
  const { useThread } = useAssistantContext();
330
330
  const [context] = (0, import_react8.useState)(() => {
331
- const useMessage = (0, import_zustand2.create)(() => ({
331
+ const useMessage = (0, import_zustand2.create)((set) => ({
332
332
  message: null,
333
333
  parentId: null,
334
334
  branches: [],
335
335
  isLast: false,
336
+ loadingIndicator: null,
336
337
  isCopied: false,
337
338
  isHovering: false,
338
- setIsCopied: () => {
339
+ setLoadingIndicator: (value) => {
340
+ set({ loadingIndicator: value });
339
341
  },
340
- setIsHovering: () => {
342
+ setIsCopied: (value) => {
343
+ set({ isCopied: value });
344
+ },
345
+ setIsHovering: (value) => {
346
+ set({ isHovering: value });
341
347
  }
342
348
  }));
343
349
  const useComposer = makeMessageComposerStore({
344
350
  onEdit: () => {
345
351
  const message = useMessage.getState().message;
346
352
  if (message.role !== "user")
347
- throw new Error("Editing is only supported for user messages");
348
- const text = message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
353
+ throw new Error(
354
+ "Tried to edit a non-user message. Editing is only supported for user messages. This is likely an internal bug in assistant-ui."
355
+ );
356
+ const text = getMessageText(message);
349
357
  return text;
350
358
  },
351
359
  onSend: (text) => {
352
360
  const { message, parentId } = useMessage.getState();
353
361
  if (message.role !== "user")
354
- throw new Error("Editing is only supported for user messages");
362
+ throw new Error(
363
+ "Tried to edit a non-user message. Editing is only supported for user messages. This is likely an internal bug in assistant-ui."
364
+ );
355
365
  const nonTextParts = message.content.filter(
356
366
  (part) => part.type !== "text" && part.type !== "ui"
357
367
  );
@@ -374,23 +384,14 @@ var MessageProvider = ({
374
384
  const context = useMessageContext2();
375
385
  const isLast = useThread((thread) => getIsLast(thread, message));
376
386
  const branches = useThread((thread) => thread.getBranches(message.id));
377
- const [isCopied, setIsCopied] = (0, import_react8.useState)(false);
378
- const [isHovering, setIsHovering] = (0, import_react8.useState)(false);
379
387
  (0, import_react8.useMemo)(() => {
380
- context.useMessage.setState(
381
- {
382
- message,
383
- parentId,
384
- branches,
385
- isLast,
386
- isCopied,
387
- isHovering,
388
- setIsCopied,
389
- setIsHovering
390
- },
391
- true
392
- );
393
- }, [context, message, parentId, branches, isLast, isCopied, isHovering]);
388
+ context.useMessage.setState({
389
+ message,
390
+ parentId,
391
+ branches,
392
+ isLast
393
+ });
394
+ }, [context, message, parentId, branches, isLast]);
394
395
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MessageContext.Provider, { value: context, children });
395
396
  };
396
397
 
@@ -425,18 +426,12 @@ var MessageRoot = (0, import_react9.forwardRef)(
425
426
  var useMessageIf = (props) => {
426
427
  const { useMessage } = useMessageContext();
427
428
  return useMessage(({ message, branches, isLast, isCopied, isHovering }) => {
428
- if (props.hasBranches === true && branches.length < 2)
429
- return false;
430
- if (props.user && message.role !== "user")
431
- return false;
432
- if (props.assistant && message.role !== "assistant")
433
- return false;
434
- if (props.lastOrHover === true && !isHovering && !isLast)
435
- return false;
436
- if (props.copied === true && !isCopied)
437
- return false;
438
- if (props.copied === false && isCopied)
439
- return false;
429
+ if (props.hasBranches === true && branches.length < 2) return false;
430
+ if (props.user && message.role !== "user") return false;
431
+ if (props.assistant && message.role !== "assistant") return false;
432
+ if (props.lastOrHover === true && !isHovering && !isLast) return false;
433
+ if (props.copied === true && !isCopied) return false;
434
+ if (props.copied === false && isCopied) return false;
440
435
  return true;
441
436
  });
442
437
  };
@@ -445,10 +440,94 @@ var MessageIf = ({ children, ...query }) => {
445
440
  return result ? children : null;
446
441
  };
447
442
 
448
- // src/primitives/message/MessageContent.tsx
443
+ // src/utils/context/combined/useCombinedStore.ts
444
+ var import_react11 = require("react");
445
+
446
+ // src/utils/context/combined/createCombinedStore.ts
447
+ var import_react10 = require("react");
448
+ var createCombinedStore = (stores) => {
449
+ const subscribe = (callback) => {
450
+ const unsubscribes = stores.map((store) => store.subscribe(callback));
451
+ return () => {
452
+ for (const unsub of unsubscribes) {
453
+ unsub();
454
+ }
455
+ };
456
+ };
457
+ return (selector) => {
458
+ const getSnapshot = () => selector(...stores.map((store) => store.getState()));
459
+ return (0, import_react10.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
460
+ };
461
+ };
462
+
463
+ // src/utils/context/combined/useCombinedStore.ts
464
+ var useCombinedStore = (stores, selector) => {
465
+ const useCombined = (0, import_react11.useMemo)(() => createCombinedStore(stores), stores);
466
+ return useCombined(selector);
467
+ };
468
+
469
+ // src/utils/context/useContentPartContext.ts
470
+ var import_react12 = require("react");
471
+ var ContentPartContext = (0, import_react12.createContext)(null);
472
+ var useContentPartContext = () => {
473
+ const context = (0, import_react12.useContext)(ContentPartContext);
474
+ if (!context)
475
+ throw new Error(
476
+ "This component must be used within a ContentPartPrimitive.Provider."
477
+ );
478
+ return context;
479
+ };
480
+
481
+ // src/primitives/contentPart/ContentPartLoadingIndicator.tsx
482
+ var ContentPartLoadingIndicator = () => {
483
+ const { useMessage } = useMessageContext();
484
+ const { useContentPart } = useContentPartContext();
485
+ const loadingIndicator = useCombinedStore(
486
+ [useMessage, useContentPart],
487
+ (m, c) => c.isLoading ? m.loadingIndicator : null
488
+ );
489
+ return loadingIndicator;
490
+ };
491
+
492
+ // src/primitives/contentPart/ContentPartProvider.tsx
493
+ var import_react13 = require("react");
494
+ var import_zustand3 = require("zustand");
449
495
  var import_jsx_runtime6 = require("react/jsx-runtime");
496
+ var useContentPartContext2 = () => {
497
+ const [context] = (0, import_react13.useState)(() => {
498
+ const useContentPart = (0, import_zustand3.create)(() => ({
499
+ part: null,
500
+ isLoading: false
501
+ }));
502
+ return { useContentPart };
503
+ });
504
+ return context;
505
+ };
506
+ var ContentPartProvider = ({
507
+ part,
508
+ isLoading,
509
+ children
510
+ }) => {
511
+ const context = useContentPartContext2();
512
+ (0, import_react13.useMemo)(() => {
513
+ context.useContentPart.setState(
514
+ {
515
+ part,
516
+ isLoading
517
+ },
518
+ true
519
+ );
520
+ }, [context, part, isLoading]);
521
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ContentPartContext.Provider, { value: context, children });
522
+ };
523
+
524
+ // src/primitives/message/MessageContent.tsx
525
+ var import_jsx_runtime7 = require("react/jsx-runtime");
450
526
  var defaultComponents = {
451
- Text: ({ part }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: part.text }),
527
+ Text: ({ part }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
528
+ part.text,
529
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContentPartLoadingIndicator, {})
530
+ ] }),
452
531
  Image: () => null,
453
532
  UI: ({ part }) => part.display,
454
533
  tools: {
@@ -463,29 +542,61 @@ var MessageContent = ({
463
542
  tools: { by_name = {}, Fallback = defaultComponents.tools.Fallback } = {}
464
543
  } = {}
465
544
  }) => {
545
+ const { useThread } = useAssistantContext();
466
546
  const { useMessage } = useMessageContext();
467
547
  const content = useMessage((s) => s.message.content);
468
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: content.map((part, i) => {
548
+ const isLoading = useCombinedStore(
549
+ [useThread, useMessage],
550
+ (t, s) => s.isLast && t.isRunning
551
+ );
552
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: content.map((part, i) => {
469
553
  const key = i;
470
- switch (part.type) {
554
+ const type = part.type;
555
+ let component = null;
556
+ switch (type) {
471
557
  case "text":
472
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { part }, key);
558
+ component = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { part });
559
+ break;
473
560
  case "image":
474
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Image, { part }, key);
561
+ component = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Image, { part });
562
+ break;
475
563
  case "ui":
476
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(UI, { part }, key);
564
+ component = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(UI, { part });
565
+ break;
477
566
  case "tool-call": {
478
567
  const Tool = by_name[part.name] || Fallback;
479
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Tool, { part }, key);
568
+ component = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Tool, { part });
569
+ break;
480
570
  }
481
571
  default:
482
- return null;
572
+ throw new Error(`Unknown content part type: ${type}`);
483
573
  }
574
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
575
+ ContentPartProvider,
576
+ {
577
+ part,
578
+ isLoading: i === content.length - 1 && isLoading,
579
+ children: component
580
+ },
581
+ key
582
+ );
484
583
  }) });
485
584
  };
486
585
 
586
+ // src/primitives/message/MessageLoading.tsx
587
+ var import_react_primitive4 = require("@radix-ui/react-primitive");
588
+ var import_react14 = require("react");
589
+ var import_jsx_runtime8 = require("react/jsx-runtime");
590
+ var MessageLoading = (0, import_react14.forwardRef)((props, ref) => {
591
+ const { useMessage } = useMessageContext();
592
+ (0, import_react14.useMemo)(() => {
593
+ useMessage.getState().setLoadingIndicator(/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_primitive4.Primitive.div, { ...props, ref }));
594
+ }, [useMessage, props, ref]);
595
+ return null;
596
+ });
597
+
487
598
  // src/primitives/thread/ThreadMessages.tsx
488
- var import_jsx_runtime7 = require("react/jsx-runtime");
599
+ var import_jsx_runtime9 = require("react/jsx-runtime");
489
600
  var getComponents = (components) => {
490
601
  return {
491
602
  EditComposer: components.EditComposer ?? components.UserMessage ?? components.Message,
@@ -498,21 +609,20 @@ var ThreadMessages = ({ components }) => {
498
609
  const thread = useThread();
499
610
  const messages = thread.messages;
500
611
  const { UserMessage, EditComposer, AssistantMessage } = getComponents(components);
501
- if (messages.length === 0)
502
- return null;
503
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: messages.map((message, idx) => {
612
+ if (messages.length === 0) return null;
613
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: messages.map((message, idx) => {
504
614
  const parentId = messages[idx - 1]?.id ?? null;
505
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
615
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
506
616
  MessageProvider,
507
617
  {
508
618
  message,
509
619
  parentId,
510
620
  children: [
511
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(MessageIf, { user: true, children: [
512
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ComposerIf, { editing: false, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(UserMessage, {}) }),
513
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ComposerIf, { editing: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(EditComposer, {}) })
621
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(MessageIf, { user: true, children: [
622
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ComposerIf, { editing: false, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(UserMessage, {}) }),
623
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ComposerIf, { editing: true, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EditComposer, {}) })
514
624
  ] }),
515
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageIf, { assistant: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AssistantMessage, {}) })
625
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MessageIf, { assistant: true, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AssistantMessage, {}) })
516
626
  ]
517
627
  },
518
628
  parentId ?? "__ROOT__"
@@ -522,17 +632,17 @@ var ThreadMessages = ({ components }) => {
522
632
 
523
633
  // src/primitives/thread/ThreadScrollToBottom.tsx
524
634
  var import_primitive3 = require("@radix-ui/primitive");
525
- var import_react_primitive4 = require("@radix-ui/react-primitive");
526
- var import_react10 = require("react");
527
- var import_jsx_runtime8 = require("react/jsx-runtime");
528
- var ThreadScrollToBottom = (0, import_react10.forwardRef)(({ onClick, ...rest }, ref) => {
635
+ var import_react_primitive5 = require("@radix-ui/react-primitive");
636
+ var import_react15 = require("react");
637
+ var import_jsx_runtime10 = require("react/jsx-runtime");
638
+ var ThreadScrollToBottom = (0, import_react15.forwardRef)(({ onClick, ...rest }, ref) => {
529
639
  const { useViewport } = useAssistantContext();
530
640
  const isAtBottom = useViewport((s) => s.isAtBottom);
531
641
  const handleScrollToBottom = () => {
532
642
  useViewport.getState().scrollToBottom();
533
643
  };
534
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
535
- import_react_primitive4.Primitive.button,
644
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
645
+ import_react_primitive5.Primitive.button,
536
646
  {
537
647
  ...rest,
538
648
  disabled: isAtBottom,
@@ -544,10 +654,10 @@ var ThreadScrollToBottom = (0, import_react10.forwardRef)(({ onClick, ...rest },
544
654
 
545
655
  // src/primitives/thread/ThreadSuggestion.tsx
546
656
  var import_primitive4 = require("@radix-ui/primitive");
547
- var import_react_primitive5 = require("@radix-ui/react-primitive");
548
- var import_react11 = require("react");
549
- var import_jsx_runtime9 = require("react/jsx-runtime");
550
- var ThreadSuggestion = (0, import_react11.forwardRef)(({ onClick, prompt, method, autoSend: send, ...rest }, ref) => {
657
+ var import_react_primitive6 = require("@radix-ui/react-primitive");
658
+ var import_react16 = require("react");
659
+ var import_jsx_runtime11 = require("react/jsx-runtime");
660
+ var ThreadSuggestion = (0, import_react16.forwardRef)(({ onClick, prompt, method, autoSend: send, ...rest }, ref) => {
551
661
  const { useThread, useComposer } = useAssistantContext();
552
662
  const isDisabled = useThread((t) => t.isRunning);
553
663
  const handleApplySuggestion = () => {
@@ -558,8 +668,8 @@ var ThreadSuggestion = (0, import_react11.forwardRef)(({ onClick, prompt, method
558
668
  composer.send();
559
669
  }
560
670
  };
561
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
562
- import_react_primitive5.Primitive.button,
671
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
672
+ import_react_primitive6.Primitive.button,
563
673
  {
564
674
  ...rest,
565
675
  disabled: isDisabled,
@@ -582,25 +692,24 @@ __export(composer_exports, {
582
692
  // src/primitives/composer/ComposerRoot.tsx
583
693
  var import_primitive5 = require("@radix-ui/primitive");
584
694
  var import_react_compose_refs2 = require("@radix-ui/react-compose-refs");
585
- var import_react_primitive6 = require("@radix-ui/react-primitive");
586
- var import_react12 = require("react");
587
- var import_jsx_runtime10 = require("react/jsx-runtime");
588
- var ComposerRoot = (0, import_react12.forwardRef)(
695
+ var import_react_primitive7 = require("@radix-ui/react-primitive");
696
+ var import_react17 = require("react");
697
+ var import_jsx_runtime12 = require("react/jsx-runtime");
698
+ var ComposerRoot = (0, import_react17.forwardRef)(
589
699
  ({ onSubmit, ...rest }, forwardedRef) => {
590
700
  const { useViewport } = useAssistantContext();
591
701
  const { useComposer } = useComposerContext();
592
- const formRef = (0, import_react12.useRef)(null);
702
+ const formRef = (0, import_react17.useRef)(null);
593
703
  const ref = (0, import_react_compose_refs2.useComposedRefs)(forwardedRef, formRef);
594
704
  const handleSubmit = (e) => {
595
705
  const composerState = useComposer.getState();
596
- if (!composerState.isEditing)
597
- return;
706
+ if (!composerState.isEditing) return;
598
707
  e.preventDefault();
599
708
  composerState.send();
600
709
  useViewport.getState().scrollToBottom();
601
710
  };
602
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
603
- import_react_primitive6.Primitive.form,
711
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
712
+ import_react_primitive7.Primitive.form,
604
713
  {
605
714
  ...rest,
606
715
  ref,
@@ -614,22 +723,20 @@ var ComposerRoot = (0, import_react12.forwardRef)(
614
723
  var import_primitive6 = require("@radix-ui/primitive");
615
724
  var import_react_compose_refs3 = require("@radix-ui/react-compose-refs");
616
725
  var import_react_slot = require("@radix-ui/react-slot");
617
- var import_react13 = require("react");
726
+ var import_react18 = require("react");
618
727
  var import_react_textarea_autosize = __toESM(require("react-textarea-autosize"));
619
- var import_jsx_runtime11 = require("react/jsx-runtime");
620
- var ComposerInput = (0, import_react13.forwardRef)(
728
+ var import_jsx_runtime13 = require("react/jsx-runtime");
729
+ var ComposerInput = (0, import_react18.forwardRef)(
621
730
  ({ autoFocus = false, asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
622
731
  const { useThread, useViewport } = useAssistantContext();
623
732
  const { useComposer, type } = useComposerContext();
624
733
  const value = useComposer((c) => {
625
- if (!c.isEditing)
626
- return "";
734
+ if (!c.isEditing) return "";
627
735
  return c.value;
628
736
  });
629
737
  const Component = asChild ? import_react_slot.Slot : import_react_textarea_autosize.default;
630
738
  const handleKeyPress = (e) => {
631
- if (disabled)
632
- return;
739
+ if (disabled) return;
633
740
  const composer = useComposer.getState();
634
741
  if (e.key === "Escape") {
635
742
  if (useComposer.getState().cancel()) {
@@ -644,26 +751,25 @@ var ComposerInput = (0, import_react13.forwardRef)(
644
751
  }
645
752
  }
646
753
  };
647
- const textareaRef = (0, import_react13.useRef)(null);
754
+ const textareaRef = (0, import_react18.useRef)(null);
648
755
  const ref = (0, import_react_compose_refs3.useComposedRefs)(forwardedRef, textareaRef);
649
756
  const autoFocusEnabled = autoFocus && !disabled;
650
- const focus = (0, import_react13.useCallback)(() => {
757
+ const focus = (0, import_react18.useCallback)(() => {
651
758
  const textarea = textareaRef.current;
652
- if (!textarea || !autoFocusEnabled)
653
- return;
759
+ if (!textarea || !autoFocusEnabled) return;
654
760
  textarea.focus();
655
761
  textarea.setSelectionRange(
656
762
  textareaRef.current.value.length,
657
763
  textareaRef.current.value.length
658
764
  );
659
765
  }, [autoFocusEnabled]);
660
- (0, import_react13.useEffect)(() => focus(), [focus]);
766
+ (0, import_react18.useEffect)(() => focus(), [focus]);
661
767
  useOnScrollToBottom(() => {
662
768
  if (type === "assistant") {
663
769
  focus();
664
770
  }
665
771
  });
666
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
772
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
667
773
  Component,
668
774
  {
669
775
  value,
@@ -672,8 +778,7 @@ var ComposerInput = (0, import_react13.forwardRef)(
672
778
  disabled,
673
779
  onChange: (0, import_primitive6.composeEventHandlers)(onChange, (e) => {
674
780
  const composerState = useComposer.getState();
675
- if (!composerState.isEditing)
676
- return;
781
+ if (!composerState.isEditing) return;
677
782
  return composerState.setValue(e.target.value);
678
783
  }),
679
784
  onKeyDown: (0, import_primitive6.composeEventHandlers)(onKeyDown, handleKeyPress)
@@ -683,15 +788,15 @@ var ComposerInput = (0, import_react13.forwardRef)(
683
788
  );
684
789
 
685
790
  // src/primitives/composer/ComposerSend.tsx
686
- var import_react_primitive7 = require("@radix-ui/react-primitive");
687
- var import_react14 = require("react");
688
- var import_jsx_runtime12 = require("react/jsx-runtime");
689
- var ComposerSend = (0, import_react14.forwardRef)(
791
+ var import_react_primitive8 = require("@radix-ui/react-primitive");
792
+ var import_react19 = require("react");
793
+ var import_jsx_runtime14 = require("react/jsx-runtime");
794
+ var ComposerSend = (0, import_react19.forwardRef)(
690
795
  ({ disabled, ...rest }, ref) => {
691
796
  const { useComposer } = useComposerContext();
692
797
  const hasValue = useComposer((c) => c.isEditing && c.value.length > 0);
693
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
694
- import_react_primitive7.Primitive.button,
798
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
799
+ import_react_primitive8.Primitive.button,
695
800
  {
696
801
  type: "submit",
697
802
  ...rest,
@@ -704,16 +809,16 @@ var ComposerSend = (0, import_react14.forwardRef)(
704
809
 
705
810
  // src/primitives/composer/ComposerCancel.tsx
706
811
  var import_primitive7 = require("@radix-ui/primitive");
707
- var import_react_primitive8 = require("@radix-ui/react-primitive");
708
- var import_react15 = require("react");
709
- var import_jsx_runtime13 = require("react/jsx-runtime");
710
- var ComposerCancel = (0, import_react15.forwardRef)(({ onClick, ...rest }, ref) => {
812
+ var import_react_primitive9 = require("@radix-ui/react-primitive");
813
+ var import_react20 = require("react");
814
+ var import_jsx_runtime15 = require("react/jsx-runtime");
815
+ var ComposerCancel = (0, import_react20.forwardRef)(({ onClick, ...rest }, ref) => {
711
816
  const { useComposer } = useComposerContext();
712
817
  const handleCancel = () => {
713
818
  useComposer.getState().cancel();
714
819
  };
715
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
716
- import_react_primitive8.Primitive.button,
820
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
821
+ import_react_primitive9.Primitive.button,
717
822
  {
718
823
  type: "button",
719
824
  ...rest,
@@ -733,32 +838,6 @@ __export(branchPicker_exports, {
733
838
  Root: () => BranchPickerRoot
734
839
  });
735
840
 
736
- // src/utils/context/combined/useCombinedStore.ts
737
- var import_react17 = require("react");
738
-
739
- // src/utils/context/combined/createCombinedStore.ts
740
- var import_react16 = require("react");
741
- var createCombinedStore = (stores) => {
742
- const subscribe = (callback) => {
743
- const unsubscribes = stores.map((store) => store.subscribe(callback));
744
- return () => {
745
- for (const unsub of unsubscribes) {
746
- unsub();
747
- }
748
- };
749
- };
750
- return (selector) => {
751
- const getSnapshot = () => selector(...stores.map((store) => store.getState()));
752
- return (0, import_react16.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
753
- };
754
- };
755
-
756
- // src/utils/context/combined/useCombinedStore.ts
757
- var useCombinedStore = (stores, selector) => {
758
- const useCombined = (0, import_react17.useMemo)(() => createCombinedStore(stores), stores);
759
- return useCombined(selector);
760
- };
761
-
762
841
  // src/actions/useGoToNextBranch.tsx
763
842
  var useGoToNextBranch = () => {
764
843
  const { useThread } = useAssistantContext();
@@ -767,8 +846,7 @@ var useGoToNextBranch = () => {
767
846
  [useMessage, useComposer],
768
847
  (m, c) => c.isEditing || m.branches.indexOf(m.message.id) + 1 >= m.branches.length
769
848
  );
770
- if (disabled)
771
- return null;
849
+ if (disabled) return null;
772
850
  return () => {
773
851
  const { message, branches } = useMessage.getState();
774
852
  useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
@@ -777,15 +855,15 @@ var useGoToNextBranch = () => {
777
855
 
778
856
  // src/utils/createActionButton.tsx
779
857
  var import_primitive8 = require("@radix-ui/primitive");
780
- var import_react_primitive9 = require("@radix-ui/react-primitive");
781
- var import_react18 = require("react");
782
- var import_jsx_runtime14 = require("react/jsx-runtime");
858
+ var import_react_primitive10 = require("@radix-ui/react-primitive");
859
+ var import_react21 = require("react");
860
+ var import_jsx_runtime16 = require("react/jsx-runtime");
783
861
  var createActionButton = (useActionButton) => {
784
- return (0, import_react18.forwardRef)(
862
+ return (0, import_react21.forwardRef)(
785
863
  (props, forwardedRef) => {
786
864
  const onClick = useActionButton(props);
787
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
788
- import_react_primitive9.Primitive.button,
865
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
866
+ import_react_primitive10.Primitive.button,
789
867
  {
790
868
  type: "button",
791
869
  disabled: !onClick,
@@ -809,8 +887,7 @@ var useGoToPreviousBranch = () => {
809
887
  [useMessage, useComposer],
810
888
  (m, c) => c.isEditing || m.branches.indexOf(m.message.id) <= 0
811
889
  );
812
- if (disabled)
813
- return null;
890
+ if (disabled) return null;
814
891
  return () => {
815
892
  const { message, branches } = useMessage.getState();
816
893
  useThread.getState().switchToBranch(
@@ -824,27 +901,27 @@ var useGoToPreviousBranch = () => {
824
901
  var BranchPickerPrevious = createActionButton(useGoToPreviousBranch);
825
902
 
826
903
  // src/primitives/branchPicker/BranchPickerCount.tsx
827
- var import_jsx_runtime15 = require("react/jsx-runtime");
904
+ var import_jsx_runtime17 = require("react/jsx-runtime");
828
905
  var BranchPickerCount = () => {
829
906
  const { useMessage } = useMessageContext();
830
907
  const branchCount = useMessage((s) => s.branches.length);
831
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children: branchCount });
908
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_jsx_runtime17.Fragment, { children: branchCount });
832
909
  };
833
910
 
834
911
  // src/primitives/branchPicker/BranchPickerNumber.tsx
835
- var import_jsx_runtime16 = require("react/jsx-runtime");
912
+ var import_jsx_runtime18 = require("react/jsx-runtime");
836
913
  var BranchPickerNumber = () => {
837
914
  const { useMessage } = useMessageContext();
838
915
  const branchIdx = useMessage((s) => s.branches.indexOf(s.message.id));
839
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: branchIdx + 1 });
916
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_jsx_runtime18.Fragment, { children: branchIdx + 1 });
840
917
  };
841
918
 
842
919
  // src/primitives/branchPicker/BranchPickerRoot.tsx
843
- var import_react_primitive10 = require("@radix-ui/react-primitive");
844
- var import_react19 = require("react");
845
- var import_jsx_runtime17 = require("react/jsx-runtime");
846
- var BranchPickerRoot = (0, import_react19.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
847
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_primitive10.Primitive.div, { ...rest, ref }) });
920
+ var import_react_primitive11 = require("@radix-ui/react-primitive");
921
+ var import_react22 = require("react");
922
+ var import_jsx_runtime19 = require("react/jsx-runtime");
923
+ var BranchPickerRoot = (0, import_react22.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
924
+ 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 }) });
848
925
  });
849
926
 
850
927
  // src/primitives/actionBar/index.ts
@@ -857,31 +934,27 @@ __export(actionBar_exports, {
857
934
  });
858
935
 
859
936
  // src/primitives/actionBar/ActionBarRoot.tsx
860
- var import_react_primitive11 = require("@radix-ui/react-primitive");
861
- var import_react20 = require("react");
862
- var import_jsx_runtime18 = require("react/jsx-runtime");
863
- var ActionBarRoot = (0, import_react20.forwardRef)(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
937
+ var import_react_primitive12 = require("@radix-ui/react-primitive");
938
+ var import_react23 = require("react");
939
+ var import_jsx_runtime20 = require("react/jsx-runtime");
940
+ var ActionBarRoot = (0, import_react23.forwardRef)(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
864
941
  const { useThread } = useAssistantContext();
865
942
  const { useMessage } = useMessageContext();
866
943
  const hideAndfloatStatus = useCombinedStore(
867
944
  [useThread, useMessage],
868
945
  (t, m) => {
869
- if (hideWhenRunning && t.isRunning)
870
- return "hidden" /* Hidden */;
946
+ if (hideWhenRunning && t.isRunning) return "hidden" /* Hidden */;
871
947
  const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
872
- if (!autohideEnabled)
873
- return "normal" /* Normal */;
874
- if (!m.isHovering)
875
- return "hidden" /* Hidden */;
948
+ if (!autohideEnabled) return "normal" /* Normal */;
949
+ if (!m.isHovering) return "hidden" /* Hidden */;
876
950
  if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branches.length <= 1)
877
951
  return "floating" /* Floating */;
878
952
  return "normal" /* Normal */;
879
953
  }
880
954
  );
881
- if (hideAndfloatStatus === "hidden" /* Hidden */)
882
- return null;
883
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
884
- import_react_primitive11.Primitive.div,
955
+ if (hideAndfloatStatus === "hidden" /* Hidden */) return null;
956
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
957
+ import_react_primitive12.Primitive.div,
885
958
  {
886
959
  "data-floating": hideAndfloatStatus === "floating" /* Floating */,
887
960
  ...rest,
@@ -893,14 +966,18 @@ var ActionBarRoot = (0, import_react20.forwardRef)(({ hideWhenRunning, autohide,
893
966
  // src/actions/useCopyMessage.tsx
894
967
  var useCopyMessage = ({ copiedDuration = 3e3 }) => {
895
968
  const { useMessage, useComposer } = useMessageContext();
896
- const isEditing = useComposer((s) => s.isEditing);
897
- if (isEditing)
898
- return null;
969
+ const hasCopyableContent = useCombinedStore(
970
+ [useMessage, useComposer],
971
+ (m, c) => {
972
+ return c.isEditing || m.message.content.some((c2) => c2.type === "text");
973
+ }
974
+ );
975
+ if (!hasCopyableContent) return null;
899
976
  return () => {
977
+ const { isEditing, value: composerValue } = useComposer.getState();
900
978
  const { message, setIsCopied } = useMessage.getState();
901
- if (message.content[0]?.type !== "text")
902
- throw new Error("Copying is only supported for text-only messages");
903
- navigator.clipboard.writeText(message.content[0].text);
979
+ const valueToCopy = isEditing ? composerValue : getMessageText(message);
980
+ navigator.clipboard.writeText(valueToCopy);
904
981
  setIsCopied(true);
905
982
  setTimeout(() => setIsCopied(false), copiedDuration);
906
983
  };
@@ -917,12 +994,9 @@ var useReloadMessage = () => {
917
994
  [useThread, useMessage],
918
995
  (t, m) => t.isRunning || m.message.role !== "assistant"
919
996
  );
920
- if (disabled)
921
- return null;
997
+ if (disabled) return null;
922
998
  return () => {
923
- const { message, parentId } = useMessage.getState();
924
- if (message.role !== "assistant")
925
- throw new Error("Reloading is only supported on assistant messages");
999
+ const { parentId } = useMessage.getState();
926
1000
  useThread.getState().startRun(parentId);
927
1001
  useViewport.getState().scrollToBottom();
928
1002
  };
@@ -938,8 +1012,7 @@ var useBeginMessageEdit = () => {
938
1012
  [useMessage, useComposer],
939
1013
  (m, c) => m.message.role !== "user" || c.isEditing
940
1014
  );
941
- if (disabled)
942
- return null;
1015
+ if (disabled) return null;
943
1016
  return () => {
944
1017
  const { edit } = useComposer.getState();
945
1018
  edit();
@@ -949,18 +1022,25 @@ var useBeginMessageEdit = () => {
949
1022
  // src/primitives/actionBar/ActionBarEdit.tsx
950
1023
  var ActionBarEdit = createActionButton(useBeginMessageEdit);
951
1024
 
1025
+ // src/primitives/contentPart/index.ts
1026
+ var contentPart_exports = {};
1027
+ __export(contentPart_exports, {
1028
+ LoadingIndicator: () => ContentPartLoadingIndicator,
1029
+ Provider: () => ContentPartProvider
1030
+ });
1031
+
952
1032
  // src/adapters/vercel/VercelAIAssistantProvider.tsx
953
- var import_react23 = require("react");
1033
+ var import_react26 = require("react");
954
1034
 
955
1035
  // src/adapters/vercel/useDummyAIAssistantContext.tsx
956
- var import_react21 = require("react");
957
- var import_zustand4 = require("zustand");
1036
+ var import_react24 = require("react");
1037
+ var import_zustand5 = require("zustand");
958
1038
 
959
1039
  // src/utils/context/stores/ViewportStore.tsx
960
- var import_zustand3 = require("zustand");
1040
+ var import_zustand4 = require("zustand");
961
1041
  var makeViewportStore = () => {
962
1042
  const scrollToBottomListeners = /* @__PURE__ */ new Set();
963
- return (0, import_zustand3.create)(() => ({
1043
+ return (0, import_zustand4.create)(() => ({
964
1044
  isAtBottom: true,
965
1045
  scrollToBottom: () => {
966
1046
  for (const listener of scrollToBottomListeners) {
@@ -978,7 +1058,7 @@ var makeViewportStore = () => {
978
1058
 
979
1059
  // src/adapters/vercel/useDummyAIAssistantContext.tsx
980
1060
  var makeDummyThreadStore = () => {
981
- return (0, import_zustand4.create)(() => ({
1061
+ return (0, import_zustand5.create)(() => ({
982
1062
  messages: [],
983
1063
  isRunning: false,
984
1064
  getBranches: () => {
@@ -987,19 +1067,19 @@ var makeDummyThreadStore = () => {
987
1067
  switchToBranch: () => {
988
1068
  throw new Error("Not implemented");
989
1069
  },
990
- append: async () => {
1070
+ append: () => {
991
1071
  throw new Error("Not implemented");
992
1072
  },
993
- cancelRun: () => {
1073
+ startRun: () => {
994
1074
  throw new Error("Not implemented");
995
1075
  },
996
- startRun: async () => {
1076
+ cancelRun: () => {
997
1077
  throw new Error("Not implemented");
998
1078
  }
999
1079
  }));
1000
1080
  };
1001
1081
  var useDummyAIAssistantContext = () => {
1002
- const [context] = (0, import_react21.useState)(() => {
1082
+ const [context] = (0, import_react24.useState)(() => {
1003
1083
  const useThread = makeDummyThreadStore();
1004
1084
  const useViewport = makeViewportStore();
1005
1085
  const useComposer = makeThreadComposerStore(useThread);
@@ -1009,7 +1089,7 @@ var useDummyAIAssistantContext = () => {
1009
1089
  };
1010
1090
 
1011
1091
  // src/adapters/vercel/useVercelAIThreadState.tsx
1012
- var import_react22 = require("react");
1092
+ var import_react25 = require("react");
1013
1093
 
1014
1094
  // src/adapters/MessageRepository.tsx
1015
1095
  var import_non_secure = require("nanoid/non-secure");
@@ -1021,15 +1101,48 @@ var optimisticPrefix = "__optimistic__";
1021
1101
  var generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
1022
1102
  var isOptimisticId = (id) => id.startsWith(optimisticPrefix);
1023
1103
  var findHead = (message) => {
1024
- if (message.next)
1025
- return findHead(message.next);
1104
+ if (message.next) return findHead(message.next);
1026
1105
  return message;
1027
1106
  };
1028
1107
  var MessageRepository = class {
1029
1108
  messages = /* @__PURE__ */ new Map();
1030
1109
  // message_id -> item
1031
1110
  head = null;
1032
- rootChildren = [];
1111
+ root = {
1112
+ children: []
1113
+ };
1114
+ getFallbackChild(p) {
1115
+ const childId = p.children.at(-1);
1116
+ const child = childId ? this.messages.get(childId) : null;
1117
+ if (child === void 0)
1118
+ throw new Error(
1119
+ "MessageRepository(getFallbackChild): Child message not found. This is likely an internal bug in assistant-ui."
1120
+ );
1121
+ return child;
1122
+ }
1123
+ performOp(newParent, child, operation) {
1124
+ const parentOrRoot = child.prev ?? this.root;
1125
+ const newParentOrRoot = newParent ?? this.root;
1126
+ if (operation === "relink" && parentOrRoot === newParentOrRoot) return;
1127
+ if (operation !== "link") {
1128
+ parentOrRoot.children = parentOrRoot.children.filter(
1129
+ (m) => m !== child.current.id
1130
+ );
1131
+ if (child.prev?.next === child) {
1132
+ child.prev.next = this.getFallbackChild(child.prev);
1133
+ }
1134
+ }
1135
+ if (operation !== "cut") {
1136
+ newParentOrRoot.children = [
1137
+ ...newParentOrRoot.children,
1138
+ child.current.id
1139
+ ];
1140
+ if (newParent && (findHead(child) === this.head || newParent.next === null)) {
1141
+ newParent.next = child;
1142
+ }
1143
+ child.prev = newParent;
1144
+ }
1145
+ }
1033
1146
  getMessages() {
1034
1147
  const messages = new Array(this.head?.level ?? 0);
1035
1148
  for (let current = this.head; current; current = current.prev) {
@@ -1037,28 +1150,18 @@ var MessageRepository = class {
1037
1150
  }
1038
1151
  return messages;
1039
1152
  }
1040
- // TODO previousId is confusing
1041
- // TODO previousId does not fix children
1042
- // TODO cut / link operations
1043
- addOrUpdateMessage(parentId, message, previousId = message.id) {
1044
- const item = this.messages.get(message.id);
1045
- if (item) {
1046
- if (item.prev?.current.id !== parentId) {
1047
- if ((item.prev?.current.id ?? null) !== parentId) {
1048
- this.deleteMessage(message.id);
1049
- } else {
1050
- item.current = message;
1051
- if (previousId !== message.id) {
1052
- this.messages.delete(previousId);
1053
- this.messages.set(message.id, item);
1054
- }
1055
- return;
1056
- }
1057
- }
1058
- }
1153
+ addOrUpdateMessage(parentId, message) {
1154
+ const existingItem = this.messages.get(message.id);
1059
1155
  const prev = parentId ? this.messages.get(parentId) : null;
1060
1156
  if (prev === void 0)
1061
- throw new Error("Unexpected: Parent message not found");
1157
+ throw new Error(
1158
+ "MessageRepository(addOrUpdateMessage): Parent message not found. This is likely an internal bug in assistant-ui."
1159
+ );
1160
+ if (existingItem) {
1161
+ existingItem.current = message;
1162
+ this.performOp(prev, existingItem, "relink");
1163
+ return;
1164
+ }
1062
1165
  const newItem = {
1063
1166
  prev,
1064
1167
  current: message,
@@ -1067,108 +1170,83 @@ var MessageRepository = class {
1067
1170
  level: prev ? prev.level + 1 : 0
1068
1171
  };
1069
1172
  this.messages.set(message.id, newItem);
1070
- if (prev) {
1071
- prev.children = [...prev.children, message.id];
1072
- prev.next = newItem;
1073
- } else {
1074
- this.rootChildren = [...this.rootChildren, message.id];
1075
- }
1076
1173
  if (this.head === prev) {
1077
1174
  this.head = newItem;
1078
1175
  }
1176
+ this.performOp(prev, newItem, "link");
1079
1177
  }
1080
- deleteMessage(messageId) {
1081
- const message = this.messages.get(messageId);
1082
- if (!message)
1083
- throw new Error("Unexpected: Message not found");
1084
- if (message.children.length > 0) {
1085
- for (const child of message.children) {
1086
- this.deleteMessage(child);
1087
- }
1088
- }
1089
- this.messages.delete(messageId);
1090
- if (message.prev) {
1091
- message.prev.children = message.prev.children.filter(
1092
- (m) => m !== messageId
1093
- );
1094
- if (message.prev.next === message) {
1095
- const childId = message.prev.children.at(-1);
1096
- const child = childId ? this.messages.get(childId) : null;
1097
- if (child === void 0)
1098
- throw new Error("Unexpected: Child message not found");
1099
- message.prev.next = child;
1100
- }
1101
- } else {
1102
- this.rootChildren = this.rootChildren.filter((m) => m !== messageId);
1103
- }
1104
- if (this.head === message) {
1105
- this.head = message.prev ? findHead(message.prev) : null;
1106
- }
1107
- }
1108
- getOptimisticId() {
1178
+ appendOptimisticMessage(parentId, message) {
1109
1179
  let optimisticId;
1110
1180
  do {
1111
1181
  optimisticId = generateOptimisticId();
1112
1182
  } while (this.messages.has(optimisticId));
1113
- return optimisticId;
1114
- }
1115
- commitOptimisticAppend(message) {
1116
- const optimisticIdUser = this.getOptimisticId();
1117
- this.addOrUpdateMessage(message.parentId, {
1118
- id: optimisticIdUser,
1119
- role: "user",
1120
- content: message.content,
1121
- createdAt: /* @__PURE__ */ new Date()
1122
- });
1123
- const optimisticIdAssistant = this.commitOptimisticRun(optimisticIdUser);
1124
- return [optimisticIdUser, optimisticIdAssistant];
1125
- }
1126
- commitOptimisticRun(parentId) {
1127
- const optimisticId = this.getOptimisticId();
1128
1183
  this.addOrUpdateMessage(parentId, {
1184
+ ...message,
1129
1185
  id: optimisticId,
1130
- role: "assistant",
1131
- content: [
1132
- {
1133
- type: "text",
1134
- text: ""
1135
- }
1136
- ],
1137
1186
  createdAt: /* @__PURE__ */ new Date()
1138
1187
  });
1139
1188
  return optimisticId;
1140
1189
  }
1141
- getBranches(messageId) {
1190
+ deleteMessage(messageId, newParentId) {
1142
1191
  const message = this.messages.get(messageId);
1192
+ const newParent = newParentId ? this.messages.get(newParentId) : null;
1143
1193
  if (!message)
1144
- throw new Error("Unexpected: Message not found");
1145
- if (message.prev) {
1146
- return message.prev.children;
1194
+ throw new Error(
1195
+ "MessageRepository(deleteMessage): Optimistic message not found. This is likely an internal bug in assistant-ui."
1196
+ );
1197
+ if (newParent === void 0)
1198
+ throw new Error(
1199
+ "MessageRepository(deleteMessage): New message not found. This is likely an internal bug in assistant-ui."
1200
+ );
1201
+ for (const child of message.children) {
1202
+ const childMessage = this.messages.get(child);
1203
+ if (!childMessage)
1204
+ throw new Error(
1205
+ "MessageRepository(deleteMessage): Child message not found. This is likely an internal bug in assistant-ui."
1206
+ );
1207
+ this.performOp(newParent, childMessage, "relink");
1147
1208
  }
1148
- return this.rootChildren;
1209
+ this.messages.delete(messageId);
1210
+ if (this.head === message) {
1211
+ this.head = this.getFallbackChild(message.prev ?? this.root);
1212
+ }
1213
+ this.performOp(null, message, "cut");
1214
+ }
1215
+ getBranches(messageId) {
1216
+ const message = this.messages.get(messageId);
1217
+ if (!message)
1218
+ throw new Error(
1219
+ "MessageRepository(getBranches): Message not found. This is likely an internal bug in assistant-ui."
1220
+ );
1221
+ const { children } = message.prev ?? this.root;
1222
+ return children;
1149
1223
  }
1150
1224
  switchToBranch(messageId) {
1151
1225
  const message = this.messages.get(messageId);
1152
1226
  if (!message)
1153
- throw new Error("Unexpected: Branch not found");
1227
+ throw new Error(
1228
+ "MessageRepository(switchToBranch): Branch not found. This is likely an internal bug in assistant-ui."
1229
+ );
1154
1230
  if (message.prev) {
1155
1231
  message.prev.next = message;
1156
1232
  }
1157
1233
  this.head = findHead(message);
1158
1234
  }
1159
1235
  resetHead(messageId) {
1160
- if (messageId) {
1161
- const message = this.messages.get(messageId);
1162
- if (!message)
1163
- throw new Error("Unexpected: Branch not found");
1164
- this.head = message;
1165
- for (let current = message; current; current = current.prev) {
1166
- if (current.prev) {
1167
- current.prev.next = current;
1168
- }
1169
- }
1170
- } else {
1236
+ if (messageId === null) {
1171
1237
  this.head = null;
1238
+ return;
1239
+ }
1240
+ const message = this.messages.get(messageId);
1241
+ if (!message)
1242
+ throw new Error(
1243
+ "MessageRepository(resetHead): Branch not found. This is likely an internal bug in assistant-ui."
1244
+ );
1245
+ this.head = message;
1246
+ for (let current = message; current; current = current.prev) {
1247
+ if (current.prev) {
1248
+ current.prev.next = current;
1249
+ }
1172
1250
  }
1173
1251
  }
1174
1252
  };
@@ -1182,8 +1260,7 @@ var ThreadMessageConverter = class {
1182
1260
  convertMessages(messages) {
1183
1261
  return messages.map((m) => {
1184
1262
  const cached = this.cache.get(m);
1185
- if (cached)
1186
- return cached;
1263
+ if (cached) return cached;
1187
1264
  const newMessage = this.converter(m);
1188
1265
  this.cache.set(m, newMessage);
1189
1266
  return newMessage;
@@ -1194,41 +1271,51 @@ var ThreadMessageConverter = class {
1194
1271
  // src/adapters/vercel/useVercelAIThreadState.tsx
1195
1272
  var vercelToThreadMessage = (message) => {
1196
1273
  if (message.role !== "user" && message.role !== "assistant")
1197
- throw new Error("Unsupported role");
1274
+ throw new Error(
1275
+ `You have a message with an unsupported role. The role ${message.role} is not supported.`
1276
+ );
1198
1277
  return {
1199
1278
  id: message.id,
1200
1279
  role: message.role,
1201
- content: [{ type: "text", text: message.content }],
1280
+ content: [
1281
+ ...message.content ? [{ type: "text", text: message.content }] : [],
1282
+ ...message.toolInvocations?.map((t) => ({
1283
+ type: "tool-call",
1284
+ name: t.toolName,
1285
+ args: t.args,
1286
+ result: "result" in t ? t.result : void 0
1287
+ })) ?? []
1288
+ ],
1289
+ // ignore type mismatch for now
1202
1290
  createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1203
1291
  innerMessage: message
1204
1292
  };
1205
1293
  };
1206
1294
  var converter = new ThreadMessageConverter(vercelToThreadMessage);
1207
1295
  var sliceMessagesUntil = (messages, messageId) => {
1208
- if (messageId == null)
1209
- return [];
1210
- if (isOptimisticId(messageId))
1211
- return messages;
1296
+ if (messageId == null) return [];
1297
+ if (isOptimisticId(messageId)) return messages;
1212
1298
  const messageIdx = messages.findIndex((m) => m.id === messageId);
1213
1299
  if (messageIdx === -1)
1214
- throw new Error("Unexpected: Message not found");
1300
+ throw new Error(
1301
+ "useVercelAIThreadState: Message not found. This is liekly an internal bug in assistant-ui."
1302
+ );
1215
1303
  return messages.slice(0, messageIdx + 1);
1216
1304
  };
1217
1305
  var hasUpcomingMessage = (isRunning, messages) => {
1218
1306
  return isRunning && messages[messages.length - 1]?.role !== "assistant";
1219
1307
  };
1220
1308
  var getIsRunning = (vercel) => {
1221
- if ("isLoading" in vercel)
1222
- return vercel.isLoading;
1309
+ if ("isLoading" in vercel) return vercel.isLoading;
1223
1310
  return vercel.status === "in_progress";
1224
1311
  };
1225
1312
  var useVercelAIThreadState = (vercel) => {
1226
- const [data] = (0, import_react22.useState)(() => new MessageRepository());
1227
- const vercelRef = (0, import_react22.useRef)(vercel);
1313
+ const [data] = (0, import_react25.useState)(() => new MessageRepository());
1314
+ const vercelRef = (0, import_react25.useRef)(vercel);
1228
1315
  vercelRef.current = vercel;
1229
1316
  const isRunning = getIsRunning(vercelRef.current);
1230
- const assistantOptimisticIdRef = (0, import_react22.useRef)(null);
1231
- const messages = (0, import_react22.useMemo)(() => {
1317
+ const assistantOptimisticIdRef = (0, import_react25.useRef)(null);
1318
+ const messages = (0, import_react25.useMemo)(() => {
1232
1319
  const vm = converter.convertMessages(vercel.messages);
1233
1320
  for (let i = 0; i < vm.length; i++) {
1234
1321
  const message = vm[i];
@@ -1236,24 +1323,28 @@ var useVercelAIThreadState = (vercel) => {
1236
1323
  data.addOrUpdateMessage(parent?.id ?? null, message);
1237
1324
  }
1238
1325
  if (assistantOptimisticIdRef.current) {
1239
- data.deleteMessage(assistantOptimisticIdRef.current);
1326
+ data.deleteMessage(assistantOptimisticIdRef.current, null);
1240
1327
  assistantOptimisticIdRef.current = null;
1241
1328
  }
1242
1329
  if (hasUpcomingMessage(isRunning, vm)) {
1243
- assistantOptimisticIdRef.current = data.commitOptimisticRun(
1244
- vm.at(-1)?.id ?? null
1330
+ assistantOptimisticIdRef.current = data.appendOptimisticMessage(
1331
+ vm.at(-1)?.id ?? null,
1332
+ {
1333
+ role: "assistant",
1334
+ content: [{ type: "text", text: "" }]
1335
+ }
1245
1336
  );
1246
1337
  }
1247
1338
  data.resetHead(assistantOptimisticIdRef.current ?? vm.at(-1)?.id ?? null);
1248
1339
  return data.getMessages();
1249
1340
  }, [data, isRunning, vercel.messages]);
1250
- const getBranches = (0, import_react22.useCallback)(
1341
+ const getBranches2 = (0, import_react25.useCallback)(
1251
1342
  (messageId) => {
1252
1343
  return data.getBranches(messageId);
1253
1344
  },
1254
1345
  [data]
1255
1346
  );
1256
- const switchToBranch = (0, import_react22.useCallback)(
1347
+ const switchToBranch2 = (0, import_react25.useCallback)(
1257
1348
  (messageId) => {
1258
1349
  data.switchToBranch(messageId);
1259
1350
  vercelRef.current.setMessages(
@@ -1262,10 +1353,12 @@ var useVercelAIThreadState = (vercel) => {
1262
1353
  },
1263
1354
  [data]
1264
1355
  );
1265
- const startRun = (0, import_react22.useCallback)(async (parentId) => {
1356
+ const startRun = (0, import_react25.useCallback)(async (parentId) => {
1266
1357
  const reloadMaybe = "reload" in vercelRef.current ? vercelRef.current.reload : void 0;
1267
1358
  if (!reloadMaybe)
1268
- throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
1359
+ throw new Error(
1360
+ "Reload is not supported by Vercel AI SDK's useAssistant."
1361
+ );
1269
1362
  const newMessages = sliceMessagesUntil(
1270
1363
  vercelRef.current.messages,
1271
1364
  parentId
@@ -1273,9 +1366,9 @@ var useVercelAIThreadState = (vercel) => {
1273
1366
  vercelRef.current.setMessages(newMessages);
1274
1367
  await reloadMaybe();
1275
1368
  }, []);
1276
- const append = (0, import_react22.useCallback)(async (message) => {
1369
+ const append = (0, import_react25.useCallback)(async (message) => {
1277
1370
  if (message.content.length !== 1 || message.content[0]?.type !== "text")
1278
- throw new Error("Only text content is supported by Vercel AI SDK");
1371
+ throw new Error("Only text content is supported by Vercel AI SDK.");
1279
1372
  const newMessages = sliceMessagesUntil(
1280
1373
  vercelRef.current.messages,
1281
1374
  message.parentId
@@ -1286,37 +1379,37 @@ var useVercelAIThreadState = (vercel) => {
1286
1379
  content: message.content[0].text
1287
1380
  });
1288
1381
  }, []);
1289
- const cancelRun = (0, import_react22.useCallback)(() => {
1382
+ const cancelRun2 = (0, import_react25.useCallback)(() => {
1290
1383
  const lastMessage = vercelRef.current.messages.at(-1);
1291
1384
  vercelRef.current.stop();
1292
1385
  if (lastMessage?.role === "user") {
1293
1386
  vercelRef.current.setInput(lastMessage.content);
1294
1387
  }
1295
1388
  }, []);
1296
- return (0, import_react22.useMemo)(
1389
+ return (0, import_react25.useMemo)(
1297
1390
  () => ({
1298
1391
  isRunning,
1299
1392
  messages,
1300
- getBranches,
1301
- switchToBranch,
1393
+ getBranches: getBranches2,
1394
+ switchToBranch: switchToBranch2,
1302
1395
  append,
1303
1396
  startRun,
1304
- cancelRun
1397
+ cancelRun: cancelRun2
1305
1398
  }),
1306
1399
  [
1307
1400
  isRunning,
1308
1401
  messages,
1309
- getBranches,
1310
- switchToBranch,
1402
+ getBranches2,
1403
+ switchToBranch2,
1311
1404
  append,
1312
1405
  startRun,
1313
- cancelRun
1406
+ cancelRun2
1314
1407
  ]
1315
1408
  );
1316
1409
  };
1317
1410
 
1318
1411
  // src/adapters/vercel/VercelAIAssistantProvider.tsx
1319
- var import_jsx_runtime19 = require("react/jsx-runtime");
1412
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1320
1413
  var VercelAIAssistantProvider = ({
1321
1414
  children,
1322
1415
  ...rest
@@ -1324,24 +1417,22 @@ var VercelAIAssistantProvider = ({
1324
1417
  const context = useDummyAIAssistantContext();
1325
1418
  const vercel = "chat" in rest ? rest.chat : rest.assistant;
1326
1419
  const threadState = useVercelAIThreadState(vercel);
1327
- (0, import_react23.useMemo)(() => {
1420
+ (0, import_react26.useMemo)(() => {
1328
1421
  context.useThread.setState(threadState, true);
1329
1422
  }, [context, threadState]);
1330
- (0, import_react23.useMemo)(() => {
1423
+ (0, import_react26.useMemo)(() => {
1331
1424
  context.useComposer.setState({
1332
1425
  value: vercel.input,
1333
1426
  setValue: vercel.setInput
1334
1427
  });
1335
1428
  }, [context, vercel.input, vercel.setInput]);
1336
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AssistantContext.Provider, { value: context, children });
1429
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(AssistantContext.Provider, { value: context, children });
1337
1430
  };
1338
1431
 
1339
1432
  // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1340
- var import_react24 = require("react");
1341
- var import_jsx_runtime20 = require("react/jsx-runtime");
1433
+ var import_react27 = require("react");
1434
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1342
1435
  var vercelToThreadMessage2 = (message) => {
1343
- if (message.role !== "user" && message.role !== "assistant")
1344
- throw new Error("Unsupported role");
1345
1436
  return {
1346
1437
  id: message.id,
1347
1438
  role: message.role,
@@ -1349,6 +1440,22 @@ var vercelToThreadMessage2 = (message) => {
1349
1440
  createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
1350
1441
  };
1351
1442
  };
1443
+ var EMPTY_BRANCHES = [];
1444
+ var getBranches = () => {
1445
+ return EMPTY_BRANCHES;
1446
+ };
1447
+ var switchToBranch = () => {
1448
+ throw new Error(
1449
+ "Branch switching is not supported by VercelRSCAssistantProvider."
1450
+ );
1451
+ };
1452
+ var cancelRun = () => {
1453
+ if (process.env["NODE_ENV"] === "development") {
1454
+ console.warn(
1455
+ "Run cancellation is not supported by VercelRSCAssistantProvider."
1456
+ );
1457
+ }
1458
+ };
1352
1459
  var VercelRSCAssistantProvider = ({
1353
1460
  children,
1354
1461
  convertMessage,
@@ -1358,53 +1465,66 @@ var VercelRSCAssistantProvider = ({
1358
1465
  reload
1359
1466
  }) => {
1360
1467
  const context = useDummyAIAssistantContext();
1361
- const converter2 = (0, import_react24.useMemo)(() => {
1468
+ const [isRunning, setIsRunning] = (0, import_react27.useState)(false);
1469
+ const withRunning = (0, import_react27.useCallback)((callback) => {
1470
+ setIsRunning(true);
1471
+ return callback.finally(() => setIsRunning(false));
1472
+ }, []);
1473
+ const converter2 = (0, import_react27.useMemo)(() => {
1362
1474
  const rscConverter = convertMessage ?? ((m) => m);
1363
1475
  return new ThreadMessageConverter((m) => {
1364
1476
  return vercelToThreadMessage2(rscConverter(m));
1365
1477
  });
1366
1478
  }, [convertMessage]);
1367
- const messages = (0, import_react24.useMemo)(() => {
1479
+ const messages = (0, import_react27.useMemo)(() => {
1368
1480
  return converter2.convertMessages(vercelMessages);
1369
1481
  }, [converter2, vercelMessages]);
1370
- const append = (0, import_react24.useCallback)(
1482
+ const append = (0, import_react27.useCallback)(
1371
1483
  async (message) => {
1372
1484
  if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? null)) {
1373
1485
  if (!edit)
1374
1486
  throw new Error(
1375
- "Unexpected: Message editing is not supported, no edit callback was provided to VercelRSCAssistantProvider."
1487
+ "Message editing is not enabled, please provide an edit callback to VercelRSCAssistantProvider."
1376
1488
  );
1377
- await edit(message);
1489
+ await withRunning(edit(message));
1378
1490
  } else {
1379
- await appendCallback(message);
1491
+ await withRunning(appendCallback(message));
1380
1492
  }
1381
1493
  },
1382
- [context, appendCallback, edit]
1494
+ [context, withRunning, appendCallback, edit]
1383
1495
  );
1384
- const startRun = (0, import_react24.useCallback)(
1496
+ const startRun = (0, import_react27.useCallback)(
1385
1497
  async (parentId) => {
1386
1498
  if (!reload)
1387
1499
  throw new Error(
1388
- "Unexpected: Message reloading is not supported, no reload callback was provided to VercelRSCAssistantProvider."
1500
+ "Message reloading is not enabled, please provide a reload callback to VercelRSCAssistantProvider."
1389
1501
  );
1390
- await reload(parentId);
1502
+ await withRunning(reload(parentId));
1391
1503
  },
1392
- [reload]
1504
+ [withRunning, reload]
1393
1505
  );
1394
- (0, import_react24.useMemo)(() => {
1395
- context.useThread.setState({
1396
- messages,
1397
- append,
1398
- startRun
1399
- });
1400
- }, [context, messages, append, startRun]);
1401
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(AssistantContext.Provider, { value: context, children });
1506
+ (0, import_react27.useMemo)(() => {
1507
+ context.useThread.setState(
1508
+ {
1509
+ messages,
1510
+ isRunning,
1511
+ getBranches,
1512
+ switchToBranch,
1513
+ append,
1514
+ startRun,
1515
+ cancelRun
1516
+ },
1517
+ true
1518
+ );
1519
+ }, [context, messages, isRunning, append, startRun]);
1520
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(AssistantContext.Provider, { value: context, children });
1402
1521
  };
1403
1522
  // Annotate the CommonJS export names for ESM import in node:
1404
1523
  0 && (module.exports = {
1405
1524
  ActionBarPrimitive,
1406
1525
  BranchPickerPrimitive,
1407
1526
  ComposerPrimitive,
1527
+ ContentPartPrimitive,
1408
1528
  MessagePrimitive,
1409
1529
  ThreadPrimitive,
1410
1530
  VercelAIAssistantProvider,