@assistant-ui/react 0.0.20 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.mjs CHANGED
@@ -4,6 +4,156 @@ var __export = (target, all) => {
4
4
  __defProp(target, name, { get: all[name], enumerable: true });
5
5
  };
6
6
 
7
+ // src/actions/useCopyMessage.tsx
8
+ import { useCallback } from "react";
9
+
10
+ // src/context/MessageContext.ts
11
+ import { createContext, useContext } from "react";
12
+ var MessageContext = createContext(null);
13
+ var useMessageContext = () => {
14
+ const context = useContext(MessageContext);
15
+ if (!context)
16
+ throw new Error(
17
+ "This component can only be used inside a component passed to <ThreadPrimitive.Messages components={...} />."
18
+ );
19
+ return context;
20
+ };
21
+
22
+ // src/utils/combined/useCombinedStore.ts
23
+ import { useMemo } from "react";
24
+
25
+ // src/utils/combined/createCombinedStore.ts
26
+ import { useSyncExternalStore } from "react";
27
+ var createCombinedStore = (stores) => {
28
+ const subscribe = (callback) => {
29
+ const unsubscribes = stores.map((store) => store.subscribe(callback));
30
+ return () => {
31
+ for (const unsub of unsubscribes) {
32
+ unsub();
33
+ }
34
+ };
35
+ };
36
+ return (selector) => {
37
+ const getSnapshot = () => selector(...stores.map((store) => store.getState()));
38
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
39
+ };
40
+ };
41
+
42
+ // src/utils/combined/useCombinedStore.ts
43
+ var useCombinedStore = (stores, selector) => {
44
+ const useCombined = useMemo(() => createCombinedStore(stores), stores);
45
+ return useCombined(selector);
46
+ };
47
+
48
+ // src/utils/getMessageText.tsx
49
+ var getMessageText = (message) => {
50
+ const textParts = message.content.filter(
51
+ (part) => part.type === "text"
52
+ );
53
+ return textParts.map((part) => part.text).join("\n\n");
54
+ };
55
+
56
+ // src/actions/useCopyMessage.tsx
57
+ var useCopyMessage = ({ copiedDuration = 3e3 }) => {
58
+ const { useMessage, useComposer } = useMessageContext();
59
+ const hasCopyableContent = useCombinedStore(
60
+ [useMessage, useComposer],
61
+ (m, c) => {
62
+ return c.isEditing || m.message.content.some((c2) => c2.type === "text");
63
+ }
64
+ );
65
+ const callback = useCallback(() => {
66
+ const { isEditing, value: composerValue } = useComposer.getState();
67
+ const { message, setIsCopied } = useMessage.getState();
68
+ const valueToCopy = isEditing ? composerValue : getMessageText(message);
69
+ navigator.clipboard.writeText(valueToCopy);
70
+ setIsCopied(true);
71
+ setTimeout(() => setIsCopied(false), copiedDuration);
72
+ }, [useComposer, useMessage, copiedDuration]);
73
+ if (!hasCopyableContent) return null;
74
+ return callback;
75
+ };
76
+
77
+ // src/actions/useReloadMessage.tsx
78
+ import { useCallback as useCallback2 } from "react";
79
+
80
+ // src/context/ThreadContext.ts
81
+ import { createContext as createContext2, useContext as useContext2 } from "react";
82
+ var ThreadContext = createContext2(null);
83
+ var useThreadContext = () => {
84
+ const context = useContext2(ThreadContext);
85
+ if (!context)
86
+ throw new Error("This component must be used within an AssistantProvider.");
87
+ return context;
88
+ };
89
+
90
+ // src/actions/useReloadMessage.tsx
91
+ var useReloadMessage = () => {
92
+ const { useThread, useViewport } = useThreadContext();
93
+ const { useMessage } = useMessageContext();
94
+ const disabled = useCombinedStore(
95
+ [useThread, useMessage],
96
+ (t, m) => t.isRunning || m.message.role !== "assistant"
97
+ );
98
+ const callback = useCallback2(() => {
99
+ const { parentId } = useMessage.getState();
100
+ useThread.getState().startRun(parentId);
101
+ useViewport.getState().scrollToBottom();
102
+ }, [useMessage, useThread, useViewport]);
103
+ if (disabled) return null;
104
+ return callback;
105
+ };
106
+
107
+ // src/actions/useBeginMessageEdit.tsx
108
+ import { useCallback as useCallback3 } from "react";
109
+ var useBeginMessageEdit = () => {
110
+ const { useMessage, useComposer } = useMessageContext();
111
+ const disabled = useCombinedStore(
112
+ [useMessage, useComposer],
113
+ (m, c) => m.message.role !== "user" || c.isEditing
114
+ );
115
+ const callback = useCallback3(() => {
116
+ const { edit } = useComposer.getState();
117
+ edit();
118
+ }, [useComposer]);
119
+ if (disabled) return null;
120
+ return callback;
121
+ };
122
+
123
+ // src/actions/useGoToNextBranch.tsx
124
+ import { useCallback as useCallback4 } from "react";
125
+ var useGoToNextBranch = () => {
126
+ const { useThread } = useThreadContext();
127
+ const { useMessage, useComposer } = useMessageContext();
128
+ const disabled = useCombinedStore(
129
+ [useMessage, useComposer],
130
+ (m, c) => c.isEditing || m.branches.indexOf(m.message.id) + 1 >= m.branches.length
131
+ );
132
+ const callback = useCallback4(() => {
133
+ const { message, branches } = useMessage.getState();
134
+ useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
135
+ }, [useMessage, useThread]);
136
+ if (disabled) return null;
137
+ return callback;
138
+ };
139
+
140
+ // src/actions/useGoToPreviousBranch.tsx
141
+ import { useCallback as useCallback5 } from "react";
142
+ var useGoToPreviousBranch = () => {
143
+ const { useThread } = useThreadContext();
144
+ const { useMessage, useComposer } = useMessageContext();
145
+ const disabled = useCombinedStore(
146
+ [useMessage, useComposer],
147
+ (m, c) => c.isEditing || m.branches.indexOf(m.message.id) <= 0
148
+ );
149
+ const callback = useCallback5(() => {
150
+ const { message, branches } = useMessage.getState();
151
+ useThread.getState().switchToBranch(branches[branches.indexOf(message.id) - 1]);
152
+ }, [useMessage, useThread]);
153
+ if (disabled) return null;
154
+ return callback;
155
+ };
156
+
7
157
  // src/primitives/thread/index.ts
8
158
  var thread_exports = {};
