@assistant-ui/react 0.0.7 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.js CHANGED
@@ -53,6 +53,7 @@ __export(thread_exports, {
53
53
  If: () => ThreadIf,
54
54
  Messages: () => ThreadMessages,
55
55
  Root: () => ThreadRoot,
56
+ ScrollToBottom: () => ThreadScrollToBottom,
56
57
  Viewport: () => ThreadViewport
57
58
  });
58
59
 
@@ -106,7 +107,7 @@ var ThreadEmpty = ({ children }) => {
106
107
  var import_primitive = require("@radix-ui/primitive");
107
108
  var import_react_compose_refs = require("@radix-ui/react-compose-refs");
108
109
  var import_react_primitive2 = require("@radix-ui/react-primitive");
109
- var import_react4 = require("react");
110
+ var import_react5 = require("react");
110
111
 
111
112
  // src/utils/hooks/useOnResizeContent.tsx
112
113
  var import_react3 = require("react");
@@ -147,22 +148,55 @@ var useOnResizeContent = (ref, callback) => {
147
148
  }, [ref.current]);
148
149
  };
149
150
 
151
+ // src/utils/hooks/useOnScrollToBottom.tsx
152
+ var import_react4 = require("react");
153
+ var useOnScrollToBottom = (callback) => {
154
+ const callbackRef = (0, import_react4.useRef)(callback);
155
+ callbackRef.current = callback;
156
+ const { useThread } = useAssistantContext();
157
+ (0, import_react4.useEffect)(() => {
158
+ return useThread.getState().onScrollToBottom(() => {
159
+ callbackRef.current();
160
+ });
161
+ }, [useThread]);
162
+ };
163
+
150
164
  // src/primitives/thread/ThreadViewport.tsx
151
- var ThreadViewport = (0, import_react4.forwardRef)(({ onScroll, children, ...rest }, forwardedRef) => {
152
- const divRef = (0, import_react4.useRef)(null);
165
+ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScroll, children, ...rest }, forwardedRef) => {
166
+ const messagesEndRef = (0, import_react5.useRef)(null);
167
+ const divRef = (0, import_react5.useRef)(null);
153
168
  const ref = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, divRef);
154
- const [isAtBottom, setIsAtBottom] = (0, import_react4.useState)(true);
169
+ const { useThread } = useAssistantContext();
170
+ const firstRenderRef = (0, import_react5.useRef)(true);
171
+ const lastScrollTop = (0, import_react5.useRef)(0);
172
+ const scrollToBottom = () => {
173
+ const div = messagesEndRef.current;
174
+ if (!div || !autoScroll)
175
+ return;
176
+ const behavior = firstRenderRef.current ? "instant" : "auto";
177
+ firstRenderRef.current = false;
178
+ useThread.setState({ isAtBottom: true });
179
+ div.scrollIntoView({ behavior });
180
+ };
155
181
  useOnResizeContent(divRef, () => {
156
- const div = divRef.current;
157
- if (!div || !isAtBottom)
182
+ if (!useThread.getState().isAtBottom)
158
183
  return;
159
- div.scrollTop = div.scrollHeight;
184
+ scrollToBottom();
185
+ });
186
+ useOnScrollToBottom(() => {
187
+ scrollToBottom();
160
188
  });
161
189
  const handleScroll = () => {
162
190
  const div = divRef.current;
163
191
  if (!div)
164
192
  return;
165
- setIsAtBottom(div.scrollHeight - div.scrollTop <= div.clientHeight + 50);
193
+ const isAtBottom = useThread.getState().isAtBottom;
194
+ const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
195
+ if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
196
+ } else if (newIsAtBottom !== isAtBottom) {
197
+ useThread.setState({ isAtBottom: newIsAtBottom });
198
+ }
199
+ lastScrollTop.current = div.scrollTop;
166
200
  };
167
201
  return /* @__PURE__ */ React.createElement(
168
202
  import_react_primitive2.Primitive.div,
@@ -171,19 +205,24 @@ var ThreadViewport = (0, import_react4.forwardRef)(({ onScroll, children, ...res
171
205
  onScroll: (0, import_primitive.composeEventHandlers)(onScroll, handleScroll),
172
206
  ref
173
207
  },
174
- children
208
+ children,
209
+ /* @__PURE__ */ React.createElement("div", { ref: messagesEndRef })
175
210
  );
176
211
  });
177
212
 
178
- // src/vercel/useVercelAIBranches.tsx
179
- var import_react5 = require("react");
180
- var ROOT_ID = "__ROOT_ID__";
213
+ // src/adapters/vercel/useVercelAIBranches.tsx
214
+ var import_react6 = require("react");
215
+
216
+ // src/utils/context/stores/AssistantTypes.ts
217
+ var ROOT_PARENT_ID = "__ROOT_ID__";
218
+
219
+ // src/adapters/vercel/useVercelAIBranches.tsx
181
220
  var UPCOMING_MESSAGE_ID = "__UPCOMING_MESSAGE_ID__";
