@assistant-ui/react 0.0.14 → 0.0.16

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
@@ -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,