@assistant-ui/react 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -86,9 +86,9 @@ var useThreadIf = (props) => {
86
86
  return false;
87
87
  if (props.empty === false && thread.messages.length === 0)
88
88
  return false;
89
- if (props.busy === true && !thread.isLoading)
89
+ if (props.running === true && !thread.isRunning)
90
90
  return false;
91
- if (props.busy === false && thread.isLoading)
91
+ if (props.running === false && thread.isRunning)
92
92
  return false;
93
93
  return true;
94
94
  });
@@ -153,12 +153,12 @@ var import_react4 = require("react");
153
153
  var useOnScrollToBottom = (callback) => {
154
154
  const callbackRef = (0, import_react4.useRef)(callback);
155
155
  callbackRef.current = callback;
156
- const { useThread } = useAssistantContext();
156
+ const { useViewport } = useAssistantContext();
157
157
  (0, import_react4.useEffect)(() => {
158
- return useThread.getState().onScrollToBottom(() => {
158
+ return useViewport.getState().onScrollToBottom(() => {
159
159
  callbackRef.current();
160
160
  });
161
- }, [useThread]);
161
+ }, [useViewport]);
162
162
  };
163
163
 
164
164
  // src/primitives/thread/ThreadViewport.tsx
@@ -166,18 +166,20 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
166
166
  const messagesEndRef = (0, import_react5.useRef)(null);
167
167
  const divRef = (0, import_react5.useRef)(null);
168
168
  const ref = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, divRef);
169
- const { useThread } = useAssistantContext();
169
+ const { useViewport } = useAssistantContext();
170
170
  const firstRenderRef = (0, import_react5.useRef)(true);
171
+ const lastScrollTop = (0, import_react5.useRef)(0);
171
172
  const scrollToBottom = () => {
172
173
  const div = messagesEndRef.current;
173
174
  if (!div || !autoScroll)
174
175
  return;
175
176
  const behavior = firstRenderRef.current ? "instant" : "auto";
176
177
  firstRenderRef.current = false;
178
+ useViewport.setState({ isAtBottom: true });
177
179
  div.scrollIntoView({ behavior });
178
180
  };
179
181
  useOnResizeContent(divRef, () => {
180
- if (!useThread.getState().isAtBottom)
182
+ if (!useViewport.getState().isAtBottom)
181
183
  return;
182
184
  scrollToBottom();
183
185
  });
@@ -188,11 +190,13 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
188
190
  const div = divRef.current;
189
191
  if (!div)
190
192
  return;
191
- const isAtBottom = useThread.getState().isAtBottom;
192
- const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight + 50;
193
- if (newIsAtBottom !== isAtBottom) {
194
- useThread.setState({ isAtBottom: newIsAtBottom });
193
+ const isAtBottom = useViewport.getState().isAtBottom;
194
+ const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
195
+ if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
196
+ } else if (newIsAtBottom !== isAtBottom) {
197
+ useViewport.setState({ isAtBottom: newIsAtBottom });
195
198
  }
199
+ lastScrollTop.current = div.scrollTop;
196
200
  };
197
201
  return /* @__PURE__ */ React.createElement(
198
202
  import_react_primitive2.Primitive.div,
@@ -206,15 +210,19 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
206
210
  );
207
211
  });
208
212
 
209
- // src/vercel/useVercelAIBranches.tsx
213
+ // src/adapters/vercel/useVercelAIBranches.tsx
210
214
  var import_react6 = require("react");
211
- var ROOT_ID = "__ROOT_ID__";
215
+
216
+ // src/utils/context/stores/AssistantTypes.ts
217
+ var ROOT_PARENT_ID = "__ROOT_ID__";
218
+
219
+ // src/adapters/vercel/useVercelAIBranches.tsx
212
220
  var UPCOMING_MESSAGE_ID = "__UPCOMING_MESSAGE_ID__";