182
221
  var updateBranchData = (data, messages) => {
183
222
  for (let i = 0; i < messages.length; i++) {
184
223
  const child = messages[i];
185
224
  const childId = child.id;
186
- const parentId = messages[i - 1]?.id ?? ROOT_ID;
225
+ const parentId = messages[i - 1]?.id ?? ROOT_PARENT_ID;
187
226
  data.parentMap.set(childId, parentId);
188
227
  const parentArray = data.branchMap.get(parentId);
189
228
  if (!parentArray) {
@@ -194,32 +233,32 @@ var updateBranchData = (data, messages) => {
194
233
  data.snapshots.set(childId, messages);
195
234
  }
196
235
  };
197
- var getParentId = (data, messages, message) => {
198
- if (message.id === UPCOMING_MESSAGE_ID) {
236
+ var getParentId = (data, messages, messageId) => {
237
+ if (messageId === UPCOMING_MESSAGE_ID) {
199
238
  const parent = messages.at(-1);
200
239
  if (!parent)
201
- return ROOT_ID;
240
+ return ROOT_PARENT_ID;
202
241
  return parent.id;
203
242
  }
204
- const parentId = data.parentMap.get(message.id);
243
+ const parentId = data.parentMap.get(messageId);
205
244
  if (!parentId)
206
245
  throw new Error("Unexpected: Message parent not found");
207
246
  return parentId;
208
247
  };
209
- var getBranchStateImpl = (data, messages, message) => {
210
- const parentId = getParentId(data, messages, message);
248
+ var getBranchStateImpl = (data, messages, messageId) => {
249
+ const parentId = getParentId(data, messages, messageId);
211
250
  const branches = data.branchMap.get(parentId) ?? [];
212
- const branchId = message.id === UPCOMING_MESSAGE_ID ? branches.length : branches.indexOf(message.id);
251
+ const branchId = messageId === UPCOMING_MESSAGE_ID ? branches.length : branches.indexOf(messageId);
213
252
  if (branchId === -1)
214
253
  throw new Error("Unexpected: Message not found in parent children");
215
- const upcomingOffset = message.id === UPCOMING_MESSAGE_ID ? 1 : 0;
254
+ const upcomingOffset = messageId === UPCOMING_MESSAGE_ID ? 1 : 0;
216
255
  return {
217
256
  branchId,
218
257
  branchCount: branches.length + upcomingOffset
219
258
  };
220
259
  };
221
- var switchToBranchImpl = (data, messages, message, branchId) => {
222
- const parentId = getParentId(data, messages, message);
260
+ var switchToBranchImpl = (data, messages, messageId, branchId) => {
261
+ const parentId = getParentId(data, messages, messageId);
223
262
  const branches = data.branchMap.get(parentId);
224
263
  if (!branches)
225
264
  throw new Error("Unexpected: Parent children not found");
@@ -228,40 +267,42 @@ var switchToBranchImpl = (data, messages, message, branchId) => {
228
267
  throw new Error("Unexpected: Requested branch not found");
229
268
  if (branchId < 0 || branchId >= branches.length)
230
269
  throw new Error("Switch to branch called with a branch index out of range");
231
- if (newMessageId === message.id)
270
+ if (newMessageId === messageId)
232
271
  return messages;
233
272
  const snapshot = data.snapshots.get(newMessageId);
234
273
  if (!snapshot)
235
274
  throw new Error("Unexpected: Branch snapshot not found");
236
275
  return snapshot;
237
276
  };
238
- var sliceMessagesUntil = (messages, message) => {
239
- if (message.id === UPCOMING_MESSAGE_ID)
277
+ var sliceMessagesUntil = (messages, messageId) => {
278
+ if (messageId === ROOT_PARENT_ID)
279
+ return [];
280
+ if (messageId === UPCOMING_MESSAGE_ID)
240
281
  return messages;
241
- const messageIdx = messages.findIndex((m) => m.id === message.id);
282
+ const messageIdx = messages.findIndex((m) => m.id === messageId);
242
283
  if (messageIdx === -1)
243
284
  throw new Error("Unexpected: Message not found");
244
- return messages.slice(0, messageIdx);
285
+ return messages.slice(0, messageIdx + 1);
245
286
  };
246
- var useVercelAIBranches = (chat) => {
247
- const data = (0, import_react5.useRef)({
287
+ var useVercelAIBranches = (chat, context) => {
288
+ const data = (0, import_react6.useRef)({
248
289
  parentMap: /* @__PURE__ */ new Map(),
249
290
  branchMap: /* @__PURE__ */ new Map(),
250
291
  snapshots: /* @__PURE__ */ new Map()
251
292
  }).current;
252
293
  updateBranchData(data, chat.messages);
253
- const getBranchState = (0, import_react5.useCallback)(
254
- (message) => {
255
- return getBranchStateImpl(data, chat.messages, message);
294
+ const getBranchState = (0, import_react6.useCallback)(
295
+ (messageId) => {
296
+ return getBranchStateImpl(data, chat.messages, messageId);
256
297
  },
257
298
  [data, chat.messages]
258
299
  );
259
- const switchToBranch = (0, import_react5.useCallback)(
260
- (message, branchId) => {
300
+ const switchToBranch = (0, import_react6.useCallback)(
301
+ (messageId, branchId) => {
261
302
  const newMessages = switchToBranchImpl(
262
303
  data,
263
304
  chat.messages,
264
- message,
305
+ messageId,
265
306
  branchId
266
307
  );
267
308
  chat.setMessages(newMessages);
@@ -269,61 +310,66 @@ var useVercelAIBranches = (chat) => {
269
310
  [data, chat.messages, chat.setMessages]
270
311
  );
271
312
  const reloadMaybe = "reload" in chat ? chat.reload : void 0;
272
- const reloadAt = (0, import_react5.useCallback)(
273
- async (message) => {
313
+ const reload = (0, import_react6.useCallback)(
314
+ async (messageId) => {
274
315
  if (!reloadMaybe)
275
316
  throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
276
- const newMessages = sliceMessagesUntil(chat.messages, message);
317
+ const newMessages = sliceMessagesUntil(chat.messages, messageId);
277
318
  chat.setMessages(newMessages);
319
+ context.useThread.getState().scrollToBottom();
278
320
  await reloadMaybe();
279
321
  },
280
- [chat.messages, chat.setMessages, reloadMaybe]
322
+ [context, chat.messages, chat.setMessages, reloadMaybe]
281
323
  );
282
- const editAt = (0, import_react5.useCallback)(
283
- async (message, newMessage) => {
284
- const newMessages = sliceMessagesUntil(chat.messages, message);
324
+ const append = (0, import_react6.useCallback)(
325
+ async (message) => {
326
+ const newMessages = sliceMessagesUntil(chat.messages, message.parentId);
285
327
  chat.setMessages(newMessages);
286
- if (newMessage.content[0]?.type !== "text")
328
+ if (message.content.length !== 1 || message.content[0]?.type !== "text")
287
329
  throw new Error("Only text content is currently supported");
330
+ context.useThread.getState().scrollToBottom();
288
331
  await chat.append({
289
332
  role: "user",
290
- content: newMessage.content[0].text
333
+ content: message.content[0].text
291
334
  });
292
335
  },
293
- [chat.messages, chat.setMessages, chat.append]
336
+ [context, chat.messages, chat.setMessages, chat.append]
294
337
  );
295
- return (0, import_react5.useMemo)(
338
+ return (0, import_react6.useMemo)(
296
339
  () => ({
297
340
  getBranchState,
298
341
  switchToBranch,
299
- editAt,
300
- reloadAt
342
+ append,
343
+ reload
301
344
  }),
302
- [getBranchState, switchToBranch, editAt, reloadAt]
345
+ [getBranchState, switchToBranch, append, reload]
303
346
  );
304
347
  };
305
348
  var hasUpcomingMessage = (thread) => {
306
349
  return thread.isLoading && thread.messages[thread.messages.length - 1]?.role !== "assistant";
307
350
  };
308
351
 
309
- // src/utils/context/ComposerState.ts
310
- var import_react7 = require("react");
352
+ // src/utils/context/useComposerContext.ts
353
+ var import_react8 = require("react");
311
354
 
312
- // src/utils/context/MessageContext.ts
313
- var import_react6 = require("react");
314
- var MessageContext = (0, import_react6.createContext)(null);
355
+ // src/utils/context/useMessageContext.ts
356
+ var import_react7 = require("react");
357
+ var MessageContext = (0, import_react7.createContext)(null);
315
358
  var useMessageContext = () => {
316
- const context = (0, import_react6.useContext)(MessageContext);
359
+ const context = (0, import_react7.useContext)(MessageContext);
317
360
  if (!context)
318
361
  throw new Error("useMessageContext must be used within a MessageProvider");
319
362
  return context;
320
363
  };
321
364
 
322
- // src/utils/context/ComposerState.ts
365
+ // src/utils/context/useComposerContext.ts
323
366
  var useComposerContext = () => {
324
367
  const { useComposer: useAssisstantComposer } = useAssistantContext();
325
- const { useComposer: useMessageComposer } = (0, import_react7.useContext)(MessageContext) ?? {};
326
- return { useComposer: useMessageComposer ?? useAssisstantComposer };
368
+ const { useComposer: useMessageComposer } = (0, import_react8.useContext)(MessageContext) ?? {};
369
+ return {
370
+ useComposer: useMessageComposer ?? useAssisstantComposer,
371
+ type: useMessageComposer ? "message" : "assistant"
372
+ };
327
373
  };
328
374
 
329
375
  // src/primitives/composer/ComposerIf.tsx
@@ -352,16 +398,15 @@ __export(message_exports, {
352
398
  });
353
399
 
354
400
  // src/primitives/message/MessageProvider.tsx
355
- var import_react8 = require("react");
401
+ var import_react9 = require("react");
356
402
  var import_zustand = require("zustand");
357
- var import_shallow = require("zustand/react/shallow");
358
403
  var getIsLast = (thread, message) => {
359
404
  const hasUpcoming = hasUpcomingMessage(thread);
360
405
  return hasUpcoming ? message.id === UPCOMING_MESSAGE_ID : thread.messages[thread.messages.length - 1]?.id === message.id;
361
406
  };
362
407
  var useMessageContext2 = () => {
363
- const { useBranchObserver } = useAssistantContext();
364
- const [context] = (0, import_react8.useState)(() => {
408
+ const [context] = (0, import_react9.useState)(() => {
409
+ const { useThread } = useAssistantContext();
365
410
  const useMessage = (0, import_zustand.create)(() => ({
366
411
  message: null,
367
412
  isLast: false,
@@ -370,10 +415,6 @@ var useMessageContext2 = () => {
370
415
  setIsCopied: () => {
371
416
  },
372
417
  setIsHovering: () => {
373
- },
374
- branchState: {
375
- branchId: 0,
376
- branchCount: 0
377
418
  }
378
419
  }));
379
420
  const useComposer = (0, import_zustand.create)((set, get) => ({
@@ -395,8 +436,8 @@ var useMessageContext2 = () => {
395
436
  const message = useMessage.getState().message;
396
437
  if (message.role !== "user")
397
438
  throw new Error("Editing is only supported for user messages");
398
- useBranchObserver.getState().editAt(message, {
399
- role: "user",
439
+ useThread.getState().append({
440
+ parentId: message.parentId,
400
441
  content: [{ type: "text", text: get().value }]
401
442
  });
402
443
  set({ isEditing: false });
@@ -412,15 +453,12 @@ var MessageProvider = ({
412
453
  message,
413
454
  children
414
455
  }) => {
415
- const { useThread, useBranchObserver } = useAssistantContext();
456
+ const { useThread } = useAssistantContext();
416
457
  const context = useMessageContext2();
417
- const branchState = useBranchObserver(
418
- (0, import_shallow.useShallow)((b) => b.getBranchState(message))
419
- );
420
458
  const isLast = useThread((thread) => getIsLast(thread, message));
421
- const [isCopied, setIsCopied] = (0, import_react8.useState)(false);
422
- const [isHovering, setIsHovering] = (0, import_react8.useState)(false);
423
- (0, import_react8.useMemo)(() => {
459
+ const [isCopied, setIsCopied] = (0, import_react9.useState)(false);
460
+ const [isHovering, setIsHovering] = (0, import_react9.useState)(false);
461
+ (0, import_react9.useMemo)(() => {
424
462
  context.useMessage.setState(
425
463
  {
426
464
  message,
@@ -428,20 +466,19 @@ var MessageProvider = ({
428
466
  isCopied,
429
467
  isHovering,
430
468
  setIsCopied,
431
- setIsHovering,
432
- branchState
469
+ setIsHovering
433
470
  },
434
471
  true
435
472
  );
436
- }, [context, message, isLast, isCopied, isHovering, branchState]);
473
+ }, [context, message, isLast, isCopied, isHovering]);
437
474
  return /* @__PURE__ */ React.createElement(MessageContext.Provider, { value: context }, children);
438
475
  };
439
476
 
440
477
  // src/primitives/message/MessageRoot.tsx
441
478
  var import_primitive2 = require("@radix-ui/primitive");
442
479
  var import_react_primitive3 = require("@radix-ui/react-primitive");
443
- var import_react9 = require("react");
444
- var MessageRoot = (0, import_react9.forwardRef)(
480
+ var import_react10 = require("react");
481
+ var MessageRoot = (0, import_react10.forwardRef)(
445
482
  ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
446
483
  const { useMessage } = useMessageContext();
447
484
  const setIsHovering = useMessage((s) => s.setIsHovering);
@@ -466,29 +503,21 @@ var MessageRoot = (0, import_react9.forwardRef)(
466
503
  // src/primitives/message/MessageIf.tsx
467
504
  var useMessageIf = (props) => {
468
505
  const { useMessage } = useMessageContext();
469
- return useMessage(
470
- ({
471
- message,
472
- isLast,
473
- isCopied,
474
- isHovering,
475
- branchState: { branchCount }
476
- }) => {
477
- if (props.hasBranches === true && branchCount < 2)
478
- return false;
479
- if (props.user && message.role !== "user")
480
- return false;
481
- if (props.assistant && message.role !== "assistant")
482
- return false;
483
- if (props.lastOrHover === true && !isHovering && !isLast)
484
- return false;
485
- if (props.copied === true && !isCopied)
486
- return false;
487
- if (props.copied === false && isCopied)
488
- return false;
489
- return true;
490
- }
491
- );
506
+ return useMessage(({ message, isLast, isCopied, isHovering }) => {
507
+ if (props.hasBranches === true && message.branchCount < 2)
508
+ return false;
509
+ if (props.user && message.role !== "user")
510
+ return false;
511
+ if (props.assistant && message.role !== "assistant")
512
+ return false;
513
+ if (props.lastOrHover === true && !isHovering && !isLast)
514
+ return false;
515
+ if (props.copied === true && !isCopied)
516
+ return false;
517
+ if (props.copied === false && isCopied)
518
+ return false;
519
+ return true;
520
+ });
492
521
  };
493
522
  var MessageIf = ({ children, ...query }) => {
494
523
  const result = useMessageIf(query);
@@ -559,13 +588,40 @@ var ThreadMessages = ({ components }) => {
559
588
  message: {
560
589
  id: UPCOMING_MESSAGE_ID,
561
590
  role: "assistant",
562
- content: [{ type: "text", text: "..." }]
591
+ content: [{ type: "text", text: "..." }],
592
+ parentId: messages.at(-1)?.id ?? ROOT_PARENT_ID,
593
+ // TODO fix these (move upcoming message to AssistantContext)
594
+ branchId: 0,
595
+ branchCount: 1,
596
+ createdAt: /* @__PURE__ */ new Date()
563
597
  }
564
598
  },
565
599
  /* @__PURE__ */ React.createElement(AssistantMessage, null)
566
600
  ));
567
601
  };
568
602
 
603
+ // src/primitives/thread/ThreadScrollToBottom.tsx
604
+ var import_primitive3 = require("@radix-ui/primitive");
605
+ var import_react_primitive4 = require("@radix-ui/react-primitive");
606
+ var import_react11 = require("react");
607
+ var ThreadScrollToBottom = (0, import_react11.forwardRef)(({ onClick, ...rest }, ref) => {
608
+ const { useThread } = useAssistantContext();
609
+ const isAtBottom = useThread((s) => s.isAtBottom);
610
+ const handleScrollToBottom = () => {
611
+ const thread = useThread.getState();
612
+ thread.scrollToBottom();
613
+ };
614
+ return /* @__PURE__ */ React.createElement(
615
+ import_react_primitive4.Primitive.button,
616
+ {
617
+ ...rest,
618
+ disabled: isAtBottom,
619
+ ref,
620
+ onClick: (0, import_primitive3.composeEventHandlers)(onClick, handleScrollToBottom)
621
+ }
622
+ );
623
+ });
624
+
569
625
  // src/primitives/composer/index.ts
570
626
  var composer_exports = {};
571
627
  __export(composer_exports, {
@@ -577,33 +633,15 @@ __export(composer_exports, {
577
633
  });
578
634
 
579
635
  // src/primitives/composer/ComposerRoot.tsx
580
- var import_primitive3 = require("@radix-ui/primitive");
636
+ var import_primitive4 = require("@radix-ui/primitive");
581
637
  var import_react_compose_refs2 = require("@radix-ui/react-compose-refs");
582
- var import_react_primitive4 = require("@radix-ui/react-primitive");
583
- var import_react10 = require("react");
584
- var ComposerFormContext = (0, import_react10.createContext)(null);
585
- var useComposerFormContext = () => {
586
- const context = (0, import_react10.useContext)(ComposerFormContext);
587
- if (!context) {
588
- throw new Error(
589
- "Composer compound components cannot be rendered outside the Composer component"
590
- );
591
- }
592
- return context;
593
- };
594
- var ComposerRoot = (0, import_react10.forwardRef)(
638
+ var import_react_primitive5 = require("@radix-ui/react-primitive");
639
+ var import_react12 = require("react");
640
+ var ComposerRoot = (0, import_react12.forwardRef)(
595
641
  ({ onSubmit, ...rest }, forwardedRef) => {
596
642
  const { useComposer } = useComposerContext();
597
- const formRef = (0, import_react10.useRef)(null);
643
+ const formRef = (0, import_react12.useRef)(null);
598
644
  const ref = (0, import_react_compose_refs2.useComposedRefs)(forwardedRef, formRef);
599
- const composerContextValue = (0, import_react10.useMemo)(
600
- () => ({
601
- submit: () => formRef.current?.dispatchEvent(
602
- new Event("submit", { cancelable: true, bubbles: true })
603
- )
604
- }),
605
- []
606
- );
607
645
  const handleSubmit = (e) => {
608
646
  const composerState = useComposer.getState();
609
647
  if (!composerState.isEditing)
@@ -611,73 +649,96 @@ var ComposerRoot = (0, import_react10.forwardRef)(
611
649
  e.preventDefault();
612
650
  composerState.send();
613
651
  };
614
- return /* @__PURE__ */ React.createElement(ComposerFormContext.Provider, { value: composerContextValue }, /* @__PURE__ */ React.createElement(
615
- import_react_primitive4.Primitive.form,
652
+ return /* @__PURE__ */ React.createElement(
653
+ import_react_primitive5.Primitive.form,
616
654
  {
617
655
  ...rest,
618
656
  ref,
619
- onSubmit: (0, import_primitive3.composeEventHandlers)(onSubmit, handleSubmit)
657
+ onSubmit: (0, import_primitive4.composeEventHandlers)(onSubmit, handleSubmit)
620
658
  }
621
- ));
659
+ );
622
660
  }
623
661
  );
624
662
 
625
663
  // src/primitives/composer/ComposerInput.tsx
626
- var import_primitive4 = require("@radix-ui/primitive");
664
+ var import_primitive5 = require("@radix-ui/primitive");
665
+ var import_react_compose_refs3 = require("@radix-ui/react-compose-refs");
627
666
  var import_react_slot = require("@radix-ui/react-slot");
628
- var import_react11 = require("react");
667
+ var import_react13 = require("react");
629
668
  var import_react_textarea_autosize = __toESM(require("react-textarea-autosize"));
630
- var ComposerInput = (0, import_react11.forwardRef)(({ asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
631
- const { useThread } = useAssistantContext();
632
- const isLoading = useThread((t) => t.isLoading);
633
- const { useComposer } = useComposerContext();
634
- const value = useComposer((c) => {
635
- if (!c.isEditing)
636
- return "";
637
- return c.value;
638
- });
639
- const Component = asChild ? import_react_slot.Slot : import_react_textarea_autosize.default;
640
- const composerForm = useComposerFormContext();
641
- const handleKeyPress = (e) => {
642
- if (disabled)
643
- return;
644
- if (e.key === "Escape") {
645
- useComposer.getState().cancel();
646
- }
647
- if (isLoading)
648
- return;
649
- if (e.key === "Enter" && e.shiftKey === false) {
650
- e.preventDefault();
651
- composerForm.submit();
652
- }
653
- };
654
- return /* @__PURE__ */ React.createElement(
655
- Component,
656
- {
657
- value,
658
- ...rest,
659
- ref: forwardedRef,
660
- disabled,
661
- onChange: (0, import_primitive4.composeEventHandlers)(onChange, (e) => {
662
- const composerState = useComposer.getState();
663
- if (!composerState.isEditing)
664
- return;
665
- return composerState.setValue(e.target.value);
666
- }),
667
- onKeyDown: (0, import_primitive4.composeEventHandlers)(onKeyDown, handleKeyPress)
668
- }
669
- );
670
- });
669
+ var ComposerInput = (0, import_react13.forwardRef)(
670
+ ({ autoFocus, asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
671
+ const { useThread } = useAssistantContext();
672
+ const { useComposer, type } = useComposerContext();
673
+ const value = useComposer((c) => {
674
+ if (!c.isEditing)
675
+ return "";
676
+ return c.value;
677
+ });
678
+ const Component = asChild ? import_react_slot.Slot : import_react_textarea_autosize.default;
679
+ const handleKeyPress = (e) => {
680
+ if (disabled)
681
+ return;
682
+ const composer = useComposer.getState();
683
+ if (e.key === "Escape" && composer.canCancel) {
684
+ e.preventDefault();
685
+ useComposer.getState().cancel();
686
+ }
687
+ if (e.key === "Enter" && e.shiftKey === false) {
688
+ const isLoading = useThread.getState().isLoading;
689
+ if (!isLoading) {
690
+ e.preventDefault();
691
+ composer.send();
692
+ }
693
+ }
694
+ };
695
+ const textareaRef = (0, import_react13.useRef)(null);
696
+ const ref = (0, import_react_compose_refs3.useComposedRefs)(forwardedRef, textareaRef);
697
+ const autoFocusEnabled = autoFocus !== false && !disabled;
698
+ const focus = (0, import_react13.useCallback)(() => {
699
+ const textarea = textareaRef.current;
700
+ if (!textarea || !autoFocusEnabled)
701
+ return;
702
+ textarea.focus();
703
+ textarea.setSelectionRange(
704
+ textareaRef.current.value.length,
705
+ textareaRef.current.value.length
706
+ );
707
+ }, [autoFocusEnabled]);
708
+ (0, import_react13.useEffect)(() => focus(), [focus]);
709
+ useOnScrollToBottom(() => {
710
+ if (type === "assistant") {
711
+ focus();
712
+ }
713
+ });
714
+ return /* @__PURE__ */ React.createElement(
715
+ Component,
716
+ {
717
+ value,
718
+ ...rest,
719
+ ref,
720
+ disabled,
721
+ onChange: (0, import_primitive5.composeEventHandlers)(onChange, (e) => {
722
+ const composerState = useComposer.getState();
723
+ if (!composerState.isEditing)
724
+ return;
725
+ return composerState.setValue(e.target.value);
726
+ }),
727
+ onKeyDown: (0, import_primitive5.composeEventHandlers)(onKeyDown, handleKeyPress)
728
+ }
729
+ );
730
+ }
731
+ );
671
732
 
672
733
  // src/primitives/composer/ComposerSend.tsx
673
- var import_react_primitive5 = require("@radix-ui/react-primitive");
674
- var import_react12 = require("react");
675
- var ComposerSend = (0, import_react12.forwardRef)(
734
+ var import_react_primitive6 = require("@radix-ui/react-primitive");
735
+ var import_react14 = require("react");
736
+ var ComposerSend = (0, import_react14.forwardRef)(
676
737
  ({ disabled, ...rest }, ref) => {
677
738
  const { useComposer } = useComposerContext();
678
739
  const hasValue = useComposer((c) => c.isEditing && c.value.length > 0);
679
740
  return /* @__PURE__ */ React.createElement(
680
- import_react_primitive5.Primitive.button,
741
+ import_react_primitive6.Primitive.button,
681
742
  {
682
743
  type: "submit",
683
744
  ...rest,
@@ -689,22 +750,22 @@ var ComposerSend = (0, import_react12.forwardRef)(
689
750
  );
690
751
 
691
752
  // src/primitives/composer/ComposerCancel.tsx
692
- var import_primitive5 = require("@radix-ui/primitive");
693
- var import_react_primitive6 = require("@radix-ui/react-primitive");
694
- var import_react13 = require("react");
695
- var ComposerCancel = (0, import_react13.forwardRef)(({ disabled, onClick, ...rest }, ref) => {
753
+ var import_primitive6 = require("@radix-ui/primitive");
754
+ var import_react_primitive7 = require("@radix-ui/react-primitive");
755
+ var import_react15 = require("react");
756
+ var ComposerCancel = (0, import_react15.forwardRef)(({ disabled, onClick, ...rest }, ref) => {
696
757
  const { useComposer } = useComposerContext();
697
758
  const hasValue = useComposer((c) => c.canCancel);
698
759
  const handleClose = () => {
699
760
  useComposer.getState().cancel();
700
761
  };
701
762
  return /* @__PURE__ */ React.createElement(
702
- import_react_primitive6.Primitive.button,
763
+ import_react_primitive7.Primitive.button,
703
764
  {
704
765
  type: "button",
705
766
  ...rest,
706
767
  ref,
707
- onClick: (0, import_primitive5.composeEventHandlers)(onClick, handleClose),
768
+ onClick: (0, import_primitive6.composeEventHandlers)(onClick, handleClose),
708
769
  disabled: disabled || !hasValue
709
770
  }
710
771
  );
@@ -720,42 +781,64 @@ __export(branchPicker_exports, {
720
781
  Root: () => BranchPickerRoot
721
782
  });
722
783
 
784
+ // src/utils/context/combined/useCombinedStore.ts
785
+ var import_react17 = require("react");
786
+
787
+ // src/utils/context/combined/createCombinedStore.ts
788
+ var import_react16 = require("react");
789
+ var createCombinedStore = (stores) => {
790
+ const subscribe = (callback) => {
791
+ const unsubscribes = stores.map((store) => store.subscribe(callback));
792
+ return () => {
793
+ for (const unsub of unsubscribes) {
794
+ unsub();
795
+ }
796
+ };
797
+ };
798
+ return (selector) => {
799
+ const getSnapshot = () => selector(...stores.map((store) => store.getState()));
800
+ return (0, import_react16.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
801
+ };
802
+ };
803
+
804
+ // src/utils/context/combined/useCombinedStore.ts
805
+ var useCombinedStore = (stores, selector) => {
806
+ const useCombined = (0, import_react17.useMemo)(() => createCombinedStore(stores), stores);
807
+ return useCombined(selector);
808
+ };
809
+
723
810
  // src/actions/useGoToNextBranch.tsx
724
811
  var useGoToNextBranch = () => {
725
- const { useThread, useBranchObserver } = useAssistantContext();
812
+ const { useThread } = useAssistantContext();
726
813
  const { useComposer, useMessage } = useMessageContext();
727
- const isLoading = useThread((s) => s.isLoading);
728
- const isEditing = useComposer((s) => s.isEditing);
729
- const hasNext = useMessage(
730
- ({ branchState: { branchId, branchCount } }) => branchId + 1 < branchCount
814
+ const disabled = useCombinedStore(
815
+ [useThread, useComposer, useMessage],
816
+ (t, c, m) => t.isLoading || c.isEditing || m.message.branchId + 1 >= m.message.branchCount
731
817
  );
732
- if (isLoading || isEditing || !hasNext)
818
+ if (disabled)
733
819
  return null;
734
820
  return () => {
735
- const {
736
- message,
737
- branchState: { branchId }
738
- } = useMessage.getState();
739
- useBranchObserver.getState().switchToBranch(message, branchId + 1);
821
+ const { message } = useMessage.getState();
822
+ useThread.getState().switchToBranch(message.id, message.branchId + 1);
740
823
  };
741
824
  };
742
825
 
743
826
  // src/utils/createActionButton.tsx
744
- var import_react14 = require("react");
745
- var import_react_primitive7 = require("@radix-ui/react-primitive");
746
- var import_primitive6 = require("@radix-ui/primitive");
827
+ var import_react18 = require("react");
828
+ var import_react_primitive8 = require("@radix-ui/react-primitive");
829
+ var import_primitive7 = require("@radix-ui/primitive");
747
830
  var createActionButton = (useActionButton) => {
748
- return (0, import_react14.forwardRef)(
831
+ return (0, import_react18.forwardRef)(
749
832
  (props, forwardedRef) => {
750
833
  const onClick = useActionButton(props);
751
834
  return /* @__PURE__ */ React.createElement(
752
- import_react_primitive7.Primitive.button,
835
+ import_react_primitive8.Primitive.button,
753
836
  {
754
837
  type: "button",
755
838
  disabled: !onClick,
756
839
  ...props,
757
840
  ref: forwardedRef,
758
- onClick: (0, import_primitive6.composeEventHandlers)(props.onClick, onClick ?? void 0)
841
+ onClick: (0, import_primitive7.composeEventHandlers)(props.onClick, onClick ?? void 0)
759
842
  }
760
843
  );
761
844
  }
@@ -767,19 +850,17 @@ var BranchPickerNext = createActionButton(useGoToNextBranch);
767
850
 
768
851
  // src/actions/useGoToPreviousBranch.tsx
769
852
  var useGoToPreviousBranch = () => {
770
- const { useThread, useBranchObserver } = useAssistantContext();
853
+ const { useThread } = useAssistantContext();
771
854
  const { useComposer, useMessage } = useMessageContext();
772
- const isLoading = useThread((s) => s.isLoading);
773
- const isEditing = useComposer((s) => s.isEditing);
774
- const hasNext = useMessage(({ branchState: { branchId } }) => branchId > 0);
775
- if (isLoading || isEditing || !hasNext)
855
+ const disabled = useCombinedStore(
856
+ [useThread, useComposer, useMessage],
857
+ (t, c, m) => t.isLoading || c.isEditing || m.message.branchId <= 0
858
+ );
859
+ if (disabled)
776
860
  return null;
777
861
  return () => {
778
- const {
779
- message,
780
- branchState: { branchId }
781
- } = useMessage.getState();
782
- useBranchObserver.getState().switchToBranch(message, branchId - 1);
862
+ const { message } = useMessage.getState();
863
+ useThread.getState().switchToBranch(message.id, message.branchId - 1);
783
864
  };
784
865
  };
785
866
 
@@ -789,22 +870,22 @@ var BranchPickerPrevious = createActionButton(useGoToPreviousBranch);
789
870
  // src/primitives/branchPicker/BranchPickerCount.tsx
790
871
  var BranchPickerCount = () => {
791
872
  const { useMessage } = useMessageContext();
792
- const branchCount = useMessage((s) => s.branchState.branchCount);
873
+ const branchCount = useMessage((s) => s.message.branchCount);
793
874
  return /* @__PURE__ */ React.createElement(React.Fragment, null, branchCount);
794
875
  };
795
876
 
796
877
  // src/primitives/branchPicker/BranchPickerNumber.tsx
797
878
  var BranchPickerNumber = () => {
798
879
  const { useMessage } = useMessageContext();
799
- const branchId = useMessage((s) => s.branchState.branchId);
880
+ const branchId = useMessage((s) => s.message.branchId);
800
881
  return /* @__PURE__ */ React.createElement(React.Fragment, null, branchId + 1);
801
882
  };
802
883
 
803
884
  // src/primitives/branchPicker/BranchPickerRoot.tsx
804
- var import_react_primitive8 = require("@radix-ui/react-primitive");
805
- var import_react15 = require("react");
806
- var BranchPickerRoot = (0, import_react15.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
807
- return /* @__PURE__ */ React.createElement(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0 }, /* @__PURE__ */ React.createElement(import_react_primitive8.Primitive.div, { ...rest, ref }));
885
+ var import_react_primitive9 = require("@radix-ui/react-primitive");
886
+ var import_react19 = require("react");
887
+ var BranchPickerRoot = (0, import_react19.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
888
+ return /* @__PURE__ */ React.createElement(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0 }, /* @__PURE__ */ React.createElement(import_react_primitive9.Primitive.div, { ...rest, ref }));
808
889
  });
809
890
 
810
891
  // src/primitives/actionBar/index.ts
@@ -817,28 +898,30 @@ __export(actionBar_exports, {
817
898
  });
818
899
 
819
900
  // src/primitives/actionBar/ActionBarRoot.tsx
820
- var import_react_primitive9 = require("@radix-ui/react-primitive");
821
- var import_react16 = require("react");
822
- var ActionBarRoot = (0, import_react16.forwardRef)(({ hideWhenBusy, autohide, autohideFloat, ...rest }, ref) => {
901
+ var import_react_primitive10 = require("@radix-ui/react-primitive");
902
+ var import_react20 = require("react");
903
+ var ActionBarRoot = (0, import_react20.forwardRef)(({ hideWhenBusy, autohide, autohideFloat, ...rest }, ref) => {
823
904
  const { useThread } = useAssistantContext();
824
905
  const { useMessage } = useMessageContext();
825
- const hideAndfloatStatus = useMessage((m) => {
826
- const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
827
- if (!autohideEnabled)
906
+ const hideAndfloatStatus = useCombinedStore(
907
+ [useThread, useMessage],
908
+ (t, m) => {
909
+ if (hideWhenBusy && t.isLoading)
910
+ return "hidden" /* Hidden */;
911
+ const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
912
+ if (!autohideEnabled)
913
+ return "normal" /* Normal */;
914
+ if (!m.isHovering)
915
+ return "hidden" /* Hidden */;
916
+ if (autohideFloat === "always" || autohideFloat === "single-branch" && m.message.branchCount <= 1)
917
+ return "floating" /* Floating */;
828
918
  return "normal" /* Normal */;
829
- if (!m.isHovering)
830
- return "hidden" /* Hidden */;
831
- if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branchState.branchCount <= 1)
832
- return "floating" /* Floating */;
833
- return "normal" /* Normal */;
834
- });
835
- const busy = useThread((t) => t.isLoading);
836
- if (hideWhenBusy && busy)
837
- return null;
919
+ }
920
+ );
838
921
  if (hideAndfloatStatus === "hidden" /* Hidden */)
839
922
  return null;
840
923
  return /* @__PURE__ */ React.createElement(
841
- import_react_primitive9.Primitive.div,
924
+ import_react_primitive10.Primitive.div,
842
925
  {
843
926
  "data-floating": hideAndfloatStatus === "floating" /* Floating */,
844
927
  ...rest,
@@ -868,17 +951,19 @@ var ActionBarCopy = createActionButton(useCopyMessage);
868
951
 
869
952
  // src/actions/useReloadMessage.tsx
870
953
  var useReloadMessage = () => {
871
- const { useThread, useBranchObserver } = useAssistantContext();
954
+ const { useThread } = useAssistantContext();
872
955
  const { useMessage } = useMessageContext();
873
- const isLoading = useThread((s) => s.isLoading);
874
- const isAssistant = useMessage((s) => s.message.role === "assistant");
875
- if (isLoading || !isAssistant)
956
+ const disabled = useCombinedStore(
957
+ [useThread, useMessage],
958
+ (t, m) => t.isLoading || m.message.role !== "assistant"
959
+ );
960
+ if (disabled)
876
961
  return null;
877
962
  return () => {
878
963
  const message = useMessage.getState().message;
879
964
  if (message.role !== "assistant")
880
965
  throw new Error("Reloading is only supported on assistant messages");
881
- useBranchObserver.getState().reloadAt(message);
966
+ useThread.getState().reload(message.id);
882
967
  };
883
968
  };
884
969
 
@@ -888,9 +973,11 @@ var ActionBarReload = createActionButton(useReloadMessage);
888
973
  // src/actions/useBeginMessageEdit.tsx
889
974
  var useBeginMessageEdit = () => {
890
975
  const { useMessage, useComposer } = useMessageContext();
891
- const isUser = useMessage((s) => s.message.role === "user");
892
- const isEditing = useComposer((s) => s.isEditing);
893
- if (!isUser || isEditing)
976
+ const disabled = useCombinedStore(
977
+ [useMessage, useComposer],
978
+ (m, c) => m.message.role !== "user" || c.isEditing
979
+ );
980
+ if (disabled)
894
981
  return null;
895
982
  return () => {
896
983
  const { edit } = useComposer.getState();
@@ -901,25 +988,41 @@ var useBeginMessageEdit = () => {
901
988
  // src/primitives/actionBar/ActionBarEdit.tsx
902
989
  var ActionBarEdit = createActionButton(useBeginMessageEdit);
903
990
 
904
- // src/vercel/VercelAIAssistantProvider.tsx
905
- var import_react18 = require("react");
991
+ // src/adapters/vercel/VercelAIAssistantProvider.tsx
992
+ var import_react22 = require("react");
906
993
 
907
- // src/vercel/useDummyAIAssistantContext.tsx
908
- var import_react17 = require("react");
994
+ // src/adapters/vercel/useDummyAIAssistantContext.tsx
995
+ var import_react21 = require("react");
909
996
  var import_zustand2 = require("zustand");
910
997
  var useDummyAIAssistantContext = () => {
911
- const [context] = (0, import_react17.useState)(() => {
998
+ const [context] = (0, import_react21.useState)(() => {
999
+ const scrollToBottomListeners = /* @__PURE__ */ new Set();
912
1000
  const useThread = (0, import_zustand2.create)()(() => ({
913
1001
  messages: [],
914
1002
  isLoading: false,
915
- reload: async () => {
916
- throw new Error("Not implemented");
917
- },
918
1003
  append: async () => {
919
1004
  throw new Error("Not implemented");
920
1005
  },
921
1006
  stop: () => {
922
1007
  throw new Error("Not implemented");
1008
+ },
1009
+ switchToBranch: () => {
1010
+ throw new Error("Not implemented");
1011
+ },
1012
+ reload: async () => {
1013
+ throw new Error("Not implemented");
1014
+ },
1015
+ isAtBottom: true,
1016
+ scrollToBottom: () => {
1017
+ for (const listener of scrollToBottomListeners) {
1018
+ listener();
1019
+ }
1020
+ },
1021
+ onScrollToBottom: (callback) => {
1022
+ scrollToBottomListeners.add(callback);
1023
+ return () => {
1024
+ scrollToBottomListeners.delete(callback);
1025
+ };
923
1026
  }
924
1027
  }));
925
1028
  const useComposer = (0, import_zustand2.create)()(() => ({
@@ -934,7 +1037,7 @@ var useDummyAIAssistantContext = () => {
934
1037
  },
935
1038
  send: () => {
936
1039
  useThread.getState().append({
937
- role: "user",
1040
+ parentId: useThread.getState().messages.at(-1)?.id ?? ROOT_PARENT_ID,
938
1041
  content: [{ type: "text", text: useComposer.getState().value }]
939
1042
  });
940
1043
  useComposer.getState().setValue("");
@@ -943,43 +1046,39 @@ var useDummyAIAssistantContext = () => {
943
1046
  useThread.getState().stop();
944
1047
  }
945
1048
  }));
946
- const useBranchObserver = (0, import_zustand2.create)()(() => ({
947
- getBranchState: () => ({
948
- branchId: 0,
949
- branchCount: 1
950
- }),
951
- switchToBranch: () => {
952
- throw new Error("Not implemented");
953
- },
954
- editAt: async () => {
955
- throw new Error("Not implemented");
956
- },
957
- reloadAt: async () => {
958
- throw new Error("Not implemented");
959
- }
960
- }));
961
- return { useThread, useComposer, useBranchObserver };
1049
+ return { useThread, useComposer };
962
1050
  });
963
1051
  return context;
964
1052
  };
965
1053
 
966
- // src/vercel/VercelAIAssistantProvider.tsx
1054
+ // src/adapters/vercel/VercelAIAssistantProvider.tsx
967
1055
  var ThreadMessageCache = /* @__PURE__ */ new WeakMap();
968
- var vercelToThreadMessage = (message) => {
1056
+ var vercelToThreadMessage = (message, parentId, branchId, branchCount) => {
969
1057
  if (message.role !== "user" && message.role !== "assistant")
970
1058
  throw new Error("Unsupported role");
971
1059
  return {
1060
+ parentId,
972
1061
  id: message.id,
973
1062
  role: message.role,
974
- content: [{ type: "text", text: message.content }]
1063
+ content: [{ type: "text", text: message.content }],
1064
+ branchId,
1065
+ branchCount,
1066
+ createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
975
1067
  };
976
1068
  };
977
- var vercelToCachedThreadMessages = (messages) => {
978
- return messages.map((m) => {
1069
+ var vercelToCachedThreadMessages = (messages, getBranchState) => {
1070
+ return messages.map((m, idx) => {
979
1071
  const cached = ThreadMessageCache.get(m);
980
- if (cached)
1072
+ const parentId = messages[idx - 1]?.id ?? ROOT_PARENT_ID;
1073
+ const { branchId, branchCount } = getBranchState(m.id);
1074
+ if (cached && cached.parentId === parentId && cached.branchId === branchId && cached.branchCount === branchCount)
981
1075
  return cached;
982
- const newMessage = vercelToThreadMessage(m);
1076
+ const newMessage = vercelToThreadMessage(
1077
+ m,
1078
+ parentId,
1079
+ branchId,
1080
+ branchCount
1081
+ );
983
1082
  ThreadMessageCache.set(m, newMessage);
984
1083
  return newMessage;
985
1084
  });
@@ -990,28 +1089,14 @@ var VercelAIAssistantProvider = ({
990
1089
  }) => {
991
1090
  const context = useDummyAIAssistantContext();
992
1091
  const vercel = "chat" in rest ? rest.chat : rest.assistant;
993
- const messages = (0, import_react18.useMemo)(() => {
994
- return vercelToCachedThreadMessages(vercel.messages);
995
- }, [vercel.messages]);
996
- const maybeReload = "reload" in vercel ? vercel.reload : null;
997
- const reload = (0, import_react18.useCallback)(async () => {
998
- if (!maybeReload)
999
- throw new Error("Reload not supported");
1000
- await maybeReload();
1001
- }, [maybeReload]);
1002
- const append = (0, import_react18.useCallback)(
1003
- async (message) => {
1004
- if (message.content[0]?.type !== "text") {
1005
- throw new Error("Only text content is currently supported");
1006
- }
1007
- await vercel.append({
1008
- role: message.role,
1009
- content: message.content[0].text
1010
- });
1011
- },
1012
- [vercel.append]
1013
- );
1014
- const stop = (0, import_react18.useCallback)(() => {
1092
+ const branches = useVercelAIBranches(vercel, context);
1093
+ const messages = (0, import_react22.useMemo)(() => {
1094
+ return vercelToCachedThreadMessages(
1095
+ vercel.messages,
1096
+ branches.getBranchState
1097
+ );
1098
+ }, [vercel.messages, branches.getBranchState]);
1099
+ const stop = (0, import_react22.useCallback)(() => {
1015
1100
  const lastMessage = vercel.messages.at(-1);
1016
1101
  vercel.stop();
1017
1102
  if (lastMessage?.role === "user") {
@@ -1019,50 +1104,49 @@ var VercelAIAssistantProvider = ({
1019
1104
  }
1020
1105
  }, [vercel.messages, vercel.stop, vercel.setInput]);
1021
1106
  const isLoading = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
1022
- (0, import_react18.useMemo)(() => {
1023
- context.useThread.setState(
1024
- {
1025
- messages,
1026
- isLoading,
1027
- reload,
1028
- append,
1029
- stop
1030
- },
1031
- true
1032
- );
1033
- }, [context, messages, reload, append, stop, isLoading]);
1034
- (0, import_react18.useMemo)(() => {
1107
+ (0, import_react22.useMemo)(() => {
1108
+ context.useThread.setState({
1109
+ messages,
1110
+ isLoading,
1111
+ stop,
1112
+ switchToBranch: branches.switchToBranch,
1113
+ append: branches.append,
1114
+ reload: branches.reload
1115
+ });
1116
+ }, [context, messages, isLoading, stop, branches]);
1117
+ (0, import_react22.useMemo)(() => {
1035
1118
  context.useComposer.setState({
1036
1119
  canCancel: isLoading,
1037
1120
  value: vercel.input,
1038
1121
  setValue: vercel.setInput
1039
1122
  });
1040
1123
  }, [context, isLoading, vercel.input, vercel.setInput]);
1041
- const branches = useVercelAIBranches(vercel);
1042
- (0, import_react18.useMemo)(() => {
1043
- context.useBranchObserver.setState(branches, true);
1044
- }, [context, branches]);
1045
1124
  return /* @__PURE__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
1046
1125
  };
1047
1126
 
1048
- // src/vercel/VercelRSCAssistantProvider.tsx
1049
- var import_react19 = require("react");
1127
+ // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1128
+ var import_react23 = require("react");
1050
1129
  var ThreadMessageCache2 = /* @__PURE__ */ new WeakMap();
1051
- var vercelToThreadMessage2 = (message) => {
1130
+ var vercelToThreadMessage2 = (parentId, message) => {
1052
1131
  if (message.role !== "user" && message.role !== "assistant")
1053
1132
  throw new Error("Unsupported role");
1054
1133
  return {
1134
+ parentId,
1055
1135
  id: message.id,
1056
1136
  role: message.role,
1057
- content: [{ type: "ui", display: message.display }]
1137
+ content: [{ type: "ui", display: message.display }],
1138
+ createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1139
+ branchId: 0,
1140
+ branchCount: 1
1058
1141
  };
1059
1142
  };
1060
1143
  var vercelToCachedThreadMessages2 = (messages) => {
1061
- return messages.map((m) => {
1144
+ return messages.map((m, idx) => {
1062
1145
  const cached = ThreadMessageCache2.get(m);
1063
- if (cached)
1146
+ const parentId = messages[idx - 1]?.id ?? ROOT_PARENT_ID;
1147
+ if (cached && cached.parentId === parentId)
1064
1148
  return cached;
1065
- const newMessage = vercelToThreadMessage2(m);
1149
+ const newMessage = vercelToThreadMessage2(parentId, m);
1066
1150
  ThreadMessageCache2.set(m, newMessage);
1067
1151
  return newMessage;
1068
1152
  });
@@ -1073,22 +1157,22 @@ var VercelRSCAssistantProvider = ({
1073
1157
  append: vercelAppend
1074
1158
  }) => {
1075
1159
  const context = useDummyAIAssistantContext();
1076
- const messages = (0, import_react19.useMemo)(() => {
1160
+ const messages = (0, import_react23.useMemo)(() => {
1077
1161
  return vercelToCachedThreadMessages2(vercelMessages);
1078
1162
  }, [vercelMessages]);
1079
- const append = (0, import_react19.useCallback)(
1163
+ const append = (0, import_react23.useCallback)(
1080
1164
  async (message) => {
1165
+ if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? ROOT_PARENT_ID))
1166
+ throw new Error("Unexpected: Message editing is not supported");
1081
1167
  if (message.content[0]?.type !== "text") {
1082
1168
  throw new Error("Only text content is currently supported");
1083
1169
  }
1084
- await vercelAppend({
1085
- role: message.role,
1086
- content: [{ type: "text", text: message.content[0].text }]
1087
- });
1170
+ context.useThread.getState().scrollToBottom();
1171
+ await vercelAppend(message);
1088
1172
  },
1089
- [vercelAppend]
1173
+ [context, vercelAppend]
1090
1174
  );
1091
- (0, import_react19.useMemo)(() => {
1175
+ (0, import_react23.useMemo)(() => {
1092
1176
  context.useThread.setState({
1093
1177
  messages,
1094
1178
  append