9
159
  __export(thread_exports, {
@@ -28,21 +178,9 @@ var ThreadRoot = forwardRef(
28
178
  }
29
179
  );
30
180
 
31
- // src/utils/context/AssistantContext.ts
32
- import { createContext, useContext } from "react";
33
- var AssistantContext = createContext(null);
34
- var useAssistantContext = () => {
35
- const context = useContext(AssistantContext);
36
- if (!context)
37
- throw new Error(
38
- "This component must be used within a AssistantProvider."
39
- );
40
- return context;
41
- };
42
-
43
181
  // src/primitives/thread/ThreadIf.tsx
44
182
  var useThreadIf = (props) => {
45
- const { useThread } = useAssistantContext();
183
+ const { useThread } = useThreadContext();
46
184
  return useThread((thread) => {
47
185
  if (props.empty === true && thread.messages.length !== 0) return false;
48
186
  if (props.empty === false && thread.messages.length === 0) return false;
@@ -113,7 +251,7 @@ import { useCallbackRef as useCallbackRef2 } from "@radix-ui/react-use-callback-
113
251
  import { useEffect as useEffect2 } from "react";
114
252
  var useOnScrollToBottom = (callback) => {
115
253
  const callbackRef = useCallbackRef2(callback);
116
- const { useViewport } = useAssistantContext();
254
+ const { useViewport } = useThreadContext();
117
255
  useEffect2(() => {
118
256
  return useViewport.getState().onScrollToBottom(() => {
119
257
  callbackRef();
@@ -127,7 +265,7 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
127
265
  const messagesEndRef = useRef(null);
128
266
  const divRef = useRef(null);
129
267
  const ref = useComposedRefs(forwardedRef, divRef);
130
- const { useViewport } = useAssistantContext();
268
+ const { useViewport } = useThreadContext();
131
269
  const firstRenderRef = useRef(true);
132
270
  const isScrollingToBottomRef = useRef(false);
133
271
  const lastScrollTop = useRef(0);
@@ -175,78 +313,23 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
175
313
  );
176
314
  });
177
315
 
178
- // src/utils/context/useComposerContext.ts
179
- import { useContext as useContext3 } from "react";
180
-
181
- // src/utils/context/useMessageContext.ts
182
- import { createContext as createContext2, useContext as useContext2 } from "react";
183
- var MessageContext = createContext2(null);
184
- var useMessageContext = () => {
185
- const context = useContext2(MessageContext);
186
- if (!context)
187
- throw new Error(
188
- "This component must be used within a MessagePrimitive.Provider."
189
- );
190
- return context;
191
- };
192
-
193
- // src/utils/context/useComposerContext.ts
194
- var useComposerContext = () => {
195
- const { useComposer: useAssisstantComposer } = useAssistantContext();
196
- const { useComposer: useMessageComposer } = useContext3(MessageContext) ?? {};
197
- return {
198
- useComposer: useMessageComposer ?? useAssisstantComposer,
199
- type: useMessageComposer ? "message" : "assistant"
200
- };
201
- };
202
-
203
- // src/primitives/composer/ComposerIf.tsx
204
- var useComposerIf = (props) => {
205
- const { useComposer } = useComposerContext();
206
- return useComposer((composer) => {
207
- if (props.editing === true && !composer.isEditing) return false;
208
- if (props.editing === false && composer.isEditing) return false;
209
- return true;
210
- });
211
- };
212
- var ComposerIf = ({ children, ...query }) => {
213
- const result = useComposerIf(query);
214
- return result ? children : null;
215
- };
216
-
217
- // src/primitives/message/index.ts
218
- var message_exports = {};
219
- __export(message_exports, {
220
- Content: () => MessageContent,
221
- If: () => MessageIf,
222
- InProgress: () => MessageInProgress,
223
- Provider: () => MessageProvider,
224
- Root: () => MessageRoot
225
- });
226
-
227
- // src/primitives/message/MessageProvider.tsx
228
- import { useMemo, useState } from "react";
316
+ // src/context/providers/MessageProvider.tsx
317
+ import { useEffect as useEffect3, useState } from "react";
229
318
  import { create as create2 } from "zustand";
230
319
 
231
- // src/utils/context/getMessageText.tsx
232
- var getMessageText = (message) => {
233
- const textParts = message.content.filter(
234
- (part) => part.type === "text"
235
- );
236
- return textParts.map((part) => part.text).join("\n\n");
237
- };
320
+ // src/context/stores/MessageComposer.ts
321
+ import { create } from "zustand";
238
322
 
239
- // src/utils/context/stores/ComposerStore.ts
240
- import {
241
- create
242
- } from "zustand";
323
+ // src/context/stores/BaseComposer.ts
243
324
  var makeBaseComposer = (set) => ({
244
325
  value: "",
245
326
  setValue: (value) => {
246
327
  set({ value });
247
328
  }
248
329
  });
249
- var makeMessageComposerStore = ({
330
+
331
+ // src/context/stores/MessageComposer.ts
332
+ var makeEditComposerStore = ({
250
333
  onEdit,
251
334
  onSend
252
335
  }) => create()((set, get, store) => ({
@@ -267,34 +350,30 @@ var makeMessageComposerStore = ({
267
350
  return true;
268
351
  }
269
352
  }));
270
- var makeThreadComposerStore = (useThread) => create()((set, get, store) => {
271
- return {
272
- ...makeBaseComposer(set, get, store),
273
- isEditing: true,
274
- send: () => {
275
- const { value } = get();
276
- set({ value: "" });
277
- useThread.getState().append({
278
- parentId: useThread.getState().messages.at(-1)?.id ?? null,
279
- content: [{ type: "text", text: value }]
280
- });
281
- },
282
- cancel: () => {
283
- const thread = useThread.getState();
284
- if (!thread.isRunning) return false;
285
- useThread.getState().cancelRun();
286
- return true;
287
- }
288
- };
289
- });
290
353
 
291
- // src/primitives/message/MessageProvider.tsx
354
+ // src/context/providers/MessageProvider.tsx
292
355
  import { jsx as jsx4 } from "react/jsx-runtime";
293
356
  var getIsLast = (thread, message) => {
294
357
  return thread.messages[thread.messages.length - 1]?.id === message.id;
295
358
  };
296
- var useMessageContext2 = () => {
297
- const { useThread } = useAssistantContext();
359
+ var syncMessage = (thread, useMessage, messageIndex) => {
360
+ const parentId = thread.messages[messageIndex - 1]?.id ?? null;
361
+ const message = thread.messages[messageIndex];
362
+ if (!message) return;
363
+ const isLast = getIsLast(thread, message);
364
+ const branches = thread.getBranches(message.id);
365
+ const currentState = useMessage.getState();
366
+ if (currentState.message === message && currentState.parentId === parentId && currentState.branches === branches && currentState.isLast === isLast)
367
+ return;
368
+ useMessage.setState({
369
+ message,
370
+ parentId,
371
+ branches,
372
+ isLast
373
+ });
374
+ };
375
+ var useMessageContext2 = (messageIndex) => {
376
+ const { useThread } = useThreadContext();
298
377
  const [context] = useState(() => {
299
378
  const useMessage = create2((set) => ({
300
379
  message: null,
@@ -314,7 +393,7 @@ var useMessageContext2 = () => {
314
393
  set({ isHovering: value });
315
394
  }
316
395
  }));
317
- const useComposer = makeMessageComposerStore({
396
+ const useComposer = makeEditComposerStore({
318
397
  onEdit: () => {
319
398
  const message = useMessage.getState().message;
320
399
  if (message.role !== "user")
@@ -339,58 +418,51 @@ var useMessageContext2 = () => {
339
418
  });
340
419
  }
341
420
  });
421
+ syncMessage(useThread.getState(), useMessage, messageIndex);
342
422
  return { useMessage, useComposer };
343
423
  });
424
+ useEffect3(() => {
425
+ return useThread.subscribe((thread) => {
426
+ syncMessage(thread, context.useMessage, messageIndex);
427
+ });
428
+ }, [context, useThread, messageIndex]);
344
429
  return context;
345
430
  };
346
431
  var MessageProvider = ({
347
- message,
348
- parentId,
432
+ messageIndex,
349
433
  children
350
434
  }) => {
351
- const { useThread } = useAssistantContext();
352
- const context = useMessageContext2();
353
- const isLast = useThread((thread) => getIsLast(thread, message));
354
- const branches = useThread((thread) => thread.getBranches(message.id));
355
- useMemo(() => {
356
- context.useMessage.setState({
357
- message,
358
- parentId,
359
- branches,
360
- isLast
361
- });
362
- }, [context, message, parentId, branches, isLast]);
435
+ const context = useMessageContext2(messageIndex);
363
436
  return /* @__PURE__ */ jsx4(MessageContext.Provider, { value: context, children });
364
437
  };
365
438
 
366
- // src/primitives/message/MessageRoot.tsx
367
- import { composeEventHandlers as composeEventHandlers2 } from "@radix-ui/primitive";
368
- import {
369
- Primitive as Primitive3
370
- } from "@radix-ui/react-primitive";
371
- import { forwardRef as forwardRef3 } from "react";
372
- import { jsx as jsx5 } from "react/jsx-runtime";
373
- var MessageRoot = forwardRef3(
374
- ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
375
- const { useMessage } = useMessageContext();
376
- const setIsHovering = useMessage((s) => s.setIsHovering);
377
- const handleMouseEnter = () => {
378
- setIsHovering(true);
379
- };
380
- const handleMouseLeave = () => {
381
- setIsHovering(false);
382
- };
383
- return /* @__PURE__ */ jsx5(
384
- Primitive3.div,
385
- {
386
- ...rest,
387
- ref,
388
- onMouseEnter: composeEventHandlers2(onMouseEnter, handleMouseEnter),
389
- onMouseLeave: composeEventHandlers2(onMouseLeave, handleMouseLeave)
390
- }
391
- );
392
- }
393
- );
439
+ // src/context/ComposerContext.ts
440
+ import { useContext as useContext3, useMemo as useMemo2 } from "react";
441
+ var useComposerContext = () => {
442
+ const { useComposer } = useThreadContext();
443
+ const { useComposer: useEditComposer } = useContext3(MessageContext) ?? {};
444
+ return useMemo2(
445
+ () => ({
446
+ useComposer: useEditComposer ?? useComposer,
447
+ type: useEditComposer ? "edit" : "new"
448
+ }),
449
+ [useEditComposer, useComposer]
450
+ );
451
+ };
452
+
453
+ // src/primitives/composer/ComposerIf.tsx
454
+ var useComposerIf = (props) => {
455
+ const { useComposer } = useComposerContext();
456
+ return useComposer((composer) => {
457
+ if (props.editing === true && !composer.isEditing) return false;
458
+ if (props.editing === false && composer.isEditing) return false;
459
+ return true;
460
+ });
461
+ };
462
+ var ComposerIf = ({ children, ...query }) => {
463
+ const result = useComposerIf(query);
464
+ return result ? children : null;
465
+ };
394
466
 
395
467
  // src/primitives/message/MessageIf.tsx
396
468
  var useMessageIf = (props) => {
@@ -410,162 +482,8 @@ var MessageIf = ({ children, ...query }) => {
410
482
  return result ? children : null;
411
483
  };
412
484
 
413
- // src/utils/context/combined/useCombinedStore.ts
414
- import { useMemo as useMemo2 } from "react";
415
-
416
- // src/utils/context/combined/createCombinedStore.ts
417
- import { useSyncExternalStore } from "react";
418
- var createCombinedStore = (stores) => {
419
- const subscribe = (callback) => {
420
- const unsubscribes = stores.map((store) => store.subscribe(callback));
421
- return () => {
422
- for (const unsub of unsubscribes) {
423
- unsub();
424
- }
425
- };
426
- };
427
- return (selector) => {
428
- const getSnapshot = () => selector(...stores.map((store) => store.getState()));
429
- return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
430
- };
431
- };
432
-
433
- // src/utils/context/combined/useCombinedStore.ts
434
- var useCombinedStore = (stores, selector) => {
435
- const useCombined = useMemo2(() => createCombinedStore(stores), stores);
436
- return useCombined(selector);
437
- };
438
-
439
- // src/utils/context/useContentPartContext.ts
440
- import { createContext as createContext3, useContext as useContext4 } from "react";
441
- var ContentPartContext = createContext3(null);
442
- var useContentPartContext = () => {
443
- const context = useContext4(ContentPartContext);
444
- if (!context)
445
- throw new Error(
446
- "This component must be used within a ContentPartPrimitive.Provider."
447
- );
448
- return context;
449
- };
450
-
451
- // src/primitives/contentPart/ContentPartInProgressIndicator.tsx
452
- var ContentPartInProgressIndicator = () => {
453
- const { useMessage } = useMessageContext();
454
- const { useContentPart } = useContentPartContext();
455
- const indicator = useCombinedStore(
456
- [useMessage, useContentPart],
457
- (m, c) => c.status === "in_progress" ? m.inProgressIndicator : null
458
- );
459
- return indicator;
460
- };
461
-
462
- // src/primitives/contentPart/ContentPartProvider.tsx
463
- import { useMemo as useMemo3, useState as useState2 } from "react";
464
- import { create as create3 } from "zustand";
465
- import { jsx as jsx6 } from "react/jsx-runtime";
466
- var useContentPartContext2 = () => {
467
- const [context] = useState2(() => {
468
- const useContentPart = create3(() => ({
469
- part: null,
470
- status: "done"
471
- }));
472
- return { useContentPart };
473
- });
474
- return context;
475
- };
476
- var ContentPartProvider = ({
477
- part,
478
- status,
479
- children
480
- }) => {
481
- const context = useContentPartContext2();
482
- useMemo3(() => {
483
- context.useContentPart.setState(
484
- {
485
- part,
486
- status
487
- },
488
- true
489
- );
490
- }, [context, part, status]);
491
- return /* @__PURE__ */ jsx6(ContentPartContext.Provider, { value: context, children });
492
- };
493
-
494
- // src/primitives/message/MessageContent.tsx
495
- import { Fragment, jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
496
- var defaultComponents = {
497
- Text: ({ part }) => /* @__PURE__ */ jsxs2(Fragment, { children: [
498
- part.text,
499
- /* @__PURE__ */ jsx7(ContentPartInProgressIndicator, {})
500
- ] }),
501
- Image: () => null,
502
- UI: ({ part }) => part.display,
503
- tools: {
504
- Fallback: () => null
505
- }
506
- };
507
- var MessageContent = ({
508
- components: {
509
- Text = defaultComponents.Text,
510
- Image = defaultComponents.Image,
511
- UI = defaultComponents.UI,
512
- tools: { by_name = {}, Fallback = defaultComponents.tools.Fallback } = {}
513
- } = {}
514
- }) => {
515
- const { useMessage } = useMessageContext();
516
- const message = useMessage((s) => s.message);
517
- const content = message.content;
518
- const status = message.role === "assistant" ? message.status : "done";
519
- return /* @__PURE__ */ jsx7(Fragment, { children: content.map((part, i) => {
520
- const key = i;
521
- const type = part.type;
522
- let component = null;
523
- switch (type) {
524
- case "text":
525
- component = /* @__PURE__ */ jsx7(Text, { part });
526
- break;
527
- case "image":
528
- component = /* @__PURE__ */ jsx7(Image, { part });
529
- break;
530
- case "ui":
531
- component = /* @__PURE__ */ jsx7(UI, { part });
532
- break;
533
- case "tool-call": {
534
- const Tool = by_name[part.name] || Fallback;
535
- component = /* @__PURE__ */ jsx7(Tool, { part });
536
- break;
537
- }
538
- default:
539
- throw new Error(`Unknown content part type: ${type}`);
540
- }
541
- return /* @__PURE__ */ jsx7(
542
- ContentPartProvider,
543
- {
544
- part,
545
- status: i === content.length - 1 ? status : "done",
546
- children: component
547
- },
548
- key
549
- );
550
- }) });
551
- };
552
-
553
- // src/primitives/message/MessageInProgress.tsx
554
- import {
555
- Primitive as Primitive4
556
- } from "@radix-ui/react-primitive";
557
- import { forwardRef as forwardRef4, useMemo as useMemo4 } from "react";
558
- import { jsx as jsx8 } from "react/jsx-runtime";
559
- var MessageInProgress = forwardRef4((props, ref) => {
560
- const { useMessage } = useMessageContext();
561
- useMemo4(() => {
562
- useMessage.getState().setInProgressIndicator(/* @__PURE__ */ jsx8(Primitive4.div, { ...props, ref }));
563
- }, [useMessage, props, ref]);
564
- return null;
565
- });
566
-
567
485
  // src/primitives/thread/ThreadMessages.tsx
568
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
486
+ import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
569
487
  var getComponents = (components) => {
570
488
  return {
571
489
  EditComposer: components.EditComposer ?? components.UserMessage ?? components.Message,
@@ -574,64 +492,62 @@ var getComponents = (components) => {
574
492
  };
575
493
  };
576
494
  var ThreadMessages = ({ components }) => {
577
- const { useThread } = useAssistantContext();
578
- const thread = useThread();
579
- const messages = thread.messages;
495
+ const { useThread } = useThreadContext();
496
+ const messagesLength = useThread((t) => t.messages.length);
580
497
  const { UserMessage, EditComposer, AssistantMessage } = getComponents(components);
581
- if (messages.length === 0) return null;
582
- return /* @__PURE__ */ jsx9(Fragment2, { children: messages.map((message, idx) => {
583
- const parentId = messages[idx - 1]?.id ?? null;
584
- return /* @__PURE__ */ jsxs3(
498
+ if (messagesLength === 0) return null;
499
+ return /* @__PURE__ */ jsx5(Fragment, { children: new Array(messagesLength).fill(null).map((_, idx) => {
500
+ const messageIndex = idx;
501
+ return /* @__PURE__ */ jsxs2(
585
502
  MessageProvider,
586
503
  {
587
- message,
588
- parentId,
504
+ messageIndex,
589
505
  children: [
590
- /* @__PURE__ */ jsxs3(MessageIf, { user: true, children: [
591
- /* @__PURE__ */ jsx9(ComposerIf, { editing: false, children: /* @__PURE__ */ jsx9(UserMessage, {}) }),
592
- /* @__PURE__ */ jsx9(ComposerIf, { editing: true, children: /* @__PURE__ */ jsx9(EditComposer, {}) })
506
+ /* @__PURE__ */ jsxs2(MessageIf, { user: true, children: [
507
+ /* @__PURE__ */ jsx5(ComposerIf, { editing: false, children: /* @__PURE__ */ jsx5(UserMessage, {}) }),
508
+ /* @__PURE__ */ jsx5(ComposerIf, { editing: true, children: /* @__PURE__ */ jsx5(EditComposer, {}) })
593
509
  ] }),
594
- /* @__PURE__ */ jsx9(MessageIf, { assistant: true, children: /* @__PURE__ */ jsx9(AssistantMessage, {}) })
510
+ /* @__PURE__ */ jsx5(MessageIf, { assistant: true, children: /* @__PURE__ */ jsx5(AssistantMessage, {}) })
595
511
  ]
596
512
  },
597
- parentId ?? "__ROOT__"
513
+ messageIndex
598
514
  );
599
515
  }) });
600
516
  };
601
517
 
602
518
  // src/primitives/thread/ThreadScrollToBottom.tsx
603
- import { composeEventHandlers as composeEventHandlers3 } from "@radix-ui/primitive";
519
+ import { composeEventHandlers as composeEventHandlers2 } from "@radix-ui/primitive";
604
520
  import {
605
- Primitive as Primitive5
521
+ Primitive as Primitive3
606
522
  } from "@radix-ui/react-primitive";
607
- import { forwardRef as forwardRef5 } from "react";
608
- import { jsx as jsx10 } from "react/jsx-runtime";
609
- var ThreadScrollToBottom = forwardRef5(({ onClick, ...rest }, ref) => {
610
- const { useViewport } = useAssistantContext();
523
+ import { forwardRef as forwardRef3 } from "react";
524
+ import { jsx as jsx6 } from "react/jsx-runtime";
525
+ var ThreadScrollToBottom = forwardRef3(({ onClick, ...rest }, ref) => {
526
+ const { useViewport } = useThreadContext();
611
527
  const isAtBottom = useViewport((s) => s.isAtBottom);
612
528
  const handleScrollToBottom = () => {
613
529
  useViewport.getState().scrollToBottom();
614
530
  };
615
- return /* @__PURE__ */ jsx10(
616
- Primitive5.button,
531
+ return /* @__PURE__ */ jsx6(
532
+ Primitive3.button,
617
533
  {
618
534
  ...rest,
619
535
  disabled: isAtBottom,
620
536
  ref,
621
- onClick: composeEventHandlers3(onClick, handleScrollToBottom)
537
+ onClick: composeEventHandlers2(onClick, handleScrollToBottom)
622
538
  }
623
539
  );
624
540
  });
625
541
 
626
542
  // src/primitives/thread/ThreadSuggestion.tsx
627
- import { composeEventHandlers as composeEventHandlers4 } from "@radix-ui/primitive";
543
+ import { composeEventHandlers as composeEventHandlers3 } from "@radix-ui/primitive";
628
544
  import {
629
- Primitive as Primitive6
545
+ Primitive as Primitive4
630
546
  } from "@radix-ui/react-primitive";
631
- import { forwardRef as forwardRef6 } from "react";
632
- import { jsx as jsx11 } from "react/jsx-runtime";
633
- var ThreadSuggestion = forwardRef6(({ onClick, prompt, method, autoSend: send, ...rest }, ref) => {
634
- const { useThread, useComposer } = useAssistantContext();
547
+ import { forwardRef as forwardRef4 } from "react";
548
+ import { jsx as jsx7 } from "react/jsx-runtime";
549
+ var ThreadSuggestion = forwardRef4(({ onClick, prompt, method, autoSend: send, ...rest }, ref) => {
550
+ const { useThread, useComposer } = useThreadContext();
635
551
  const isDisabled = useThread((t) => t.isRunning);
636
552
  const handleApplySuggestion = () => {
637
553
  const thread = useThread.getState();
@@ -641,13 +557,13 @@ var ThreadSuggestion = forwardRef6(({ onClick, prompt, method, autoSend: send, .
641
557
  composer.send();
642
558
  }
643
559
  };
644
- return /* @__PURE__ */ jsx11(
645
- Primitive6.button,
560
+ return /* @__PURE__ */ jsx7(
561
+ Primitive4.button,
646
562
  {
647
563
  ...rest,
648
564
  disabled: isDisabled,
649
565
  ref,
650
- onClick: composeEventHandlers4(onClick, handleApplySuggestion)
566
+ onClick: composeEventHandlers3(onClick, handleApplySuggestion)
651
567
  }
652
568
  );
653
569
  });
@@ -663,16 +579,16 @@ __export(composer_exports, {
663
579
  });
664
580
 
665
581
  // src/primitives/composer/ComposerRoot.tsx
666
- import { composeEventHandlers as composeEventHandlers5 } from "@radix-ui/primitive";
582
+ import { composeEventHandlers as composeEventHandlers4 } from "@radix-ui/primitive";
667
583
  import { useComposedRefs as useComposedRefs2 } from "@radix-ui/react-compose-refs";
668
584
  import {
669
- Primitive as Primitive7
585
+ Primitive as Primitive5
670
586
  } from "@radix-ui/react-primitive";
671
- import { forwardRef as forwardRef7, useRef as useRef2 } from "react";
672
- import { jsx as jsx12 } from "react/jsx-runtime";
673
- var ComposerRoot = forwardRef7(
587
+ import { forwardRef as forwardRef5, useRef as useRef2 } from "react";
588
+ import { jsx as jsx8 } from "react/jsx-runtime";
589
+ var ComposerRoot = forwardRef5(
674
590
  ({ onSubmit, ...rest }, forwardedRef) => {
675
- const { useViewport } = useAssistantContext();
591
+ const { useViewport } = useThreadContext();
676
592
  const { useComposer } = useComposerContext();
677
593
  const formRef = useRef2(null);
678
594
  const ref = useComposedRefs2(forwardedRef, formRef);
@@ -683,32 +599,32 @@ var ComposerRoot = forwardRef7(
683
599
  composerState.send();
684
600
  useViewport.getState().scrollToBottom();
685
601
  };
686
- return /* @__PURE__ */ jsx12(
687
- Primitive7.form,
602
+ return /* @__PURE__ */ jsx8(
603
+ Primitive5.form,
688
604
  {
689
605
  ...rest,
690
606
  ref,
691
- onSubmit: composeEventHandlers5(onSubmit, handleSubmit)
607
+ onSubmit: composeEventHandlers4(onSubmit, handleSubmit)
692
608
  }
693
609
  );
694
610
  }
695
611
  );
696
612
 
697
613
  // src/primitives/composer/ComposerInput.tsx
698
- import { composeEventHandlers as composeEventHandlers6 } from "@radix-ui/primitive";
614
+ import { composeEventHandlers as composeEventHandlers5 } from "@radix-ui/primitive";
699
615
  import { useComposedRefs as useComposedRefs3 } from "@radix-ui/react-compose-refs";
700
616
  import { Slot } from "@radix-ui/react-slot";
701
617
  import {
702
- forwardRef as forwardRef8,
703
- useCallback,
704
- useEffect as useEffect3,
618
+ forwardRef as forwardRef6,
619
+ useCallback as useCallback6,
620
+ useEffect as useEffect4,
705
621
  useRef as useRef3
706
622
  } from "react";
707
623
  import TextareaAutosize from "react-textarea-autosize";
708
- import { jsx as jsx13 } from "react/jsx-runtime";
709
- var ComposerInput = forwardRef8(
624
+ import { jsx as jsx9 } from "react/jsx-runtime";
625
+ var ComposerInput = forwardRef6(
710
626
  ({ autoFocus = false, asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
711
- const { useThread, useViewport } = useAssistantContext();
627
+ const { useThread, useViewport } = useThreadContext();
712
628
  const { useComposer, type } = useComposerContext();
713
629
  const value = useComposer((c) => {
714
630
  if (!c.isEditing) return "";
@@ -734,7 +650,7 @@ var ComposerInput = forwardRef8(
734
650
  const textareaRef = useRef3(null);
735
651
  const ref = useComposedRefs3(forwardedRef, textareaRef);
736
652
  const autoFocusEnabled = autoFocus && !disabled;
737
- const focus = useCallback(() => {
653
+ const focus = useCallback6(() => {
738
654
  const textarea = textareaRef.current;
739
655
  if (!textarea || !autoFocusEnabled) return;
740
656
  textarea.focus();
@@ -743,25 +659,25 @@ var ComposerInput = forwardRef8(
743
659
  textareaRef.current.value.length
744
660
  );
745
661
  }, [autoFocusEnabled]);
746
- useEffect3(() => focus(), [focus]);
662
+ useEffect4(() => focus(), [focus]);
747
663
  useOnScrollToBottom(() => {
748
- if (type === "assistant") {
664
+ if (type === "new") {
749
665
  focus();
750
666
  }
751
667
  });
752
- return /* @__PURE__ */ jsx13(
668
+ return /* @__PURE__ */ jsx9(
753
669
  Component,
754
670
  {
755
671
  value,
756
672
  ...rest,
757
673
  ref,
758
674
  disabled,
759
- onChange: composeEventHandlers6(onChange, (e) => {
675
+ onChange: composeEventHandlers5(onChange, (e) => {
760
676
  const composerState = useComposer.getState();
761
677
  if (!composerState.isEditing) return;
762
678
  return composerState.setValue(e.target.value);
763
679
  }),
764
- onKeyDown: composeEventHandlers6(onKeyDown, handleKeyPress)
680
+ onKeyDown: composeEventHandlers5(onKeyDown, handleKeyPress)
765
681
  }
766
682
  );
767
683
  }
@@ -769,16 +685,16 @@ var ComposerInput = forwardRef8(
769
685
 
770
686
  // src/primitives/composer/ComposerSend.tsx
771
687
  import {
772
- Primitive as Primitive8
688
+ Primitive as Primitive6
773
689
  } from "@radix-ui/react-primitive";
774
- import { forwardRef as forwardRef9 } from "react";
775
- import { jsx as jsx14 } from "react/jsx-runtime";
776
- var ComposerSend = forwardRef9(
690
+ import { forwardRef as forwardRef7 } from "react";
691
+ import { jsx as jsx10 } from "react/jsx-runtime";
692
+ var ComposerSend = forwardRef7(
777
693
  ({ disabled, ...rest }, ref) => {
778
694
  const { useComposer } = useComposerContext();
779
695
  const hasValue = useComposer((c) => c.isEditing && c.value.length > 0);
780
- return /* @__PURE__ */ jsx14(
781
- Primitive8.button,
696
+ return /* @__PURE__ */ jsx10(
697
+ Primitive6.button,
782
698
  {
783
699
  type: "submit",
784
700
  ...rest,
@@ -790,122 +706,253 @@ var ComposerSend = forwardRef9(
790
706
  );
791
707
 
792
708
  // src/primitives/composer/ComposerCancel.tsx
793
- import { composeEventHandlers as composeEventHandlers7 } from "@radix-ui/primitive";
709
+ import { composeEventHandlers as composeEventHandlers6 } from "@radix-ui/primitive";
794
710
  import {
795
- Primitive as Primitive9
711
+ Primitive as Primitive7
796
712
  } from "@radix-ui/react-primitive";
797
- import { forwardRef as forwardRef10 } from "react";
798
- import { jsx as jsx15 } from "react/jsx-runtime";
799
- var ComposerCancel = forwardRef10(({ onClick, ...rest }, ref) => {
713
+ import { forwardRef as forwardRef8 } from "react";
714
+ import { jsx as jsx11 } from "react/jsx-runtime";
715
+ var ComposerCancel = forwardRef8(({ onClick, ...rest }, ref) => {
800
716
  const { useComposer } = useComposerContext();
801
717
  const handleCancel = () => {
802
718
  useComposer.getState().cancel();
803
719
  };
804
- return /* @__PURE__ */ jsx15(
805
- Primitive9.button,
720
+ return /* @__PURE__ */ jsx11(
721
+ Primitive7.button,
806
722
  {
807
723
  type: "button",
808
724
  ...rest,
809
725
  ref,
810
- onClick: composeEventHandlers7(onClick, handleCancel)
726
+ onClick: composeEventHandlers6(onClick, handleCancel)
811
727
  }
812
728
  );
813
729
  });
814
730
 
815
- // src/primitives/branchPicker/index.ts
816
- var branchPicker_exports = {};
817
- __export(branchPicker_exports, {
818
- Count: () => BranchPickerCount,
819
- Next: () => BranchPickerNext,
820
- Number: () => BranchPickerNumber,
821
- Previous: () => BranchPickerPrevious,
822
- Root: () => BranchPickerRoot
731
+ // src/primitives/message/index.ts
732
+ var message_exports = {};
733
+ __export(message_exports, {
734
+ Content: () => MessageContent,
735
+ If: () => MessageIf,
736
+ InProgress: () => MessageInProgress,
737
+ Root: () => MessageRoot
823
738
  });
824
739
 
825
- // src/actions/useGoToNextBranch.tsx
826
- import { useCallback as useCallback2 } from "react";
827
- var useGoToNextBranch = () => {
828
- const { useThread } = useAssistantContext();
829
- const { useMessage, useComposer } = useMessageContext();
830
- const disabled = useCombinedStore(
831
- [useMessage, useComposer],
832
- (m, c) => c.isEditing || m.branches.indexOf(m.message.id) + 1 >= m.branches.length
833
- );
834
- const callback = useCallback2(() => {
835
- const { message, branches } = useMessage.getState();
836
- useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
837
- }, [useMessage, useThread]);
838
- if (disabled) return null;
839
- return callback;
840
- };
841
-
842
- // src/utils/createActionButton.tsx
843
- import { composeEventHandlers as composeEventHandlers8 } from "@radix-ui/primitive";
740
+ // src/primitives/message/MessageRoot.tsx
741
+ import { composeEventHandlers as composeEventHandlers7 } from "@radix-ui/primitive";
844
742
  import {
845
- Primitive as Primitive10
743
+ Primitive as Primitive8
846
744
  } from "@radix-ui/react-primitive";
847
- import { forwardRef as forwardRef11 } from "react";
848
- import { jsx as jsx16 } from "react/jsx-runtime";
849
- var createActionButton = (useActionButton) => {
850
- return forwardRef11(
851
- (props, forwardedRef) => {
852
- const onClick = useActionButton(props);
853
- return /* @__PURE__ */ jsx16(
854
- Primitive10.button,
855
- {
856
- type: "button",
857
- disabled: !onClick,
858
- ...props,
859
- ref: forwardedRef,
860
- onClick: composeEventHandlers8(props.onClick, onClick ?? void 0)
861
- }
862
- );
863
- }
864
- );
865
- };
745
+ import { forwardRef as forwardRef9 } from "react";
746
+ import { jsx as jsx12 } from "react/jsx-runtime";
747
+ var MessageRoot = forwardRef9(
748
+ ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
749
+ const { useMessage } = useMessageContext();
750
+ const setIsHovering = useMessage((s) => s.setIsHovering);
751
+ const handleMouseEnter = () => {
752
+ setIsHovering(true);
753
+ };
754
+ const handleMouseLeave = () => {
755
+ setIsHovering(false);
756
+ };
757
+ return /* @__PURE__ */ jsx12(
758
+ Primitive8.div,
759
+ {
760
+ ...rest,
761
+ ref,
762
+ onMouseEnter: composeEventHandlers7(onMouseEnter, handleMouseEnter),
763
+ onMouseLeave: composeEventHandlers7(onMouseLeave, handleMouseLeave)
764
+ }
765
+ );
766
+ }
767
+ );
866
768
 
867
- // src/primitives/branchPicker/BranchPickerNext.tsx
868
- var BranchPickerNext = createActionButton(useGoToNextBranch);
769
+ // src/context/providers/ContentPartProvider.tsx
770
+ import { useEffect as useEffect5, useState as useState2 } from "react";
771
+ import { create as create3 } from "zustand";
869
772
 
870
- // src/actions/useGoToPreviousBranch.tsx
871
- import { useCallback as useCallback3 } from "react";
872
- var useGoToPreviousBranch = () => {
873
- const { useThread } = useAssistantContext();
874
- const { useMessage, useComposer } = useMessageContext();
875
- const disabled = useCombinedStore(
876
- [useMessage, useComposer],
877
- (m, c) => c.isEditing || m.branches.indexOf(m.message.id) <= 0
878
- );
879
- const callback = useCallback3(() => {
880
- const { message, branches } = useMessage.getState();
881
- useThread.getState().switchToBranch(branches[branches.indexOf(message.id) - 1]);
882
- }, [useMessage, useThread]);
883
- if (disabled) return null;
884
- return callback;
773
+ // src/context/ContentPartContext.ts
774
+ import { createContext as createContext3, useContext as useContext4 } from "react";
775
+ var ContentPartContext = createContext3(
776
+ null
777
+ );
778
+ var useContentPartContext = () => {
779
+ const context = useContext4(ContentPartContext);
780
+ if (!context)
781
+ throw new Error(
782
+ "This component can only be used inside a component passed to <MessagePrimitive.Content components={...} >."
783
+ );
784
+ return context;
885
785
  };
886
786
 
887
- // src/primitives/branchPicker/BranchPickerPrevious.tsx
888
- var BranchPickerPrevious = createActionButton(useGoToPreviousBranch);
889
-
890
- // src/primitives/branchPicker/BranchPickerCount.tsx
891
- import { Fragment as Fragment3, jsx as jsx17 } from "react/jsx-runtime";
892
- var BranchPickerCount = () => {
893
- const { useMessage } = useMessageContext();
894
- const branchCount = useMessage((s) => s.branches.length);
895
- return /* @__PURE__ */ jsx17(Fragment3, { children: branchCount });
787
+ // src/context/providers/ContentPartProvider.tsx
788
+ import { jsx as jsx13 } from "react/jsx-runtime";
789
+ var syncContentPart = ({ message }, useContentPart, partIndex) => {
790
+ const part = message.content[partIndex];
791
+ if (!part) return;
792
+ const messageStatus = message.role === "assistant" ? message.status : "done";
793
+ const status = partIndex === message.content.length - 1 ? messageStatus : "done";
794
+ useContentPart.setState({ part, status });
896
795
  };
897
-
898
- // src/primitives/branchPicker/BranchPickerNumber.tsx
899
- import { Fragment as Fragment4, jsx as jsx18 } from "react/jsx-runtime";
900
- var BranchPickerNumber = () => {
796
+ var useContentPartContext2 = (partIndex) => {
901
797
  const { useMessage } = useMessageContext();
902
- const branchIdx = useMessage((s) => s.branches.indexOf(s.message.id));
903
- return /* @__PURE__ */ jsx18(Fragment4, { children: branchIdx + 1 });
904
- };
905
-
906
- // src/primitives/branchPicker/BranchPickerRoot.tsx
907
- import {
908
- Primitive as Primitive11
798
+ const [context] = useState2(() => {
799
+ const useContentPart = create3(() => ({
800
+ part: { type: "text", text: "" },
801
+ status: "done"
802
+ }));
803
+ syncContentPart(useMessage.getState(), useContentPart, partIndex);
804
+ return { useContentPart };
805
+ });
806
+ useEffect5(() => {
807
+ return useMessage.subscribe((message) => {
808
+ syncContentPart(message, context.useContentPart, partIndex);
809
+ });
810
+ }, [context, useMessage, partIndex]);
811
+ return context;
812
+ };
813
+ var ContentPartProvider = ({
814
+ partIndex,
815
+ children
816
+ }) => {
817
+ const context = useContentPartContext2(partIndex);
818
+ return /* @__PURE__ */ jsx13(ContentPartContext.Provider, { value: context, children });
819
+ };
820
+
821
+ // src/primitives/contentPart/ContentPartInProgressIndicator.tsx
822
+ var ContentPartInProgressIndicator = () => {
823
+ const { useMessage } = useMessageContext();
824
+ const { useContentPart } = useContentPartContext();
825
+ const indicator = useCombinedStore(
826
+ [useMessage, useContentPart],
827
+ (m, c) => c.status === "in_progress" ? m.inProgressIndicator : null
828
+ );
829
+ return indicator;
830
+ };
831
+
832
+ // src/primitives/message/MessageContent.tsx
833
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs3 } from "react/jsx-runtime";
834
+ var defaultComponents = {
835
+ Text: ({ part }) => /* @__PURE__ */ jsxs3(Fragment2, { children: [
836
+ part.text,
837
+ /* @__PURE__ */ jsx14(ContentPartInProgressIndicator, {})
838
+ ] }),
839
+ Image: () => null,
840
+ UI: ({ part }) => part.display,
841
+ tools: {
842
+ Fallback: () => null
843
+ }
844
+ };
845
+ var MessageContent = ({
846
+ components: {
847
+ Text = defaultComponents.Text,
848
+ Image = defaultComponents.Image,
849
+ UI = defaultComponents.UI,
850
+ tools: { by_name = {}, Fallback = defaultComponents.tools.Fallback } = {}
851
+ } = {}
852
+ }) => {
853
+ const { useMessage } = useMessageContext();
854
+ const message = useMessage((s) => s.message);
855
+ const content = message.content;
856
+ return /* @__PURE__ */ jsx14(Fragment2, { children: content.map((part, i) => {
857
+ const partIndex = i;
858
+ const type = part.type;
859
+ let component = null;
860
+ switch (type) {
861
+ case "text":
862
+ component = /* @__PURE__ */ jsx14(Text, { part });
863
+ break;
864
+ case "image":
865
+ component = /* @__PURE__ */ jsx14(Image, { part });
866
+ break;
867
+ case "ui":
868
+ component = /* @__PURE__ */ jsx14(UI, { part });
869
+ break;
870
+ case "tool-call": {
871
+ const Tool = by_name[part.name] || Fallback;
872
+ component = /* @__PURE__ */ jsx14(Tool, { part });
873
+ break;
874
+ }
875
+ default:
876
+ throw new Error(`Unknown content part type: ${type}`);
877
+ }
878
+ return /* @__PURE__ */ jsx14(ContentPartProvider, { partIndex, children: component }, partIndex);
879
+ }) });
880
+ };
881
+
882
+ // src/primitives/message/MessageInProgress.tsx
883
+ import {
884
+ Primitive as Primitive9
885
+ } from "@radix-ui/react-primitive";
886
+ import { forwardRef as forwardRef10, useMemo as useMemo3 } from "react";
887
+ import { jsx as jsx15 } from "react/jsx-runtime";
888
+ var MessageInProgress = forwardRef10((props, ref) => {
889
+ const { useMessage } = useMessageContext();
890
+ useMemo3(() => {
891
+ useMessage.getState().setInProgressIndicator(/* @__PURE__ */ jsx15(Primitive9.span, { ...props, ref }));
892
+ }, [useMessage, props, ref]);
893
+ return null;
894
+ });
895
+
896
+ // src/primitives/branchPicker/index.ts
897
+ var branchPicker_exports = {};
898
+ __export(branchPicker_exports, {
899
+ Count: () => BranchPickerCount,
900
+ Next: () => BranchPickerNext,
901
+ Number: () => BranchPickerNumber,
902
+ Previous: () => BranchPickerPrevious,
903
+ Root: () => BranchPickerRoot
904
+ });
905
+
906
+ // src/utils/createActionButton.tsx
907
+ import { composeEventHandlers as composeEventHandlers8 } from "@radix-ui/primitive";
908
+ import {
909
+ Primitive as Primitive10
910
+ } from "@radix-ui/react-primitive";
911
+ import { forwardRef as forwardRef11 } from "react";
912
+ import { jsx as jsx16 } from "react/jsx-runtime";
913
+ var createActionButton = (useActionButton) => {
914
+ return forwardRef11(
915
+ (props, forwardedRef) => {
916
+ const onClick = useActionButton(props);
917
+ return /* @__PURE__ */ jsx16(
918
+ Primitive10.button,
919
+ {
920
+ type: "button",
921
+ disabled: !onClick,
922
+ ...props,
923
+ ref: forwardedRef,
924
+ onClick: composeEventHandlers8(props.onClick, onClick ?? void 0)
925
+ }
926
+ );
927
+ }
928
+ );
929
+ };
930
+
931
+ // src/primitives/branchPicker/BranchPickerNext.tsx
932
+ var BranchPickerNext = createActionButton(useGoToNextBranch);
933
+
934
+ // src/primitives/branchPicker/BranchPickerPrevious.tsx
935
+ var BranchPickerPrevious = createActionButton(useGoToPreviousBranch);
936
+
937
+ // src/primitives/branchPicker/BranchPickerCount.tsx
938
+ import { Fragment as Fragment3, jsx as jsx17 } from "react/jsx-runtime";
939
+ var BranchPickerCount = () => {
940
+ const { useMessage } = useMessageContext();
941
+ const branchCount = useMessage((s) => s.branches.length);
942
+ return /* @__PURE__ */ jsx17(Fragment3, { children: branchCount });
943
+ };
944
+
945
+ // src/primitives/branchPicker/BranchPickerNumber.tsx
946
+ import { Fragment as Fragment4, jsx as jsx18 } from "react/jsx-runtime";
947
+ var BranchPickerNumber = () => {
948
+ const { useMessage } = useMessageContext();
949
+ const branchIdx = useMessage((s) => s.branches.indexOf(s.message.id));
950
+ return /* @__PURE__ */ jsx18(Fragment4, { children: branchIdx + 1 });
951
+ };
952
+
953
+ // src/primitives/branchPicker/BranchPickerRoot.tsx
954
+ import {
955
+ Primitive as Primitive11
909
956
  } from "@radix-ui/react-primitive";
910
957
  import { forwardRef as forwardRef12 } from "react";
911
958
  import { jsx as jsx19 } from "react/jsx-runtime";
@@ -929,7 +976,7 @@ import {
929
976
  import { forwardRef as forwardRef13 } from "react";
930
977
  import { jsx as jsx20 } from "react/jsx-runtime";
931
978
  var ActionBarRoot = forwardRef13(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
932
- const { useThread } = useAssistantContext();
979
+ const { useThread } = useThreadContext();
933
980
  const { useMessage } = useMessageContext();
934
981
  const hideAndfloatStatus = useCombinedStore(
935
982
  [useThread, useMessage],
@@ -954,162 +1001,31 @@ var ActionBarRoot = forwardRef13(({ hideWhenRunning, autohide, autohideFloat, ..
954
1001
  );
955
1002
  });
956
1003
 
957
- // src/actions/useCopyMessage.tsx
958
- import { useCallback as useCallback4 } from "react";
959
- var useCopyMessage = ({ copiedDuration = 3e3 }) => {
960
- const { useMessage, useComposer } = useMessageContext();
961
- const hasCopyableContent = useCombinedStore(
962
- [useMessage, useComposer],
963
- (m, c) => {
964
- return c.isEditing || m.message.content.some((c2) => c2.type === "text");
965
- }
966
- );
967
- const callback = useCallback4(() => {
968
- const { isEditing, value: composerValue } = useComposer.getState();
969
- const { message, setIsCopied } = useMessage.getState();
970
- const valueToCopy = isEditing ? composerValue : getMessageText(message);
971
- navigator.clipboard.writeText(valueToCopy);
972
- setIsCopied(true);
973
- setTimeout(() => setIsCopied(false), copiedDuration);
974
- }, [useComposer, useMessage, copiedDuration]);
975
- if (!hasCopyableContent) return null;
976
- return callback;
977
- };
978
-
979
1004
  // src/primitives/actionBar/ActionBarCopy.tsx
980
1005
  var ActionBarCopy = createActionButton(useCopyMessage);
981
1006
 
982
- // src/actions/useReloadMessage.tsx
983
- import { useCallback as useCallback5 } from "react";
984
- var useReloadMessage = () => {
985
- const { useThread, useViewport } = useAssistantContext();
986
- const { useMessage } = useMessageContext();
987
- const disabled = useCombinedStore(
988
- [useThread, useMessage],
989
- (t, m) => t.isRunning || m.message.role !== "assistant"
990
- );
991
- const callback = useCallback5(() => {
992
- const { parentId } = useMessage.getState();
993
- useThread.getState().startRun(parentId);
994
- useViewport.getState().scrollToBottom();
995
- }, [useMessage, useThread, useViewport]);
996
- if (disabled) return null;
997
- return callback;
998
- };
999
-
1000
1007
  // src/primitives/actionBar/ActionBarReload.tsx
1001
1008
  var ActionBarReload = createActionButton(useReloadMessage);
1002
1009
 
1003
- // src/actions/useBeginMessageEdit.tsx
1004
- import { useCallback as useCallback6 } from "react";
1005
- var useBeginMessageEdit = () => {
1006
- const { useMessage, useComposer } = useMessageContext();
1007
- const disabled = useCombinedStore(
1008
- [useMessage, useComposer],
1009
- (m, c) => m.message.role !== "user" || c.isEditing
1010
- );
1011
- const callback = useCallback6(() => {
1012
- const { edit } = useComposer.getState();
1013
- edit();
1014
- }, [useComposer]);
1015
- if (disabled) return null;
1016
- return callback;
1017
- };
1018
-
1019
1010
  // src/primitives/actionBar/ActionBarEdit.tsx
1020
1011
  var ActionBarEdit = createActionButton(useBeginMessageEdit);
1021
1012
 
1022
1013
  // src/primitives/contentPart/index.ts
1023
1014
  var contentPart_exports = {};
1024
1015
  __export(contentPart_exports, {
1025
- InProgressIndicator: () => ContentPartInProgressIndicator,
1026
- Provider: () => ContentPartProvider
1016
+ InProgressIndicator: () => ContentPartInProgressIndicator
1027
1017
  });
1028
1018
 
1029
- // src/adapters/vercel/VercelAIAssistantProvider.tsx
1030
- import { useEffect as useEffect6 } from "react";
1031
-
1032
- // src/adapters/core/AssistantProvider.tsx
1033
- import {
1034
- useEffect as useEffect4,
1035
- useInsertionEffect,
1036
- useRef as useRef4,
1037
- useState as useState3
1038
- } from "react";
1039
- import { create as create5 } from "zustand";
1019
+ // src/runtime/vercel-ai/rsc/useVercelRSCRuntime.tsx
1020
+ import { useEffect as useEffect7, useInsertionEffect, useState as useState3 } from "react";
1040
1021
 
1041
- // src/utils/context/stores/ViewportStore.tsx
1022
+ // src/runtime/vercel-ai/rsc/VercelRSCRuntime.tsx
1042
1023
  import { create as create4 } from "zustand";
1043
- var makeViewportStore = () => {
1044
- const scrollToBottomListeners = /* @__PURE__ */ new Set();
1045
- return create4(() => ({
1046
- isAtBottom: true,
1047
- scrollToBottom: () => {
1048
- for (const listener of scrollToBottomListeners) {
1049
- listener();
1050
- }
1051
- },
1052
- onScrollToBottom: (callback) => {
1053
- scrollToBottomListeners.add(callback);
1054
- return () => {
1055
- scrollToBottomListeners.delete(callback);
1056
- };
1057
- }
1058
- }));
1059
- };
1060
-
1061
- // src/adapters/core/AssistantProvider.tsx
1062
- import { jsx as jsx21 } from "react/jsx-runtime";
1063
- var makeThreadStore = (runtimeRef) => {
1064
- const useThread = create5(() => ({
1065
- messages: runtimeRef.current.messages,
1066
- isRunning: runtimeRef.current.isRunning,
1067
- getBranches: (messageId) => runtimeRef.current.getBranches(messageId),
1068
- switchToBranch: (branchId) => runtimeRef.current.switchToBranch(branchId),
1069
- startRun: (parentId) => runtimeRef.current.startRun(parentId),
1070
- append: (message) => runtimeRef.current.append(message),
1071
- cancelRun: () => runtimeRef.current.cancelRun()
1072
- }));
1073
- const onRuntimeUpdate = () => {
1074
- useThread.setState({
1075
- messages: runtimeRef.current.messages,
1076
- isRunning: runtimeRef.current.isRunning
1077
- });
1078
- };
1079
- return {
1080
- useThread,
1081
- onRuntimeUpdate
1082
- };
1083
- };
1084
- var AssistantProvider = ({ children, runtime }) => {
1085
- const runtimeRef = useRef4(runtime);
1086
- useInsertionEffect(() => {
1087
- runtimeRef.current = runtime;
1088
- });
1089
- const [{ context, onRuntimeUpdate }] = useState3(() => {
1090
- const { useThread, onRuntimeUpdate: onRuntimeUpdate2 } = makeThreadStore(runtimeRef);
1091
- const useViewport = makeViewportStore();
1092
- const useComposer = makeThreadComposerStore(useThread);
1093
- return {
1094
- context: {
1095
- useViewport,
1096
- useThread,
1097
- useComposer
1098
- },
1099
- onRuntimeUpdate: onRuntimeUpdate2
1100
- };
1101
- });
1102
- useEffect4(() => {
1103
- onRuntimeUpdate();
1104
- return runtime.subscribe(onRuntimeUpdate);
1105
- }, [onRuntimeUpdate, runtime]);
1106
- return /* @__PURE__ */ jsx21(AssistantContext.Provider, { value: context, children });
1107
- };
1108
1024
 
1109
- // src/adapters/core/vercel-use-chat/useVercelUseChatRuntime.tsx
1110
- import { useEffect as useEffect5, useInsertionEffect as useInsertionEffect2, useMemo as useMemo5, useState as useState4 } from "react";
1025
+ // src/runtime/vercel-ai/rsc/useVercelRSCSync.tsx
1026
+ import { useEffect as useEffect6, useMemo as useMemo4 } from "react";
1111
1027
 
1112
- // src/adapters/ThreadMessageConverter.ts
1028
+ // src/runtime/vercel-ai/utils/ThreadMessageConverter.ts
1113
1029
  var ThreadMessageConverter = class {
1114
1030
  cache = /* @__PURE__ */ new WeakMap();
1115
1031
  convertMessages(converter, messages) {
@@ -1122,17 +1038,129 @@ var ThreadMessageConverter = class {
1122
1038
  }
1123
1039
  };
1124
1040
 
1125
- // src/adapters/vercel/VercelThreadMessage.tsx
1126
- var symbolInnerMessage = Symbol("innerMessage");
1127
- var symbolInnerRSCMessage = Symbol("innerRSCMessage");
1128
- var getVercelMessage = (message) => {
1129
- return message[symbolInnerMessage];
1130
- };
1041
+ // src/runtime/vercel-ai/rsc/getVercelRSCMessage.tsx
1042
+ var symbolInnerRSCMessage = Symbol("innerVercelRSCMessage");
1131
1043
  var getVercelRSCMessage = (message) => {
1132
1044
  return message[symbolInnerRSCMessage];
1133
1045
  };
1134
1046
 
1135
- // src/adapters/idUtils.tsx
1047
+ // src/runtime/vercel-ai/rsc/useVercelRSCSync.tsx
1048
+ var vercelToThreadMessage = (converter, rawMessage) => {
1049
+ const message = converter(rawMessage);
1050
+ return {
1051
+ id: message.id,
1052
+ role: message.role,
1053
+ content: [{ type: "ui", display: message.display }],
1054
+ createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1055
+ ...{ status: "done" },
1056
+ [symbolInnerRSCMessage]: rawMessage
1057
+ };
1058
+ };
1059
+ var useVercelRSCSync = (adapter, updateData) => {
1060
+ const [converter, convertCallback] = useMemo4(() => {
1061
+ const rscConverter = adapter.convertMessage ?? ((m) => m);
1062
+ const convertCallback2 = (m, cache) => {
1063
+ if (cache) return cache;
1064
+ return vercelToThreadMessage(rscConverter, m);
1065
+ };
1066
+ return [new ThreadMessageConverter(), convertCallback2];
1067
+ }, [adapter.convertMessage]);
1068
+ useEffect6(() => {
1069
+ updateData(converter.convertMessages(convertCallback, adapter.messages));
1070
+ }, [updateData, converter, convertCallback, adapter.messages]);
1071
+ };
1072
+
1073
+ // src/runtime/vercel-ai/rsc/VercelRSCRuntime.tsx
1074
+ var EMPTY_BRANCHES = Object.freeze([]);
1075
+ var VercelRSCRuntime = class {
1076
+ constructor(adapter) {
1077
+ this.adapter = adapter;
1078
+ this.useAdapter = create4(() => ({
1079
+ adapter
1080
+ }));
1081
+ }
1082
+ useAdapter;
1083
+ _subscriptions = /* @__PURE__ */ new Set();
1084
+ isRunning = false;
1085
+ messages = [];
1086
+ withRunning = (callback) => {
1087
+ this.isRunning = true;
1088
+ return callback.finally(() => {
1089
+ this.isRunning = false;
1090
+ });
1091
+ };
1092
+ getBranches() {
1093
+ return EMPTY_BRANCHES;
1094
+ }
1095
+ switchToBranch() {
1096
+ throw new Error(
1097
+ "Branch switching is not supported by VercelRSCAssistantProvider."
1098
+ );
1099
+ }
1100
+ async append(message) {
1101
+ if (message.parentId !== (this.messages.at(-1)?.id ?? null)) {
1102
+ if (!this.adapter.edit)
1103
+ throw new Error(
1104
+ "Message editing is not enabled, please provide an edit callback to VercelRSCAssistantProvider."
1105
+ );
1106
+ await this.withRunning(this.adapter.edit(message));
1107
+ } else {
1108
+ await this.withRunning(this.adapter.append(message));
1109
+ }
1110
+ }
1111
+ async startRun(parentId) {
1112
+ if (!this.adapter.reload)
1113
+ throw new Error(
1114
+ "Message reloading is not enabled, please provide a reload callback to VercelRSCAssistantProvider."
1115
+ );
1116
+ await this.withRunning(this.adapter.reload(parentId));
1117
+ }
1118
+ cancelRun() {
1119
+ if (process.env["NODE_ENV"] === "development") {
1120
+ console.warn(
1121
+ "Run cancellation is not supported by VercelRSCAssistantProvider."
1122
+ );
1123
+ }
1124
+ }
1125
+ subscribe(callback) {
1126
+ this._subscriptions.add(callback);
1127
+ return () => this._subscriptions.delete(callback);
1128
+ }
1129
+ onAdapterUpdated() {
1130
+ if (this.useAdapter.getState().adapter !== this.adapter) {
1131
+ this.useAdapter.setState({ adapter: this.adapter });
1132
+ }
1133
+ }
1134
+ updateData = (messages) => {
1135
+ this.messages = messages;
1136
+ for (const callback of this._subscriptions) callback();
1137
+ };
1138
+ unstable_synchronizer = () => {
1139
+ const { adapter } = this.useAdapter();
1140
+ useVercelRSCSync(adapter, this.updateData);
1141
+ return null;
1142
+ };
1143
+ };
1144
+
1145
+ // src/runtime/vercel-ai/rsc/useVercelRSCRuntime.tsx
1146
+ var useVercelRSCRuntime = (adapter) => {
1147
+ const [runtime] = useState3(() => new VercelRSCRuntime(adapter));
1148
+ useInsertionEffect(() => {
1149
+ runtime.adapter = adapter;
1150
+ });
1151
+ useEffect7(() => {
1152
+ runtime.onAdapterUpdated();
1153
+ });
1154
+ return runtime;
1155
+ };
1156
+
1157
+ // src/runtime/vercel-ai/ui/use-chat/useVercelUseChatRuntime.tsx
1158
+ import { useEffect as useEffect10, useInsertionEffect as useInsertionEffect2, useState as useState4 } from "react";
1159
+
1160
+ // src/runtime/vercel-ai/ui/VercelAIRuntime.tsx
1161
+ import { create as create5 } from "zustand";
1162
+
1163
+ // src/runtime/utils/idUtils.tsx
1136
1164
  import { customAlphabet } from "nanoid/non-secure";
1137
1165
  var generateId = customAlphabet(
1138
1166
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
@@ -1141,7 +1169,7 @@ var generateId = customAlphabet(
1141
1169
  var optimisticPrefix = "__optimistic__";
1142
1170
  var generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
1143
1171
 
1144
- // src/adapters/MessageRepository.tsx
1172
+ // src/runtime/utils/MessageRepository.tsx
1145
1173
  var findHead = (message) => {
1146
1174
  if (message.next) return findHead(message.next);
1147
1175
  return message;
@@ -1210,10 +1238,10 @@ var MessageRepository = class {
1210
1238
  level: prev ? prev.level + 1 : 0
1211
1239
  };
1212
1240
  this.messages.set(message.id, newItem);
1241
+ this.performOp(prev, newItem, "link");
1213
1242
  if (this.head === prev) {
1214
1243
  this.head = newItem;
1215
1244
  }
1216
- this.performOp(prev, newItem, "link");
1217
1245
  }
1218
1246
  appendOptimisticMessage(parentId, message) {
1219
1247
  let optimisticId;
@@ -1230,14 +1258,14 @@ var MessageRepository = class {
1230
1258
  }
1231
1259
  deleteMessage(messageId, replacementId) {
1232
1260
  const message = this.messages.get(messageId);
1233
- const replacement = replacementId ? this.messages.get(replacementId) : null;
1234
1261
  if (!message)
1235
1262
  throw new Error(
1236
1263
  "MessageRepository(deleteMessage): Optimistic message not found. This is likely an internal bug in assistant-ui."
1237
1264
  );
1265
+ const replacement = replacementId === void 0 ? message.prev : replacementId === null ? null : this.messages.get(replacementId);
1238
1266
  if (replacement === void 0)
1239
1267
  throw new Error(
1240
- "MessageRepository(deleteMessage): New message not found. This is likely an internal bug in assistant-ui."
1268
+ "MessageRepository(deleteMessage): Replacement not found. This is likely an internal bug in assistant-ui."
1241
1269
  );
1242
1270
  for (const child of message.children) {
1243
1271
  const childMessage = this.messages.get(child);
@@ -1247,11 +1275,11 @@ var MessageRepository = class {
1247
1275
  );
1248
1276
  this.performOp(replacement, childMessage, "relink");
1249
1277
  }
1278
+ this.performOp(null, message, "cut");
1250
1279
  this.messages.delete(messageId);
1251
1280
  if (this.head === message) {
1252
- this.head = replacement;
1281
+ this.head = replacement ? findHead(replacement) : null;
1253
1282
  }
1254
- this.performOp(null, message, "cut");
1255
1283
  }
1256
1284
  getBranches(messageId) {
1257
1285
  const message = this.messages.get(messageId);
@@ -1292,7 +1320,13 @@ var MessageRepository = class {
1292
1320
  }
1293
1321
  };
1294
1322
 
1295
- // src/adapters/core/vercel-use-chat/VercelUseChatRuntime.tsx
1323
+ // src/runtime/vercel-ai/ui/getVercelAIMessage.tsx
1324
+ var symbolInnerAIMessage = Symbol("innerVercelAIUIMessage");
1325
+ var getVercelAIMessage = (message) => {
1326
+ return message[symbolInnerAIMessage];
1327
+ };
1328
+
1329
+ // src/runtime/vercel-ai/ui/utils/sliceMessagesUntil.tsx
1296
1330
  var sliceMessagesUntil = (messages, messageId) => {
1297
1331
  if (messageId == null) return [];
1298
1332
  const messageIdx = messages.findIndex((m) => m.id === messageId);
@@ -1300,18 +1334,97 @@ var sliceMessagesUntil = (messages, messageId) => {
1300
1334
  throw new Error(
1301
1335
  "useVercelAIThreadState: Message not found. This is liekly an internal bug in assistant-ui."
1302
1336
  );
1303
- return messages.slice(0, messageIdx + 1);
1337
+ return messages.slice(0, messageIdx + 1);
1338
+ };
1339
+
1340
+ // src/runtime/vercel-ai/ui/utils/useVercelAIComposerSync.tsx
1341
+ import { useEffect as useEffect8 } from "react";
1342
+ var useVercelAIComposerSync = (vercel) => {
1343
+ const { useComposer } = useThreadContext();
1344
+ useEffect8(() => {
1345
+ useComposer.setState({
1346
+ value: vercel.input,
1347
+ setValue: vercel.setInput
1348
+ });
1349
+ }, [useComposer, vercel.input, vercel.setInput]);
1350
+ };
1351
+
1352
+ // src/runtime/vercel-ai/ui/utils/useVercelAIThreadSync.tsx
1353
+ import { useEffect as useEffect9, useMemo as useMemo5 } from "react";
1354
+ var getIsRunning = (vercel) => {
1355
+ if ("isLoading" in vercel) return vercel.isLoading;
1356
+ return vercel.status === "in_progress";
1357
+ };
1358
+ var vercelToThreadMessage2 = (message, status) => {
1359
+ const common = {
1360
+ id: message.id,
1361
+ createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1362
+ [symbolInnerAIMessage]: message
1363
+ };
1364
+ switch (message.role) {
1365
+ case "user":
1366
+ return {
1367
+ ...common,
1368
+ role: "user",
1369
+ content: [{ type: "text", text: message.content }]
1370
+ };
1371
+ case "assistant":
1372
+ return {
1373
+ ...common,
1374
+ role: "assistant",
1375
+ content: [
1376
+ ...message.content ? [{ type: "text", text: message.content }] : [],
1377
+ ...message.toolInvocations?.map(
1378
+ (t) => ({
1379
+ type: "tool-call",
1380
+ name: t.toolName,
1381
+ args: t.args,
1382
+ result: "result" in t ? t.result : void 0
1383
+ })
1384
+ ) ?? []
1385
+ ],
1386
+ status
1387
+ };
1388
+ default:
1389
+ throw new Error(
1390
+ `You have a message with an unsupported role. The role ${message.role} is not supported.`
1391
+ );
1392
+ }
1393
+ };
1394
+ var useVercelAIThreadSync = (vercel, updateData) => {
1395
+ const isRunning = getIsRunning(vercel);
1396
+ const converter = useMemo5(() => new ThreadMessageConverter(), []);
1397
+ useEffect9(() => {
1398
+ const lastMessageId = vercel.messages.at(-1)?.id;
1399
+ const convertCallback = (message, cache) => {
1400
+ const status = lastMessageId === message.id && isRunning ? "in_progress" : "done";
1401
+ if (cache && (cache.role === "user" || cache.status === status))
1402
+ return cache;
1403
+ return vercelToThreadMessage2(message, status);
1404
+ };
1405
+ const messages = converter.convertMessages(
1406
+ convertCallback,
1407
+ vercel.messages
1408
+ );
1409
+ updateData(isRunning, messages);
1410
+ }, [updateData, isRunning, vercel.messages, converter]);
1304
1411
  };
1412
+
1413
+ // src/runtime/vercel-ai/ui/VercelAIRuntime.tsx
1305
1414
  var hasUpcomingMessage = (isRunning, messages) => {
1306
1415
  return isRunning && messages[messages.length - 1]?.role !== "assistant";
1307
1416
  };
1308
- var VercelUseChatRuntime = class {
1417
+ var VercelAIRuntime = class {
1309
1418
  constructor(vercel) {
1310
1419
  this.vercel = vercel;
1420
+ this.useVercel = create5(() => ({
1421
+ vercel
1422
+ }));
1311
1423
  }
1312
1424
  _subscriptions = /* @__PURE__ */ new Set();
1313
1425
  repository = new MessageRepository();
1314
1426
  assistantOptimisticId = null;
1427
+ useVercel;
1315
1428
  messages = [];
1316
1429
  isRunning = false;
1317
1430
  getBranches(messageId) {
@@ -1319,9 +1432,7 @@ var VercelUseChatRuntime = class {
1319
1432
  }
1320
1433
  switchToBranch(branchId) {
1321
1434
  this.repository.switchToBranch(branchId);
1322
- this.vercel.setMessages(
1323
- this.repository.getMessages().map(getVercelMessage).filter((m) => m != null)
1324
- );
1435
+ this.updateVercelMessages(this.repository.getMessages());
1325
1436
  }
1326
1437
  async append(message) {
1327
1438
  if (message.content.length !== 1 || message.content[0]?.type !== "text")
@@ -1347,20 +1458,44 @@ var VercelUseChatRuntime = class {
1347
1458
  await reloadMaybe();
1348
1459
  }
1349
1460
  cancelRun() {
1350
- const lastMessage = this.vercel.messages.at(-1);
1461
+ const previousMessage = this.vercel.messages.at(-1);
1351
1462
  this.vercel.stop();
1352
- if (lastMessage?.role === "user") {
1353
- this.vercel.setInput(lastMessage.content);
1463
+ if (this.assistantOptimisticId) {
1464
+ this.repository.deleteMessage(this.assistantOptimisticId);
1465
+ this.assistantOptimisticId = null;
1466
+ }
1467
+ let messages = this.repository.getMessages();
1468
+ if (previousMessage?.role === "user" && previousMessage.id === messages.at(-1)?.id) {
1469
+ this.vercel.setInput(previousMessage.content);
1470
+ this.repository.deleteMessage(previousMessage.id);
1471
+ messages = this.repository.getMessages();
1472
+ }
1473
+ setTimeout(() => {
1474
+ this.updateVercelMessages(messages);
1475
+ }, 0);
1476
+ }
1477
+ subscribe(callback) {
1478
+ this._subscriptions.add(callback);
1479
+ return () => this._subscriptions.delete(callback);
1480
+ }
1481
+ updateVercelMessages = (messages) => {
1482
+ this.vercel.setMessages(
1483
+ messages.map(getVercelAIMessage).filter((m) => m != null)
1484
+ );
1485
+ };
1486
+ onVercelUpdated() {
1487
+ if (this.useVercel.getState().vercel !== this.vercel) {
1488
+ this.useVercel.setState({ vercel: this.vercel });
1354
1489
  }
1355
1490
  }
1356
- updateData(isRunning, vm) {
1491
+ updateData = (isRunning, vm) => {
1357
1492
  for (let i = 0; i < vm.length; i++) {
1358
1493
  const message = vm[i];
1359
1494
  const parent = vm[i - 1];
1360
1495
  this.repository.addOrUpdateMessage(parent?.id ?? null, message);
1361
1496
  }
1362
1497
  if (this.assistantOptimisticId) {
1363
- this.repository.deleteMessage(this.assistantOptimisticId, null);
1498
+ this.repository.deleteMessage(this.assistantOptimisticId);
1364
1499
  this.assistantOptimisticId = null;
1365
1500
  }
1366
1501
  if (hasUpcomingMessage(isRunning, vm)) {
@@ -1378,211 +1513,194 @@ var VercelUseChatRuntime = class {
1378
1513
  this.messages = this.repository.getMessages();
1379
1514
  this.isRunning = isRunning;
1380
1515
  for (const callback of this._subscriptions) callback();
1381
- }
1382
- subscribe(callback) {
1383
- this._subscriptions.add(callback);
1384
- return () => this._subscriptions.delete(callback);
1385
- }
1386
- };
1387
-
1388
- // src/adapters/core/vercel-use-chat/useVercelUseChatRuntime.tsx
1389
- var getIsRunning = (vercel) => {
1390
- if ("isLoading" in vercel) return vercel.isLoading;
1391
- return vercel.status === "in_progress";
1392
- };
1393
- var vercelToThreadMessage = (message, status) => {
1394
- const common = {
1395
- id: message.id,
1396
- createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1397
- [symbolInnerMessage]: message
1398
1516
  };
1399
- switch (message.role) {
1400
- case "user":
1401
- return {
1402
- ...common,
1403
- role: "user",
1404
- content: [{ type: "text", text: message.content }]
1405
- };
1406
- case "assistant":
1407
- return {
1408
- ...common,
1409
- role: "assistant",
1410
- content: [
1411
- ...message.content ? [{ type: "text", text: message.content }] : [],
1412
- ...message.toolInvocations?.map(
1413
- (t) => ({
1414
- type: "tool-call",
1415
- name: t.toolName,
1416
- args: t.args,
1417
- result: "result" in t ? t.result : void 0
1418
- })
1419
- ) ?? []
1420
- ],
1421
- status
1422
- };
1423
- default:
1424
- throw new Error(
1425
- `You have a message with an unsupported role. The role ${message.role} is not supported.`
1426
- );
1427
- }
1517
+ unstable_synchronizer = () => {
1518
+ const { vercel } = this.useVercel();
1519
+ useVercelAIThreadSync(vercel, this.updateData);
1520
+ useVercelAIComposerSync(vercel);
1521
+ return null;
1522
+ };
1428
1523
  };
1429
- var useVercelUseChatRuntime = (vercel) => {
1430
- const [runtime] = useState4(() => new VercelUseChatRuntime(vercel));
1524
+
1525
+ // src/runtime/vercel-ai/ui/use-chat/useVercelUseChatRuntime.tsx
1526
+ var useVercelUseChatRuntime = (chatHelpers) => {
1527
+ const [runtime] = useState4(() => new VercelAIRuntime(chatHelpers));
1431
1528
  useInsertionEffect2(() => {
1432
- runtime.vercel = vercel;
1529
+ runtime.vercel = chatHelpers;
1530
+ });
1531
+ useEffect10(() => {
1532
+ runtime.onVercelUpdated();
1433
1533
  });
1434
- const isRunning = getIsRunning(vercel);
1435
- const converter = useMemo5(() => new ThreadMessageConverter(), []);
1436
- const messages = useMemo5(() => {
1437
- const lastMessageId = vercel.messages.at(-1)?.id;
1438
- const convertCallback = (message, cache) => {
1439
- const status = lastMessageId === message.id && isRunning ? "in_progress" : "done";
1440
- if (cache && (cache.role === "user" || cache.status === status))
1441
- return cache;
1442
- return vercelToThreadMessage(message, status);
1443
- };
1444
- return converter.convertMessages(convertCallback, vercel.messages);
1445
- }, [isRunning, vercel.messages, converter]);
1446
- useEffect5(() => {
1447
- runtime.updateData(isRunning, messages);
1448
- }, [runtime, isRunning, messages]);
1449
1534
  return runtime;
1450
1535
  };
1451
1536
 
1452
- // src/adapters/vercel/VercelAIAssistantProvider.tsx
1453
- import { jsx as jsx22, jsxs as jsxs4 } from "react/jsx-runtime";
1454
- var VercelAIAssistantProvider = ({
1455
- children,
1456
- ...rest
1457
- }) => {
1458
- const vercel = "chat" in rest ? rest.chat : rest.assistant;
1459
- const runtime = useVercelUseChatRuntime(vercel);
1460
- return /* @__PURE__ */ jsxs4(AssistantProvider, { runtime, children: [
1461
- /* @__PURE__ */ jsx22(ComposerSync, { vercel }),
1462
- children
1463
- ] });
1464
- };
1465
- var ComposerSync = ({
1466
- vercel
1467
- }) => {
1468
- const { useComposer } = useAssistantContext();
1469
- useEffect6(() => {
1470
- useComposer.setState({
1471
- value: vercel.input,
1472
- setValue: vercel.setInput
1473
- });
1474
- }, [useComposer, vercel.input, vercel.setInput]);
1475
- return null;
1537
+ // src/runtime/vercel-ai/ui/use-assistant/useVercelUseAssistantRuntime.tsx
1538
+ import { useEffect as useEffect11, useInsertionEffect as useInsertionEffect3, useState as useState5 } from "react";
1539
+ var useVercelUseAssistantRuntime = (assistantHelpers) => {
1540
+ const [runtime] = useState5(() => new VercelAIRuntime(assistantHelpers));
1541
+ useInsertionEffect3(() => {
1542
+ runtime.vercel = assistantHelpers;
1543
+ });
1544
+ useEffect11(() => {
1545
+ runtime.onVercelUpdated();
1546
+ });
1547
+ return runtime;
1476
1548
  };
1477
1549
 
1478
- // src/adapters/core/vercel-rsc/useVercelRSCRuntime.tsx
1479
- import { useEffect as useEffect7, useInsertionEffect as useInsertionEffect3, useMemo as useMemo6, useState as useState5 } from "react";
1550
+ // src/context/providers/AssistantRuntimeProvider.tsx
1551
+ import { memo } from "react";
1480
1552
 
1481
- // src/adapters/core/vercel-rsc/VercelRSCRuntime.tsx
1482
- var EMPTY_BRANCHES = Object.freeze([]);
1483
- var VercelRSCRuntime = class {
1484
- constructor(adapter) {
1485
- this.adapter = adapter;
1486
- }
1487
- _subscriptions = /* @__PURE__ */ new Set();
1488
- isRunning = false;
1489
- messages = [];
1490
- withRunning = (callback) => {
1491
- this.isRunning = true;
1492
- return callback.finally(() => {
1493
- this.isRunning = false;
1494
- });
1495
- };
1496
- getBranches() {
1497
- return EMPTY_BRANCHES;
1498
- }
1499
- switchToBranch() {
1500
- throw new Error(
1501
- "Branch switching is not supported by VercelRSCAssistantProvider."
1502
- );
1503
- }
1504
- async append(message) {
1505
- if (message.parentId !== (this.messages.at(-1)?.id ?? null)) {
1506
- if (!this.adapter.edit)
1507
- throw new Error(
1508
- "Message editing is not enabled, please provide an edit callback to VercelRSCAssistantProvider."
1509
- );
1510
- await this.withRunning(this.adapter.edit(message));
1511
- } else {
1512
- await this.withRunning(this.adapter.append(message));
1513
- }
1514
- }
1515
- async startRun(parentId) {
1516
- if (!this.adapter.reload)
1517
- throw new Error(
1518
- "Message reloading is not enabled, please provide a reload callback to VercelRSCAssistantProvider."
1519
- );
1520
- await this.withRunning(this.adapter.reload(parentId));
1521
- }
1522
- cancelRun() {
1523
- if (process.env["NODE_ENV"] === "development") {
1524
- console.warn(
1525
- "Run cancellation is not supported by VercelRSCAssistantProvider."
1526
- );
1553
+ // src/context/providers/ThreadProvider.tsx
1554
+ import { useEffect as useEffect12, useInsertionEffect as useInsertionEffect4, useRef as useRef4, useState as useState6 } from "react";
1555
+
1556
+ // src/context/stores/Composer.ts
1557
+ import { create as create6 } from "zustand";
1558
+ var makeComposerStore = (useThread) => create6()((set, get, store) => {
1559
+ return {
1560
+ ...makeBaseComposer(set, get, store),
1561
+ isEditing: true,
1562
+ send: () => {
1563
+ const { setValue, value } = get();
1564
+ setValue("");
1565
+ useThread.getState().append({
1566
+ parentId: useThread.getState().messages.at(-1)?.id ?? null,
1567
+ content: [{ type: "text", text: value }]
1568
+ });
1569
+ },
1570
+ cancel: () => {
1571
+ const thread = useThread.getState();
1572
+ if (!thread.isRunning) return false;
1573
+ useThread.getState().cancelRun();
1574
+ return true;
1527
1575
  }
1528
- }
1529
- updateData(messages) {
1530
- this.messages = messages;
1531
- for (const callback of this._subscriptions) callback();
1532
- }
1533
- subscribe(callback) {
1534
- this._subscriptions.add(callback);
1535
- return () => this._subscriptions.delete(callback);
1536
- }
1537
- };
1576
+ };
1577
+ });
1538
1578
 
1539
- // src/adapters/core/vercel-rsc/useVercelRSCRuntime.tsx
1540
- var vercelToThreadMessage2 = (converter, rawMessage) => {
1541
- const message = converter(rawMessage);
1579
+ // src/context/stores/Thread.ts
1580
+ import { create as create7 } from "zustand";
1581
+ var makeThreadStore = (runtimeRef) => {
1582
+ const useThread = create7(() => ({
1583
+ messages: runtimeRef.current.messages,
1584
+ isRunning: runtimeRef.current.isRunning,
1585
+ getBranches: (messageId) => runtimeRef.current.getBranches(messageId),
1586
+ switchToBranch: (branchId) => runtimeRef.current.switchToBranch(branchId),
1587
+ startRun: (parentId) => runtimeRef.current.startRun(parentId),
1588
+ append: (message) => runtimeRef.current.append(message),
1589
+ cancelRun: () => runtimeRef.current.cancelRun()
1590
+ }));
1591
+ const onRuntimeUpdate = () => {
1592
+ useThread.setState({
1593
+ messages: runtimeRef.current.messages,
1594
+ isRunning: runtimeRef.current.isRunning
1595
+ });
1596
+ };
1542
1597
  return {
1543
- id: message.id,
1544
- role: message.role,
1545
- content: [{ type: "ui", display: message.display }],
1546
- createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1547
- ...{ status: "done" },
1548
- [symbolInnerRSCMessage]: rawMessage
1598
+ useThread,
1599
+ onRuntimeUpdate
1549
1600
  };
1550
1601
  };
1551
- var useVercelRSCRuntime = (adapter) => {
1552
- const [runtime] = useState5(() => new VercelRSCRuntime(adapter));
1553
- useInsertionEffect3(() => {
1554
- runtime.adapter = adapter;
1602
+
1603
+ // src/context/stores/ThreadViewport.tsx
1604
+ import { create as create8 } from "zustand";
1605
+ var makeThreadViewportStore = () => {
1606
+ const scrollToBottomListeners = /* @__PURE__ */ new Set();
1607
+ return create8(() => ({
1608
+ isAtBottom: true,
1609
+ scrollToBottom: () => {
1610
+ for (const listener of scrollToBottomListeners) {
1611
+ listener();
1612
+ }
1613
+ },
1614
+ onScrollToBottom: (callback) => {
1615
+ scrollToBottomListeners.add(callback);
1616
+ return () => {
1617
+ scrollToBottomListeners.delete(callback);
1618
+ };
1619
+ }
1620
+ }));
1621
+ };
1622
+
1623
+ // src/context/providers/ThreadProvider.tsx
1624
+ import { jsx as jsx21, jsxs as jsxs4 } from "react/jsx-runtime";
1625
+ var ThreadProvider = ({
1626
+ children,
1627
+ runtime
1628
+ }) => {
1629
+ const runtimeRef = useRef4(runtime);
1630
+ useInsertionEffect4(() => {
1631
+ runtimeRef.current = runtime;
1555
1632
  });
1556
- const [converter, convertCallback] = useMemo6(() => {
1557
- const rscConverter = adapter.convertMessage ?? ((m) => m);
1558
- const convertCallback2 = (m, cache) => {
1559
- if (cache) return cache;
1560
- return vercelToThreadMessage2(rscConverter, m);
1633
+ const [{ context, onRuntimeUpdate }] = useState6(() => {
1634
+ const { useThread, onRuntimeUpdate: onRuntimeUpdate2 } = makeThreadStore(runtimeRef);
1635
+ const useViewport = makeThreadViewportStore();
1636
+ const useComposer = makeComposerStore(useThread);
1637
+ return {
1638
+ context: {
1639
+ useViewport,
1640
+ useThread,
1641
+ useComposer
1642
+ },
1643
+ onRuntimeUpdate: onRuntimeUpdate2
1561
1644
  };
1562
- return [new ThreadMessageConverter(), convertCallback2];
1563
- }, [adapter.convertMessage]);
1564
- useEffect7(() => {
1565
- runtime.updateData(
1566
- converter.convertMessages(convertCallback, adapter.messages)
1567
- );
1568
- }, [runtime, converter, convertCallback, adapter.messages]);
1569
- return runtime;
1645
+ });
1646
+ useEffect12(() => {
1647
+ onRuntimeUpdate();
1648
+ return runtime.subscribe(onRuntimeUpdate);
1649
+ }, [onRuntimeUpdate, runtime]);
1650
+ const RuntimeSynchronizer = runtime.unstable_synchronizer;
1651
+ return /* @__PURE__ */ jsxs4(ThreadContext.Provider, { value: context, children: [
1652
+ RuntimeSynchronizer && /* @__PURE__ */ jsx21(RuntimeSynchronizer, {}),
1653
+ children
1654
+ ] });
1570
1655
  };
1571
1656
 
1572
- // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1657
+ // src/context/providers/AssistantRuntimeProvider.tsx
1658
+ import { jsx as jsx22 } from "react/jsx-runtime";
1659
+ var AssistantRuntimeProviderImpl = ({ children, runtime }) => {
1660
+ return /* @__PURE__ */ jsx22(ThreadProvider, { runtime, children });
1661
+ };
1662
+ var AssistantRuntimeProvider = memo(AssistantRuntimeProviderImpl);
1663
+
1664
+ // src/runtime/vercel-deprecated/VercelAIAssistantProvider.tsx
1573
1665
  import { jsx as jsx23 } from "react/jsx-runtime";
1666
+ var VercelUseChatRuntimeProvider = ({
1667
+ chat,
1668
+ children
1669
+ }) => {
1670
+ const runtime = useVercelUseChatRuntime(chat);
1671
+ return /* @__PURE__ */ jsx23(AssistantRuntimeProvider, { runtime, children });
1672
+ };
1673
+ var VercelUseAssistantRuntimeProvider = ({
1674
+ assistant,
1675
+ children
1676
+ }) => {
1677
+ const runtime = useVercelUseAssistantRuntime(assistant);
1678
+ return /* @__PURE__ */ jsx23(AssistantRuntimeProvider, { runtime, children });
1679
+ };
1680
+ var VercelAIAssistantProvider = ({
1681
+ children,
1682
+ ...rest
1683
+ }) => {
1684
+ if ("chat" in rest) {
1685
+ return /* @__PURE__ */ jsx23(VercelUseChatRuntimeProvider, { chat: rest.chat, children });
1686
+ }
1687
+ return /* @__PURE__ */ jsx23(VercelUseAssistantRuntimeProvider, { assistant: rest.assistant, children });
1688
+ };
1689
+
1690
+ // src/runtime/vercel-deprecated/VercelRSCAssistantProvider.tsx
1691
+ import { jsx as jsx24 } from "react/jsx-runtime";
1574
1692
  var VercelRSCAssistantProvider = ({
1575
1693
  children,
1576
1694
  ...adapter
1577
1695
  }) => {
1578
1696
  const runtime = useVercelRSCRuntime(adapter);
1579
- return /* @__PURE__ */ jsx23(AssistantProvider, { runtime, children });
1697
+ return /* @__PURE__ */ jsx24(AssistantRuntimeProvider, { runtime, children });
1580
1698
  };
1581
1699
 
1582
- // src/adapters/core/local/useLocalRuntime.tsx
1583
- import { useInsertionEffect as useInsertionEffect4, useState as useState6 } from "react";
1700
+ // src/runtime/local/useLocalRuntime.tsx
1701
+ import { useInsertionEffect as useInsertionEffect5, useState as useState7 } from "react";
1584
1702
 
1585
- // src/adapters/core/local/LocalRuntime.tsx
1703
+ // src/runtime/local/LocalRuntime.tsx
1586
1704
  var LocalRuntime = class {
1587
1705
  constructor(adapter) {
1588
1706
  this.adapter = adapter;
@@ -1665,16 +1783,16 @@ var LocalRuntime = class {
1665
1783
  }
1666
1784
  };
1667
1785
 
1668
- // src/adapters/core/local/useLocalRuntime.tsx
1786
+ // src/runtime/local/useLocalRuntime.tsx
1669
1787
  var useLocalRuntime = (adapter) => {
1670
- const [runtime] = useState6(() => new LocalRuntime(adapter));
1671
- useInsertionEffect4(() => {
1788
+ const [runtime] = useState7(() => new LocalRuntime(adapter));
1789
+ useInsertionEffect5(() => {
1672
1790
  runtime.adapter = adapter;
1673
1791
  });
1674
1792
  return runtime;
1675
1793
  };
1676
1794
 
1677
- // src/adapters/core/local/vercel/VercelModelAdapter.tsx
1795
+ // src/runtime/local/vercel/VercelModelAdapter.tsx
1678
1796
  import { streamText } from "ai";
1679
1797
  var VercelModelAdapter = class {
1680
1798
  constructor(model) {
@@ -1717,6 +1835,7 @@ var VercelModelAdapter = class {
1717
1835
  };
1718
1836
  export {
1719
1837
  actionBar_exports as ActionBarPrimitive,
1838
+ AssistantRuntimeProvider,
1720
1839
  branchPicker_exports as BranchPickerPrimitive,
1721
1840
  composer_exports as ComposerPrimitive,
1722
1841
  contentPart_exports as ContentPartPrimitive,
@@ -1724,15 +1843,20 @@ export {
1724
1843
  thread_exports as ThreadPrimitive,
1725
1844
  VercelAIAssistantProvider,
1726
1845
  VercelRSCAssistantProvider,
1727
- AssistantProvider as unstable_AssistantProvider,
1846
+ getVercelAIMessage,
1847
+ getVercelRSCMessage,
1728
1848
  VercelModelAdapter as unstable_VercelModelAdapter,
1729
- getVercelMessage as unstable_getVercelMessage,
1730
- getVercelRSCMessage as unstable_getVercelRSCMessage,
1849
+ useComposerContext as unstable_useComposerContext,
1850
+ useContentPartContext as unstable_useContentPartContext,
1731
1851
  useLocalRuntime as unstable_useLocalRuntime,
1732
1852
  useMessageContext as unstable_useMessageContext,
1853
+ useThreadContext as unstable_useThreadContext,
1733
1854
  useBeginMessageEdit,
1734
1855
  useCopyMessage,
1735
1856
  useGoToNextBranch,
1736
1857
  useGoToPreviousBranch,
1737
- useReloadMessage
1858
+ useReloadMessage,
1859
+ useVercelRSCRuntime,
1860
+ useVercelUseAssistantRuntime,
1861
+ useVercelUseChatRuntime
1738
1862
  };