213
221
  var updateBranchData = (data, messages) => {
214
222
  for (let i = 0; i < messages.length; i++) {
215
223
  const child = messages[i];
216
224
  const childId = child.id;
217
- const parentId = messages[i - 1]?.id ?? ROOT_ID;
225
+ const parentId = messages[i - 1]?.id ?? ROOT_PARENT_ID;
218
226
  data.parentMap.set(childId, parentId);
219
227
  const parentArray = data.branchMap.get(parentId);
220
228
  if (!parentArray) {
@@ -225,32 +233,32 @@ var updateBranchData = (data, messages) => {
225
233
  data.snapshots.set(childId, messages);
226
234
  }
227
235
  };
228
- var getParentId = (data, messages, message) => {
229
- if (message.id === UPCOMING_MESSAGE_ID) {
236
+ var getParentId = (data, messages, messageId) => {
237
+ if (messageId === UPCOMING_MESSAGE_ID) {
230
238
  const parent = messages.at(-1);
231
239
  if (!parent)
232
- return ROOT_ID;
240
+ return ROOT_PARENT_ID;
233
241
  return parent.id;
234
242
  }
235
- const parentId = data.parentMap.get(message.id);
243
+ const parentId = data.parentMap.get(messageId);
236
244
  if (!parentId)
237
245
  throw new Error("Unexpected: Message parent not found");
238
246
  return parentId;
239
247
  };
240
- var getBranchStateImpl = (data, messages, message) => {
241
- const parentId = getParentId(data, messages, message);
248
+ var getBranchStateImpl = (data, messages, messageId) => {
249
+ const parentId = getParentId(data, messages, messageId);
242
250
  const branches = data.branchMap.get(parentId) ?? [];
243
- 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);
244
252
  if (branchId === -1)
245
253
  throw new Error("Unexpected: Message not found in parent children");
246
- const upcomingOffset = message.id === UPCOMING_MESSAGE_ID ? 1 : 0;
254
+ const upcomingOffset = messageId === UPCOMING_MESSAGE_ID ? 1 : 0;
247
255
  return {
248
256
  branchId,
249
257
  branchCount: branches.length + upcomingOffset
250
258
  };
251
259
  };
252
- var switchToBranchImpl = (data, messages, message, branchId) => {
253
- const parentId = getParentId(data, messages, message);
260
+ var switchToBranchImpl = (data, messages, messageId, branchId) => {
261
+ const parentId = getParentId(data, messages, messageId);
254
262
  const branches = data.branchMap.get(parentId);
255
263
  if (!branches)
256
264
  throw new Error("Unexpected: Parent children not found");
@@ -259,20 +267,22 @@ var switchToBranchImpl = (data, messages, message, branchId) => {
259
267
  throw new Error("Unexpected: Requested branch not found");
260
268
  if (branchId < 0 || branchId >= branches.length)
261
269
  throw new Error("Switch to branch called with a branch index out of range");
262
- if (newMessageId === message.id)
270
+ if (newMessageId === messageId)
263
271
  return messages;
264
272
  const snapshot = data.snapshots.get(newMessageId);
265
273
  if (!snapshot)
266
274
  throw new Error("Unexpected: Branch snapshot not found");
267
275
  return snapshot;
268
276
  };
269
- var sliceMessagesUntil = (messages, message) => {
270
- 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)
271
281
  return messages;
272
- const messageIdx = messages.findIndex((m) => m.id === message.id);
282
+ const messageIdx = messages.findIndex((m) => m.id === messageId);
273
283
  if (messageIdx === -1)
274
284
  throw new Error("Unexpected: Message not found");
275
- return messages.slice(0, messageIdx);
285
+ return messages.slice(0, messageIdx + 1);
276
286
  };
277
287
  var useVercelAIBranches = (chat, context) => {
278
288
  const data = (0, import_react6.useRef)({
@@ -282,17 +292,17 @@ var useVercelAIBranches = (chat, context) => {
282
292
  }).current;
283
293
  updateBranchData(data, chat.messages);
284
294
  const getBranchState = (0, import_react6.useCallback)(
285
- (message) => {
286
- return getBranchStateImpl(data, chat.messages, message);
295
+ (messageId) => {
296
+ return getBranchStateImpl(data, chat.messages, messageId);
287
297
  },
288
298
  [data, chat.messages]
289
299
  );
290
300
  const switchToBranch = (0, import_react6.useCallback)(
291
- (message, branchId) => {
301
+ (messageId, branchId) => {
292
302
  const newMessages = switchToBranchImpl(
293
303
  data,
294
304
  chat.messages,
295
- message,
305
+ messageId,
296
306
  branchId
297
307
  );
298
308
  chat.setMessages(newMessages);
@@ -300,27 +310,27 @@ var useVercelAIBranches = (chat, context) => {
300
310
  [data, chat.messages, chat.setMessages]
301
311
  );
302
312
  const reloadMaybe = "reload" in chat ? chat.reload : void 0;
303
- const reloadAt = (0, import_react6.useCallback)(
304
- async (message) => {
313
+ const startRun = (0, import_react6.useCallback)(
314
+ async (parentId) => {
305
315
  if (!reloadMaybe)
306
316
  throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
307
- const newMessages = sliceMessagesUntil(chat.messages, message);
317
+ const newMessages = sliceMessagesUntil(chat.messages, parentId);
308
318
  chat.setMessages(newMessages);
309
- context.useThread.getState().scrollToBottom();
319
+ context.useViewport.getState().scrollToBottom();
310
320
  await reloadMaybe();
311
321
  },
312
322
  [context, chat.messages, chat.setMessages, reloadMaybe]
313
323
  );
314
- const editAt = (0, import_react6.useCallback)(
315
- async (message, newMessage) => {
316
- const newMessages = sliceMessagesUntil(chat.messages, message);
324
+ const append = (0, import_react6.useCallback)(
325
+ async (message) => {
326
+ const newMessages = sliceMessagesUntil(chat.messages, message.parentId);
317
327
  chat.setMessages(newMessages);
318
- if (newMessage.content[0]?.type !== "text")
328
+ if (message.content.length !== 1 || message.content[0]?.type !== "text")
319
329
  throw new Error("Only text content is currently supported");
320
- context.useThread.getState().scrollToBottom();
330
+ context.useViewport.getState().scrollToBottom();
321
331
  await chat.append({
322
332
  role: "user",
323
- content: newMessage.content[0].text
333
+ content: message.content[0].text
324
334
  });
325
335
  },
326
336
  [context, chat.messages, chat.setMessages, chat.append]
@@ -329,14 +339,14 @@ var useVercelAIBranches = (chat, context) => {
329
339
  () => ({
330
340
  getBranchState,
331
341
  switchToBranch,
332
- editAt,
333
- reloadAt
342
+ append,
343
+ startRun
334
344
  }),
335
- [getBranchState, switchToBranch, editAt, reloadAt]
345
+ [getBranchState, switchToBranch, append, startRun]
336
346
  );
337
347
  };
338
348
  var hasUpcomingMessage = (thread) => {
339
- return thread.isLoading && thread.messages[thread.messages.length - 1]?.role !== "assistant";
349
+ return thread.isRunning && thread.messages[thread.messages.length - 1]?.role !== "assistant";
340
350
  };
341
351
 
342
352
  // src/utils/context/useComposerContext.ts
@@ -389,16 +399,62 @@ __export(message_exports, {
389
399
 
390
400
  // src/primitives/message/MessageProvider.tsx
391
401
  var import_react9 = require("react");
402
+ var import_zustand2 = require("zustand");
403
+
404
+ // src/utils/context/stores/ComposerStore.ts
392
405
  var import_zustand = require("zustand");
393
- var import_shallow = require("zustand/react/shallow");
406
+ var makeBaseComposer = (set) => ({
407
+ value: "",
408
+ setValue: (value) => {
409
+ set({ value });
410
+ }
411
+ });
412
+ var makeMessageComposerStore = ({
413
+ onEdit,
414
+ onSend
415
+ }) => (0, import_zustand.create)()((set, get, store) => ({
416
+ ...makeBaseComposer(set, get, store),
417
+ canCancel: true,
418
+ isEditing: false,
419
+ edit: () => {
420
+ const value = onEdit();
421
+ set({ isEditing: true, value });
422
+ },
423
+ send: () => {
424
+ const value = get().value;
425
+ set({ isEditing: false });
426
+ return onSend(value);
427
+ },
428
+ cancel: () => {
429
+ set({ isEditing: false });
430
+ }
431
+ }));
432
+ var makeThreadComposerStore = ({
433
+ onSend,
434
+ onCancel
435
+ }) => (0, import_zustand.create)()((set, get, store) => ({
436
+ ...makeBaseComposer(set, get, store),
437
+ isEditing: true,
438
+ canCancel: false,
439
+ send: () => {
440
+ const value = get().value;
441
+ set({ value: "", canCancel: true });
442
+ onSend(value).then(() => {
443
+ set({ canCancel: false });
444
+ });
445
+ },
446
+ cancel: onCancel
447
+ }));
448
+
449
+ // src/primitives/message/MessageProvider.tsx
394
450
  var getIsLast = (thread, message) => {
395
451
  const hasUpcoming = hasUpcomingMessage(thread);
396
452
  return hasUpcoming ? message.id === UPCOMING_MESSAGE_ID : thread.messages[thread.messages.length - 1]?.id === message.id;
397
453
  };
398
454
  var useMessageContext2 = () => {
399
- const { useBranchObserver } = useAssistantContext();
400
455
  const [context] = (0, import_react9.useState)(() => {
401
- const useMessage = (0, import_zustand.create)(() => ({
456
+ const { useThread } = useAssistantContext();
457
+ const useMessage = (0, import_zustand2.create)(() => ({
402
458
  message: null,
403
459
  isLast: false,
404
460
  isCopied: false,
@@ -406,40 +462,25 @@ var useMessageContext2 = () => {
406
462
  setIsCopied: () => {
407
463
  },
408
464
  setIsHovering: () => {
409
- },
410
- branchState: {
411
- branchId: 0,
412
- branchCount: 0
413
465
  }
414
466
  }));
415
- const useComposer = (0, import_zustand.create)((set, get) => ({
416
- isEditing: false,
417
- canCancel: true,
418
- edit: () => {
467
+ const useComposer = makeMessageComposerStore({
468
+ onEdit: () => {
419
469
  const message = useMessage.getState().message;
420
470
  if (message.role !== "user")
421
471
  throw new Error("Editing is only supported for user messages");
422
472
  if (message.content[0]?.type !== "text")
423
473
  throw new Error("Editing is only supported for text-only messages");
424
- return set({
425
- isEditing: true,
426
- value: message.content[0].text
427
- });
474
+ return message.content[0].text;
428
475
  },
429
- cancel: () => set({ isEditing: false }),
430
- send: () => {
476
+ onSend: (text) => {
431
477
  const message = useMessage.getState().message;
432
- if (message.role !== "user")
433
- throw new Error("Editing is only supported for user messages");
434
- useBranchObserver.getState().editAt(message, {
435
- role: "user",
436
- content: [{ type: "text", text: get().value }]
478
+ return useThread.getState().append({
479
+ parentId: message.parentId,
480
+ content: [{ type: "text", text }]
437
481
  });
438
- set({ isEditing: false });
439
- },
440
- value: "",
441
- setValue: (value) => set({ value })
442
- }));
482
+ }
483
+ });
443
484
  return { useMessage, useComposer };
444
485
  });
445
486
  return context;
@@ -448,11 +489,8 @@ var MessageProvider = ({
448
489
  message,
449
490
  children
450
491
  }) => {
451
- const { useThread, useBranchObserver } = useAssistantContext();
492
+ const { useThread } = useAssistantContext();
452
493
  const context = useMessageContext2();
453
- const branchState = useBranchObserver(
454
- (0, import_shallow.useShallow)((b) => b.getBranchState(message))
455
- );
456
494
  const isLast = useThread((thread) => getIsLast(thread, message));
457
495
  const [isCopied, setIsCopied] = (0, import_react9.useState)(false);
458
496
  const [isHovering, setIsHovering] = (0, import_react9.useState)(false);
@@ -464,12 +502,11 @@ var MessageProvider = ({
464
502
  isCopied,
465
503
  isHovering,
466
504
  setIsCopied,
467
- setIsHovering,
468
- branchState
505
+ setIsHovering
469
506
  },
470
507
  true
471
508
  );
472
- }, [context, message, isLast, isCopied, isHovering, branchState]);
509
+ }, [context, message, isLast, isCopied, isHovering]);
473
510
  return /* @__PURE__ */ React.createElement(MessageContext.Provider, { value: context }, children);
474
511
  };
475
512
 
@@ -502,29 +539,21 @@ var MessageRoot = (0, import_react10.forwardRef)(
502
539
  // src/primitives/message/MessageIf.tsx
503
540
  var useMessageIf = (props) => {
504
541
  const { useMessage } = useMessageContext();
505
- return useMessage(
506
- ({
507
- message,
508
- isLast,
509
- isCopied,
510
- isHovering,
511
- branchState: { branchCount }
512
- }) => {
513
- if (props.hasBranches === true && branchCount < 2)
514
- return false;
515
- if (props.user && message.role !== "user")
516
- return false;
517
- if (props.assistant && message.role !== "assistant")
518
- return false;
519
- if (props.lastOrHover === true && !isHovering && !isLast)
520
- return false;
521
- if (props.copied === true && !isCopied)
522
- return false;
523
- if (props.copied === false && isCopied)
524
- return false;
525
- return true;
526
- }
527
- );
542
+ return useMessage(({ message, isLast, isCopied, isHovering }) => {
543
+ if (props.hasBranches === true && message.branchCount < 2)
544
+ return false;
545
+ if (props.user && message.role !== "user")
546
+ return false;
547
+ if (props.assistant && message.role !== "assistant")
548
+ return false;
549
+ if (props.lastOrHover === true && !isHovering && !isLast)
550
+ return false;
551
+ if (props.copied === true && !isCopied)
552
+ return false;
553
+ if (props.copied === false && isCopied)
554
+ return false;
555
+ return true;
556
+ });
528
557
  };
529
558
  var MessageIf = ({ children, ...query }) => {
530
559
  const result = useMessageIf(query);
@@ -595,7 +624,12 @@ var ThreadMessages = ({ components }) => {
595
624
  message: {
596
625
  id: UPCOMING_MESSAGE_ID,
597
626
  role: "assistant",
598
- content: [{ type: "text", text: "..." }]
627
+ content: [{ type: "text", text: "..." }],
628
+ parentId: messages.at(-1)?.id ?? ROOT_PARENT_ID,
629
+ // TODO fix these (move upcoming message to AssistantContext)
630
+ branchId: 0,
631
+ branchCount: 1,
632
+ createdAt: /* @__PURE__ */ new Date()
599
633
  }
600
634
  },
601
635
  /* @__PURE__ */ React.createElement(AssistantMessage, null)
@@ -607,18 +641,16 @@ var import_primitive3 = require("@radix-ui/primitive");
607
641
  var import_react_primitive4 = require("@radix-ui/react-primitive");
608
642
  var import_react11 = require("react");
609
643
  var ThreadScrollToBottom = (0, import_react11.forwardRef)(({ onClick, ...rest }, ref) => {
610
- const { useThread } = useAssistantContext();
611
- const isAtBottom = useThread((s) => s.isAtBottom);
644
+ const { useViewport } = useAssistantContext();
645
+ const isAtBottom = useViewport((s) => s.isAtBottom);
612
646
  const handleScrollToBottom = () => {
613
- const thread = useThread.getState();
614
- thread.scrollToBottom();
647
+ useViewport.getState().scrollToBottom();
615
648
  };
616
- if (isAtBottom)
617
- return null;
618
649
  return /* @__PURE__ */ React.createElement(
619
650
  import_react_primitive4.Primitive.button,
620
651
  {
621
652
  ...rest,
653
+ disabled: isAtBottom,
622
654
  ref,
623
655
  onClick: (0, import_primitive3.composeEventHandlers)(onClick, handleScrollToBottom)
624
656
  }
@@ -688,8 +720,8 @@ var ComposerInput = (0, import_react13.forwardRef)(
688
720
  useComposer.getState().cancel();
689
721
  }
690
722
  if (e.key === "Enter" && e.shiftKey === false) {
691
- const isLoading = useThread.getState().isLoading;
692
- if (!isLoading) {
723
+ const isRunning = useThread.getState().isRunning;
724
+ if (!isRunning) {
693
725
  e.preventDefault();
694
726
  composer.send();
695
727
  }
@@ -812,20 +844,17 @@ var useCombinedStore = (stores, selector) => {
812
844
 
813
845
  // src/actions/useGoToNextBranch.tsx
814
846
  var useGoToNextBranch = () => {
815
- const { useThread, useBranchObserver } = useAssistantContext();
847
+ const { useThread } = useAssistantContext();
816
848
  const { useComposer, useMessage } = useMessageContext();
817
849
  const disabled = useCombinedStore(
818
850
  [useThread, useComposer, useMessage],
819
- (t, c, m) => t.isLoading || c.isEditing || m.branchState.branchId + 1 >= m.branchState.branchCount
851
+ (t, c, m) => t.isRunning || c.isEditing || m.message.branchId + 1 >= m.message.branchCount
820
852
  );
821
853
  if (disabled)
822
854
  return null;
823
855
  return () => {
824
- const {
825
- message,
826
- branchState: { branchId }
827
- } = useMessage.getState();
828
- useBranchObserver.getState().switchToBranch(message, branchId + 1);
856
+ const { message } = useMessage.getState();
857
+ useThread.getState().switchToBranch(message.id, message.branchId + 1);
829
858
  };
830
859
  };
831
860
 
@@ -856,20 +885,17 @@ var BranchPickerNext = createActionButton(useGoToNextBranch);
856
885
 
857
886
  // src/actions/useGoToPreviousBranch.tsx
858
887
  var useGoToPreviousBranch = () => {
859
- const { useThread, useBranchObserver } = useAssistantContext();
888
+ const { useThread } = useAssistantContext();
860
889
  const { useComposer, useMessage } = useMessageContext();
861
890
  const disabled = useCombinedStore(
862
891
  [useThread, useComposer, useMessage],
863
- (t, c, m) => t.isLoading || c.isEditing || m.branchState.branchId <= 0
892
+ (t, c, m) => t.isRunning || c.isEditing || m.message.branchId <= 0
864
893
  );
865
894
  if (disabled)
866
895
  return null;
867
896
  return () => {
868
- const {
869
- message,
870
- branchState: { branchId }
871
- } = useMessage.getState();
872
- useBranchObserver.getState().switchToBranch(message, branchId - 1);
897
+ const { message } = useMessage.getState();
898
+ useThread.getState().switchToBranch(message.id, message.branchId - 1);
873
899
  };
874
900
  };
875
901
 
@@ -879,14 +905,14 @@ var BranchPickerPrevious = createActionButton(useGoToPreviousBranch);
879
905
  // src/primitives/branchPicker/BranchPickerCount.tsx
880
906
  var BranchPickerCount = () => {
881
907
  const { useMessage } = useMessageContext();
882
- const branchCount = useMessage((s) => s.branchState.branchCount);
908
+ const branchCount = useMessage((s) => s.message.branchCount);
883
909
  return /* @__PURE__ */ React.createElement(React.Fragment, null, branchCount);
884
910
  };
885
911
 
886
912
  // src/primitives/branchPicker/BranchPickerNumber.tsx
887
913
  var BranchPickerNumber = () => {
888
914
  const { useMessage } = useMessageContext();
889
- const branchId = useMessage((s) => s.branchState.branchId);
915
+ const branchId = useMessage((s) => s.message.branchId);
890
916
  return /* @__PURE__ */ React.createElement(React.Fragment, null, branchId + 1);
891
917
  };
892
918
 
@@ -909,20 +935,20 @@ __export(actionBar_exports, {
909
935
  // src/primitives/actionBar/ActionBarRoot.tsx
910
936
  var import_react_primitive10 = require("@radix-ui/react-primitive");
911
937
  var import_react20 = require("react");
912
- var ActionBarRoot = (0, import_react20.forwardRef)(({ hideWhenBusy, autohide, autohideFloat, ...rest }, ref) => {
938
+ var ActionBarRoot = (0, import_react20.forwardRef)(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
913
939
  const { useThread } = useAssistantContext();
914
940
  const { useMessage } = useMessageContext();
915
941
  const hideAndfloatStatus = useCombinedStore(
916
942
  [useThread, useMessage],
917
943
  (t, m) => {
918
- if (hideWhenBusy && t.isLoading)
944
+ if (hideWhenRunning && t.isRunning)
919
945
  return "hidden" /* Hidden */;
920
946
  const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
921
947
  if (!autohideEnabled)
922
948
  return "normal" /* Normal */;
923
949
  if (!m.isHovering)
924
950
  return "hidden" /* Hidden */;
925
- if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branchState.branchCount <= 1)
951
+ if (autohideFloat === "always" || autohideFloat === "single-branch" && m.message.branchCount <= 1)
926
952
  return "floating" /* Floating */;
927
953
  return "normal" /* Normal */;
928
954
  }
@@ -960,11 +986,11 @@ var ActionBarCopy = createActionButton(useCopyMessage);
960
986
 
961
987
  // src/actions/useReloadMessage.tsx
962
988
  var useReloadMessage = () => {
963
- const { useThread, useBranchObserver } = useAssistantContext();
989
+ const { useThread } = useAssistantContext();
964
990
  const { useMessage } = useMessageContext();
965
991
  const disabled = useCombinedStore(
966
992
  [useThread, useMessage],
967
- (t, m) => t.isLoading || m.message.role !== "assistant"
993
+ (t, m) => t.isRunning || m.message.role !== "assistant"
968
994
  );
969
995
  if (disabled)
970
996
  return null;
@@ -972,7 +998,7 @@ var useReloadMessage = () => {
972
998
  const message = useMessage.getState().message;
973
999
  if (message.role !== "assistant")
974
1000
  throw new Error("Reloading is only supported on assistant messages");
975
- useBranchObserver.getState().reloadAt(message);
1001
+ useThread.getState().startRun(message.parentId);
976
1002
  };
977
1003
  };
978
1004
 
@@ -997,95 +1023,98 @@ var useBeginMessageEdit = () => {
997
1023
  // src/primitives/actionBar/ActionBarEdit.tsx
998
1024
  var ActionBarEdit = createActionButton(useBeginMessageEdit);
999
1025
 
1000
- // src/vercel/VercelAIAssistantProvider.tsx
1026
+ // src/adapters/vercel/VercelAIAssistantProvider.tsx
1001
1027
  var import_react22 = require("react");
1002
1028
 
1003
- // src/vercel/useDummyAIAssistantContext.tsx
1029
+ // src/adapters/vercel/useDummyAIAssistantContext.tsx
1004
1030
  var import_react21 = require("react");
1005
- var import_zustand2 = require("zustand");
1031
+ var import_zustand4 = require("zustand");
1032
+
1033
+ // src/utils/context/stores/ViewportStore.tsx
1034
+ var import_zustand3 = require("zustand");
1035
+ var makeViewportStore = () => {
1036
+ const scrollToBottomListeners = /* @__PURE__ */ new Set();
1037
+ return (0, import_zustand3.create)(() => ({
1038
+ isAtBottom: true,
1039
+ scrollToBottom: () => {
1040
+ for (const listener of scrollToBottomListeners) {
1041
+ listener();
1042
+ }
1043
+ },
1044
+ onScrollToBottom: (callback) => {
1045
+ scrollToBottomListeners.add(callback);
1046
+ return () => {
1047
+ scrollToBottomListeners.delete(callback);
1048
+ };
1049
+ }
1050
+ }));
1051
+ };
1052
+
1053
+ // src/adapters/vercel/useDummyAIAssistantContext.tsx
1006
1054
  var useDummyAIAssistantContext = () => {
1007
1055
  const [context] = (0, import_react21.useState)(() => {
1008
- const scrollToBottomListeners = /* @__PURE__ */ new Set();
1009
- const useThread = (0, import_zustand2.create)()(() => ({
1056
+ const useThread = (0, import_zustand4.create)()(() => ({
1057
+ id: "",
1010
1058
  messages: [],
1011
- isLoading: false,
1059
+ isRunning: false,
1012
1060
  append: async () => {
1013
1061
  throw new Error("Not implemented");
1014
1062
  },
1015
- stop: () => {
1016
- throw new Error("Not implemented");
1017
- },
1018
- isAtBottom: true,
1019
- scrollToBottom: () => {
1020
- for (const listener of scrollToBottomListeners) {
1021
- listener();
1022
- }
1023
- },
1024
- onScrollToBottom: (callback) => {
1025
- scrollToBottomListeners.add(callback);
1026
- return () => {
1027
- scrollToBottomListeners.delete(callback);
1028
- };
1029
- }
1030
- }));
1031
- const useComposer = (0, import_zustand2.create)()(() => ({
1032
- isEditing: true,
1033
- canCancel: false,
1034
- value: "",
1035
- setValue: (value) => {
1036
- useComposer.setState({ value });
1037
- },
1038
- edit: () => {
1063
+ cancelRun: () => {
1039
1064
  throw new Error("Not implemented");
1040
1065
  },
1041
- send: () => {
1042
- useThread.getState().append({
1043
- role: "user",
1044
- content: [{ type: "text", text: useComposer.getState().value }]
1045
- });
1046
- useComposer.getState().setValue("");
1047
- },
1048
- cancel: () => {
1049
- useThread.getState().stop();
1050
- }
1051
- }));
1052
- const useBranchObserver = (0, import_zustand2.create)()(() => ({
1053
- getBranchState: () => ({
1054
- branchId: 0,
1055
- branchCount: 1
1056
- }),
1057
1066
  switchToBranch: () => {
1058
1067
  throw new Error("Not implemented");
1059
1068
  },
1060
- editAt: async () => {
1061
- throw new Error("Not implemented");
1062
- },
1063
- reloadAt: async () => {
1069
+ startRun: async () => {
1064
1070
  throw new Error("Not implemented");
1065
1071
  }
1066
1072
  }));
1067
- return { useThread, useComposer, useBranchObserver };
1073
+ const useViewport = makeViewportStore();
1074
+ const useComposer = makeThreadComposerStore({
1075
+ onSend: async (text) => {
1076
+ await useThread.getState().append({
1077
+ parentId: useThread.getState().messages.at(-1)?.id ?? ROOT_PARENT_ID,
1078
+ content: [{ type: "text", text }]
1079
+ });
1080
+ },
1081
+ onCancel: () => {
1082
+ useThread.getState().cancelRun();
1083
+ }
1084
+ });
1085
+ return { useThread, useViewport, useComposer };
1068
1086
  });
1069
1087
  return context;
1070
1088
  };
1071
1089
 
1072
- // src/vercel/VercelAIAssistantProvider.tsx
1090
+ // src/adapters/vercel/VercelAIAssistantProvider.tsx
1073
1091
  var ThreadMessageCache = /* @__PURE__ */ new WeakMap();
1074
- var vercelToThreadMessage = (message) => {
1092
+ var vercelToThreadMessage = (message, parentId, branchId, branchCount) => {
1075
1093
  if (message.role !== "user" && message.role !== "assistant")
1076
1094
  throw new Error("Unsupported role");
1077
1095
  return {
1096
+ parentId,
1078
1097
  id: message.id,
1079
1098
  role: message.role,
1080
- content: [{ type: "text", text: message.content }]
1099
+ content: [{ type: "text", text: message.content }],
1100
+ branchId,
1101
+ branchCount,
1102
+ createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
1081
1103
  };
1082
1104
  };
1083
- var vercelToCachedThreadMessages = (messages) => {
1084
- return messages.map((m) => {
1105
+ var vercelToCachedThreadMessages = (messages, getBranchState) => {
1106
+ return messages.map((m, idx) => {
1085
1107
  const cached = ThreadMessageCache.get(m);
1086
- if (cached)
1108
+ const parentId = messages[idx - 1]?.id ?? ROOT_PARENT_ID;
1109
+ const { branchId, branchCount } = getBranchState(m.id);
1110
+ if (cached && cached.parentId === parentId && cached.branchId === branchId && cached.branchCount === branchCount)
1087
1111
  return cached;
1088
- const newMessage = vercelToThreadMessage(m);
1112
+ const newMessage = vercelToThreadMessage(
1113
+ m,
1114
+ parentId,
1115
+ branchId,
1116
+ branchCount
1117
+ );
1089
1118
  ThreadMessageCache.set(m, newMessage);
1090
1119
  return newMessage;
1091
1120
  });
@@ -1096,70 +1125,64 @@ var VercelAIAssistantProvider = ({
1096
1125
  }) => {
1097
1126
  const context = useDummyAIAssistantContext();
1098
1127
  const vercel = "chat" in rest ? rest.chat : rest.assistant;
1128
+ const branches = useVercelAIBranches(vercel, context);
1099
1129
  const messages = (0, import_react22.useMemo)(() => {
1100
- return vercelToCachedThreadMessages(vercel.messages);
1101
- }, [vercel.messages]);
1102
- const append = (0, import_react22.useCallback)(
1103
- async (message) => {
1104
- if (message.content[0]?.type !== "text") {
1105
- throw new Error("Only text content is currently supported");
1106
- }
1107
- context.useThread.getState().scrollToBottom();
1108
- await vercel.append({
1109
- role: message.role,
1110
- content: message.content[0].text
1111
- });
1112
- },
1113
- [context, vercel.append]
1114
- );
1115
- const stop = (0, import_react22.useCallback)(() => {
1130
+ return vercelToCachedThreadMessages(
1131
+ vercel.messages,
1132
+ branches.getBranchState
1133
+ );
1134
+ }, [vercel.messages, branches.getBranchState]);
1135
+ const cancelRun = (0, import_react22.useCallback)(() => {
1116
1136
  const lastMessage = vercel.messages.at(-1);
1117
1137
  vercel.stop();
1118
1138
  if (lastMessage?.role === "user") {
1119
1139
  vercel.setInput(lastMessage.content);
1120
1140
  }
1121
1141
  }, [vercel.messages, vercel.stop, vercel.setInput]);
1122
- const isLoading = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
1142
+ const isRunning = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
1123
1143
  (0, import_react22.useMemo)(() => {
1124
1144
  context.useThread.setState({
1125
1145
  messages,
1126
- isLoading,
1127
- append,
1128
- stop
1146
+ isRunning,
1147
+ cancelRun,
1148
+ switchToBranch: branches.switchToBranch,
1149
+ append: branches.append,
1150
+ startRun: branches.startRun
1129
1151
  });
1130
- }, [context, messages, append, stop, isLoading]);
1152
+ }, [context, messages, isRunning, cancelRun, branches]);
1131
1153
  (0, import_react22.useMemo)(() => {
1132
1154
  context.useComposer.setState({
1133
- canCancel: isLoading,
1155
+ canCancel: isRunning,
1134
1156
  value: vercel.input,
1135
1157
  setValue: vercel.setInput
1136
1158
  });
1137
- }, [context, isLoading, vercel.input, vercel.setInput]);
1138
- const branches = useVercelAIBranches(vercel, context);
1139
- (0, import_react22.useMemo)(() => {
1140
- context.useBranchObserver.setState(branches, true);
1141
- }, [context, branches]);
1159
+ }, [context, isRunning, vercel.input, vercel.setInput]);
1142
1160
  return /* @__PURE__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
1143
1161
  };
1144
1162
 
1145
- // src/vercel/VercelRSCAssistantProvider.tsx
1163
+ // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1146
1164
  var import_react23 = require("react");
1147
1165
  var ThreadMessageCache2 = /* @__PURE__ */ new WeakMap();
1148
- var vercelToThreadMessage2 = (message) => {
1166
+ var vercelToThreadMessage2 = (parentId, message) => {
1149
1167
  if (message.role !== "user" && message.role !== "assistant")
1150
1168
  throw new Error("Unsupported role");
1151
1169
  return {
1170
+ parentId,
1152
1171
  id: message.id,
1153
1172
  role: message.role,
1154
- content: [{ type: "ui", display: message.display }]
1173
+ content: [{ type: "ui", display: message.display }],
1174
+ createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1175
+ branchId: 0,
1176
+ branchCount: 1
1155
1177
  };
1156
1178
  };
1157
1179
  var vercelToCachedThreadMessages2 = (messages) => {
1158
- return messages.map((m) => {
1180
+ return messages.map((m, idx) => {
1159
1181
  const cached = ThreadMessageCache2.get(m);
1160
- if (cached)
1182
+ const parentId = messages[idx - 1]?.id ?? ROOT_PARENT_ID;
1183
+ if (cached && cached.parentId === parentId)
1161
1184
  return cached;
1162
- const newMessage = vercelToThreadMessage2(m);
1185
+ const newMessage = vercelToThreadMessage2(parentId, m);
1163
1186
  ThreadMessageCache2.set(m, newMessage);
1164
1187
  return newMessage;
1165
1188
  });
@@ -1175,14 +1198,13 @@ var VercelRSCAssistantProvider = ({
1175
1198
  }, [vercelMessages]);
1176
1199
  const append = (0, import_react23.useCallback)(
1177
1200
  async (message) => {
1201
+ if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? ROOT_PARENT_ID))
1202
+ throw new Error("Unexpected: Message editing is not supported");
1178
1203
  if (message.content[0]?.type !== "text") {
1179
1204
  throw new Error("Only text content is currently supported");
1180
1205
  }
1181
- context.useThread.getState().scrollToBottom();
1182
- await vercelAppend({
1183
- role: message.role,
1184
- content: [{ type: "text", text: message.content[0].text }]
1185
- });
1206
+ context.useViewport.getState().scrollToBottom();
1207
+ await vercelAppend(message);
1186
1208
  },
1187
1209
  [context, vercelAppend]
1188
1210
  );