@assistant-ui/react 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -33,18 +33,15 @@ __export(src_exports, {
33
33
  ActionBarPrimitive: () => actionBar_exports,
34
34
  BranchPickerPrimitive: () => branchPicker_exports,
35
35
  ComposerPrimitive: () => composer_exports,
36
- EditBarPrimitive: () => editBar_exports,
37
36
  MessagePrimitive: () => message_exports,
38
37
  ThreadPrimitive: () => thread_exports,
39
- VercelAIThreadProvider: () => VercelAIThreadProvider,
38
+ VercelAIThreadProvider: () => VercelAIAssistantProvider,
40
39
  unstable_useMessageContext: () => useMessageContext,
41
40
  useBeginMessageEdit: () => useBeginMessageEdit,
42
- useCancelMessageEdit: () => useCancelMessageEdit,
43
41
  useCopyMessage: () => useCopyMessage,
44
42
  useGoToNextBranch: () => useGoToNextBranch,
45
43
  useGoToPreviousBranch: () => useGoToPreviousBranch,
46
- useReloadMessage: () => useReloadMessage,
47
- useSaveMessageEdit: () => useSaveMessageEdit
44
+ useReloadMessage: () => useReloadMessage
48
45
  });
49
46
  module.exports = __toCommonJS(src_exports);
50
47
 
@@ -67,87 +64,22 @@ var ThreadRoot = (0, import_react.forwardRef)(
67
64
  }
68
65
  );
69
66
 
70
- // src/utils/context/createStoreContext.tsx
67
+ // src/utils/context/AssistantContext.ts
71
68
  var import_react2 = require("react");
72
- var import_with_selector = require("use-sync-external-store/with-selector");
73
-
74
- // src/utils/context/shallow.ts
75
- function shallow(objA, objB) {
76
- if (Object.is(objA, objB)) {
77
- return true;
78
- }
79
- if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) {
80
- return false;
81
- }
82
- const keysA = Object.keys(objA);
83
- if (keysA.length !== Object.keys(objB).length) {
84
- return false;
85
- }
86
- for (const keyA of keysA) {
87
- if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) {
88
- return false;
89
- }
90
- }
91
- return true;
92
- }
93
-
94
- // src/utils/context/createStoreContext.tsx
95
- var createStoreContext = (providerName) => {
96
- const context = (0, import_react2.createContext)(null);
97
- const StoreProvider = ({ children, ...rest }) => {
98
- const unstableContext = rest;
99
- const [store] = (0, import_react2.useState)(() => {
100
- let state = unstableContext;
101
- const listeners = /* @__PURE__ */ new Set();
102
- return {
103
- subscribe: (cb) => {
104
- listeners.add(cb);
105
- return () => listeners.delete(cb);
106
- },
107
- emit: () => {
108
- for (const listener of listeners) {
109
- listener();
110
- }
111
- },
112
- snapshot: () => {
113
- return state;
114
- },
115
- setState: (value) => {
116
- state = value;
117
- store.emit();
118
- }
119
- };
120
- });
121
- (0, import_react2.useMemo)(
122
- () => store.setState(unstableContext),
123
- Object.values(unstableContext)
124
- );
125
- return /* @__PURE__ */ React.createElement(context.Provider, { value: store }, children);
126
- };
127
- const useStoreContext = (consumerName, selector) => {
128
- const store = (0, import_react2.useContext)(context);
129
- if (!store)
130
- throw new Error(
131
- `${consumerName} can only be used inside ${providerName}.`
132
- );
133
- return (0, import_with_selector.useSyncExternalStoreWithSelector)(
134
- store.subscribe,
135
- store.snapshot,
136
- store.snapshot,
137
- selector,
138
- shallow
69
+ var AssistantContext = (0, import_react2.createContext)(null);
70
+ var useAssistantContext = () => {
71
+ const context = (0, import_react2.useContext)(AssistantContext);
72
+ if (!context)
73
+ throw new Error(
74
+ "useAssistantContext must be used within a AssistantProvider"
139
75
  );
140
- };
141
- return [StoreProvider, useStoreContext];
76
+ return context;
142
77
  };
143
78
 
144
- // src/utils/context/ThreadContext.ts
145
- var [ThreadContextProvider, useThreadContext] = createStoreContext("Thread.Provider");
146
-
147
79
  // src/primitives/thread/ThreadIf.tsx
148
80
  var useThreadIf = (props) => {
149
- return useThreadContext("Thread.If", (s) => {
150
- const thread = s.chat;
81
+ const { useThread } = useAssistantContext();
82
+ return useThread((thread) => {
151
83
  if (props.empty === true && thread.messages.length !== 0)
152
84
  return false;
153
85
  if (props.empty === false && thread.messages.length === 0)
@@ -356,76 +288,150 @@ var useChatWithBranches = (chat) => {
356
288
  );
357
289
  return (0, import_react5.useMemo)(
358
290
  () => ({
359
- ...chat,
360
291
  getBranchState,
361
292
  switchToBranch,
362
293
  editAt,
363
294
  reloadAt
364
295
  }),
365
- [chat, getBranchState, switchToBranch, editAt, reloadAt]
296
+ [getBranchState, switchToBranch, editAt, reloadAt]
366
297
  );
367
298
  };
299
+ var hasUpcomingMessage = (thread) => {
300
+ return thread.isLoading && thread.messages[thread.messages.length - 1]?.role !== "assistant";
301
+ };
302
+
303
+ // src/utils/context/ComposerState.ts
304
+ var import_react7 = require("react");
305
+
306
+ // src/utils/context/MessageContext.ts
307
+ var import_react6 = require("react");
308
+ var MessageContext = (0, import_react6.createContext)(null);
309
+ var useMessageContext = () => {
310
+ const context = (0, import_react6.useContext)(MessageContext);
311
+ if (!context)
312
+ throw new Error("useMessageContext must be used within a MessageProvider");
313
+ return context;
314
+ };
315
+
316
+ // src/utils/context/ComposerState.ts
317
+ var useComposerContext = () => {
318
+ const { useComposer: useAssisstantComposer } = useAssistantContext();
319
+ const { useComposer: useMessageComposer } = (0, import_react7.useContext)(MessageContext) ?? {};
320
+ return { useComposer: useMessageComposer ?? useAssisstantComposer };
321
+ };
322
+
323
+ // src/primitives/composer/ComposerIf.tsx
324
+ var useComposerIf = (props) => {
325
+ const { useComposer } = useComposerContext();
326
+ return useComposer((composer) => {
327
+ if (props.editing === true && !composer.isEditing)
328
+ return false;
329
+ if (props.editing === false && composer.isEditing)
330
+ return false;
331
+ return true;
332
+ });
333
+ };
334
+ var ComposerIf = ({ children, ...query }) => {
335
+ const result = useComposerIf(query);
336
+ return result ? children : null;
337
+ };
368
338
 
369
339
  // src/primitives/message/index.ts
370
340
  var message_exports = {};
371
341
  __export(message_exports, {
372
342
  Content: () => MessageContent,
373
- EditableContent: () => MessageEditableContent,
374
343
  If: () => MessageIf,
375
344
  Provider: () => MessageProvider,
376
345
  Root: () => MessageRoot
377
346
  });
378
347
 
379
348
  // src/primitives/message/MessageProvider.tsx
380
- var import_react6 = require("react");
381
-
382
- // src/utils/context/MessageContext.ts
383
- var [MessageContextProvider, useMessageContext] = createStoreContext("Thread.Provider");
384
-
385
- // src/primitives/message/MessageProvider.tsx
349
+ var import_react8 = require("react");
350
+ var import_zustand = require("zustand");
351
+ var import_shallow = require("zustand/react/shallow");
352
+ var getIsLast = (thread, message) => {
353
+ const hasUpcoming = hasUpcomingMessage(thread);
354
+ return hasUpcoming ? message.id === UPCOMING_MESSAGE_ID : thread.messages[thread.messages.length - 1]?.id === message.id;
355
+ };
356
+ var useMessageContext2 = () => {
357
+ const { useBranchObserver } = useAssistantContext();
358
+ const [context] = (0, import_react8.useState)(() => {
359
+ const useMessage = (0, import_zustand.create)(() => ({
360
+ message: null,
361
+ isLast: false,
362
+ isCopied: false,
363
+ isHovering: false,
364
+ setIsCopied: () => {
365
+ },
366
+ setIsHovering: () => {
367
+ },
368
+ branchState: {
369
+ branchId: 0,
370
+ branchCount: 0
371
+ }
372
+ }));
373
+ const useComposer = (0, import_zustand.create)((set, get) => ({
374
+ isEditing: false,
375
+ canCancel: true,
376
+ edit: () => set({
377
+ isEditing: true,
378
+ value: useMessage.getState().message.content
379
+ }),
380
+ cancel: () => set({ isEditing: false }),
381
+ send: () => {
382
+ const message = useMessage.getState().message;
383
+ useBranchObserver.getState().editAt(message, {
384
+ ...message,
385
+ id: void 0,
386
+ // remove id to create a new message
387
+ content: get().value
388
+ });
389
+ set({ isEditing: false });
390
+ },
391
+ value: "",
392
+ setValue: (value) => set({ value })
393
+ }));
394
+ return { useMessage, useComposer };
395
+ });
396
+ return context;
397
+ };
386
398
  var MessageProvider = ({
387
399
  message,
388
400
  children
389
401
  }) => {
390
- const getBranchState = useThreadContext(
391
- "Message.Provider",
392
- (s) => s.chat.getBranchState
393
- );
394
- const [editState, setEditState] = (0, import_react6.useState)({
395
- isEditing: false
396
- });
397
- const [isCopied, setIsCopied] = (0, import_react6.useState)(false);
398
- const [isHovering, setIsHovering] = (0, import_react6.useState)(false);
399
- const branchState = (0, import_react6.useMemo)(
400
- () => getBranchState(message),
401
- [getBranchState, message]
402
- );
403
- return /* @__PURE__ */ React.createElement(
404
- MessageContextProvider,
405
- {
406
- message,
407
- editState,
408
- setEditState,
409
- branchState,
410
- isCopied,
411
- setIsCopied,
412
- isHovering,
413
- setIsHovering
414
- },
415
- children
402
+ const { useThread, useBranchObserver } = useAssistantContext();
403
+ const context = useMessageContext2();
404
+ const branchState = useBranchObserver(
405
+ (0, import_shallow.useShallow)((b) => b.getBranchState(message))
416
406
  );
407
+ const isLast = useThread((thread) => getIsLast(thread, message));
408
+ const [isCopied, setIsCopied] = (0, import_react8.useState)(false);
409
+ const [isHovering, setIsHovering] = (0, import_react8.useState)(false);
410
+ (0, import_react8.useMemo)(() => {
411
+ context.useMessage.setState(
412
+ {
413
+ message,
414
+ isLast,
415
+ isCopied,
416
+ isHovering,
417
+ setIsCopied,
418
+ setIsHovering,
419
+ branchState
420
+ },
421
+ true
422
+ );
423
+ }, [context, message, isLast, isCopied, isHovering, branchState]);
424
+ return /* @__PURE__ */ React.createElement(MessageContext.Provider, { value: context }, children);
417
425
  };
418
426
 
419
427
  // src/primitives/message/MessageRoot.tsx
420
- var import_react7 = require("react");
421
- var import_react_primitive3 = require("@radix-ui/react-primitive");
422
428
  var import_primitive2 = require("@radix-ui/primitive");
423
- var MessageRoot = (0, import_react7.forwardRef)(
429
+ var import_react_primitive3 = require("@radix-ui/react-primitive");
430
+ var import_react9 = require("react");
431
+ var MessageRoot = (0, import_react9.forwardRef)(
424
432
  ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
425
- const setIsHovering = useMessageContext(
426
- "Message.Root",
427
- (s) => s.setIsHovering
428
- );
433
+ const { useMessage } = useMessageContext();
434
+ const setIsHovering = useMessage((s) => s.setIsHovering);
429
435
  const handleMouseEnter = () => {
430
436
  setIsHovering(true);
431
437
  };
@@ -446,22 +452,22 @@ var MessageRoot = (0, import_react7.forwardRef)(
446
452
 
447
453
  // src/primitives/message/MessageIf.tsx
448
454
  var useMessageIf = (props) => {
449
- const thread = useThreadContext("Message.If", (s) => s.chat);
450
- return useMessageContext(
451
- "Message.If",
452
- ({ message, editState: { isEditing }, isCopied, isHovering }) => {
453
- const { branchCount } = thread.getBranchState(message);
455
+ const { useMessage } = useMessageContext();
456
+ return useMessage(
457
+ ({
458
+ message,
459
+ isLast,
460
+ isCopied,
461
+ isHovering,
462
+ branchState: { branchCount }
463
+ }) => {
454
464
  if (props.hasBranches === true && branchCount < 2)
455
465
  return false;
456
466
  if (props.user && message.role !== "user")
457
467
  return false;
458
468
  if (props.assistant && message.role !== "assistant")
459
469
  return false;
460
- if (props.editing === true && !isEditing)
461
- return false;
462
- if (props.editing === false && isEditing)
463
- return false;
464
- if (props.unstable_hoveringOrLast === true && !isHovering && thread.messages[thread.messages.length - 1]?.id !== message.id)
470
+ if (props.lastOrHover === true && !isHovering && !isLast)
465
471
  return false;
466
472
  if (props.copied === true && !isCopied)
467
473
  return false;
@@ -478,60 +484,32 @@ var MessageIf = ({ children, ...query }) => {
478
484
 
479
485
  // src/primitives/message/MessageContent.tsx
480
486
  var MessageContent = () => {
481
- const content = useMessageContext(
482
- "Message.Content",
483
- (s) => s.message.content
484
- );
487
+ const { useMessage } = useMessageContext();
488
+ const content = useMessage((s) => s.message.content);
485
489
  return /* @__PURE__ */ React.createElement(React.Fragment, null, content);
486
490
  };
487
491
 
488
- // src/primitives/message/MessageEditableContent.tsx
489
- var import_react8 = require("react");
490
- var import_primitive3 = require("@radix-ui/primitive");
491
- var import_react_textarea_autosize = __toESM(require("react-textarea-autosize"));
492
- var MessageEditableContent = (0, import_react8.forwardRef)(({ onChange, value, ...rest }, forwardedRef) => {
493
- const [editState, setEditState] = useMessageContext(
494
- "Message.EditableContent",
495
- (s) => [s.editState, s.setEditState]
496
- );
497
- const handleChange = (e) => {
498
- setEditState({ isEditing: true, value: e.target.value });
499
- };
500
- if (!editState.isEditing)
501
- throw new Error(
502
- "Message.EditableContent may only be rendered when edit mode is enabled. Consider wrapping the component in <Message.If editing>."
503
- );
504
- return /* @__PURE__ */ React.createElement(
505
- import_react_textarea_autosize.default,
506
- {
507
- ...rest,
508
- ref: forwardedRef,
509
- onChange: (0, import_primitive3.composeEventHandlers)(onChange, handleChange),
510
- value: editState.value || value
511
- }
512
- );
513
- });
514
-
515
492
  // src/primitives/thread/ThreadMessages.tsx
516
493
  var getComponents = (components) => {
517
494
  return {
518
- EditingUserMessage: components.EditingUserMessage ?? components.UserMessage ?? components.Message,
495
+ EditComposer: components.EditComposer ?? components.UserMessage ?? components.Message,
519
496
  UserMessage: components.UserMessage ?? components.Message,
520
497
  AssistantMessage: components.AssistantMessage ?? components.Message
521
498
  };
522
499
  };
523
500
  var ThreadMessages = ({ components }) => {
524
- const chat = useThreadContext("Thread.Messages", (s) => s.chat);
525
- const messages = chat.messages;
526
- const { UserMessage, EditingUserMessage, AssistantMessage } = getComponents(components);
501
+ const { useThread } = useAssistantContext();
502
+ const thread = useThread();
503
+ const messages = thread.messages;
504
+ const { UserMessage, EditComposer, AssistantMessage } = getComponents(components);
527
505
  if (messages.length === 0)
528
506
  return null;
529
507
  return /* @__PURE__ */ React.createElement(React.Fragment, null, messages.map((message, idx) => {
530
508
  return (
531
509
  // biome-ignore lint/suspicious/noArrayIndexKey: fixes a11y issues with branch navigation
532
- /* @__PURE__ */ React.createElement(MessageProvider, { key: idx, message }, /* @__PURE__ */ React.createElement(MessageIf, { user: true, editing: false }, /* @__PURE__ */ React.createElement(UserMessage, null)), /* @__PURE__ */ React.createElement(MessageIf, { user: true, editing: true }, /* @__PURE__ */ React.createElement(EditingUserMessage, null)), /* @__PURE__ */ React.createElement(MessageIf, { assistant: true }, /* @__PURE__ */ React.createElement(AssistantMessage, null)))
510
+ /* @__PURE__ */ React.createElement(MessageProvider, { key: idx, message }, /* @__PURE__ */ React.createElement(MessageIf, { user: true }, /* @__PURE__ */ React.createElement(ComposerIf, { editing: false }, /* @__PURE__ */ React.createElement(UserMessage, null)), /* @__PURE__ */ React.createElement(ComposerIf, { editing: true }, /* @__PURE__ */ React.createElement(EditComposer, null))), /* @__PURE__ */ React.createElement(MessageIf, { assistant: true }, /* @__PURE__ */ React.createElement(AssistantMessage, null)))
533
511
  );
534
- }), chat.isLoading && chat.messages[chat.messages.length - 1]?.role !== "assistant" && /* @__PURE__ */ React.createElement(
512
+ }), hasUpcomingMessage(thread) && /* @__PURE__ */ React.createElement(
535
513
  MessageProvider,
536
514
  {
537
515
  message: {
@@ -547,20 +525,21 @@ var ThreadMessages = ({ components }) => {
547
525
  // src/primitives/composer/index.ts
548
526
  var composer_exports = {};
549
527
  __export(composer_exports, {
528
+ Cancel: () => ComposerCancel,
529
+ If: () => ComposerIf,
550
530
  Input: () => ComposerInput,
551
531
  Root: () => ComposerRoot,
552
- Send: () => ComposerSend,
553
- Stop: () => ComposerStop
532
+ Send: () => ComposerSend
554
533
  });
555
534
 
556
535
  // src/primitives/composer/ComposerRoot.tsx
557
- var import_react9 = require("react");
558
- var import_react_primitive4 = require("@radix-ui/react-primitive");
559
- var import_primitive4 = require("@radix-ui/primitive");
536
+ var import_primitive3 = require("@radix-ui/primitive");
560
537
  var import_react_compose_refs2 = require("@radix-ui/react-compose-refs");
561
- var ComposerContext = (0, import_react9.createContext)(null);
562
- var useComposerContext = () => {
563
- const context = (0, import_react9.useContext)(ComposerContext);
538
+ var import_react_primitive4 = require("@radix-ui/react-primitive");
539
+ var import_react10 = require("react");
540
+ var ComposerFormContext = (0, import_react10.createContext)(null);
541
+ var useComposerFormContext = () => {
542
+ const context = (0, import_react10.useContext)(ComposerFormContext);
564
543
  if (!context) {
565
544
  throw new Error(
566
545
  "Composer compound components cannot be rendered outside the Composer component"
@@ -568,15 +547,12 @@ var useComposerContext = () => {
568
547
  }
569
548
  return context;
570
549
  };
571
- var ComposerRoot = (0, import_react9.forwardRef)(
550
+ var ComposerRoot = (0, import_react10.forwardRef)(
572
551
  ({ onSubmit, ...rest }, forwardedRef) => {
573
- const handleSubmit = useThreadContext(
574
- "Composer.Root",
575
- (s) => s.chat.handleSubmit
576
- );
577
- const formRef = (0, import_react9.useRef)(null);
552
+ const { useComposer } = useComposerContext();
553
+ const formRef = (0, import_react10.useRef)(null);
578
554
  const ref = (0, import_react_compose_refs2.useComposedRefs)(forwardedRef, formRef);
579
- const composerContextValue = (0, import_react9.useMemo)(
555
+ const composerContextValue = (0, import_react10.useMemo)(
580
556
  () => ({
581
557
  submit: () => formRef.current?.dispatchEvent(
582
558
  new Event("submit", { cancelable: true, bubbles: true })
@@ -584,104 +560,111 @@ var ComposerRoot = (0, import_react9.forwardRef)(
584
560
  }),
585
561
  []
586
562
  );
587
- return /* @__PURE__ */ React.createElement(ComposerContext.Provider, { value: composerContextValue }, /* @__PURE__ */ React.createElement(
563
+ const handleSubmit = (e) => {
564
+ const composerState = useComposer.getState();
565
+ if (!composerState.isEditing)
566
+ return;
567
+ e.preventDefault();
568
+ composerState.send();
569
+ };
570
+ return /* @__PURE__ */ React.createElement(ComposerFormContext.Provider, { value: composerContextValue }, /* @__PURE__ */ React.createElement(
588
571
  import_react_primitive4.Primitive.form,
589
572
  {
590
573
  ...rest,
591
574
  ref,
592
- onSubmit: (0, import_primitive4.composeEventHandlers)(onSubmit, handleSubmit)
575
+ onSubmit: (0, import_primitive3.composeEventHandlers)(onSubmit, handleSubmit)
593
576
  }
594
577
  ));
595
578
  }
596
579
  );
597
580
 
598
581
  // src/primitives/composer/ComposerInput.tsx
599
- var import_react10 = require("react");
582
+ var import_primitive4 = require("@radix-ui/primitive");
600
583
  var import_react_slot = require("@radix-ui/react-slot");
601
- var import_primitive5 = require("@radix-ui/primitive");
602
- var import_react_textarea_autosize2 = __toESM(require("react-textarea-autosize"));
603
- var ComposerInput = (0, import_react10.forwardRef)(({ asChild, onChange, onKeyDown, ...rest }, forwardedRef) => {
604
- const chat = useThreadContext(
605
- "Composer.Input",
606
- ({ chat: { input, handleInputChange, isLoading } }) => ({
607
- input,
608
- handleInputChange,
609
- isLoading
610
- })
611
- );
612
- const Component = asChild ? import_react_slot.Slot : import_react_textarea_autosize2.default;
613
- const composer = useComposerContext();
584
+ var import_react11 = require("react");
585
+ var import_react_textarea_autosize = __toESM(require("react-textarea-autosize"));
586
+ var ComposerInput = (0, import_react11.forwardRef)(({ asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
587
+ const { useThread } = useAssistantContext();
588
+ const isLoading = useThread((t) => t.isLoading);
589
+ const { useComposer } = useComposerContext();
590
+ const value = useComposer((c) => {
591
+ if (!c.isEditing)
592
+ return "";
593
+ return c.value;
594
+ });
595
+ const Component = asChild ? import_react_slot.Slot : import_react_textarea_autosize.default;
596
+ const composerForm = useComposerFormContext();
614
597
  const handleKeyPress = (e) => {
615
- if (chat.isLoading || rest.disabled)
598
+ if (disabled)
599
+ return;
600
+ if (e.key === "Escape") {
601
+ useComposer.getState().cancel();
602
+ }
603
+ if (isLoading)
616
604
  return;
617
605
  if (e.key === "Enter" && e.shiftKey === false) {
618
606
  e.preventDefault();
619
- composer.submit();
607
+ composerForm.submit();
620
608
  }
621
609
  };
622
610
  return /* @__PURE__ */ React.createElement(
623
611
  Component,
624
612
  {
625
- value: chat.input,
613
+ value,
626
614
  ...rest,
627
615
  ref: forwardedRef,
628
- onChange: (0, import_primitive5.composeEventHandlers)(onChange, chat.handleInputChange),
629
- onKeyDown: (0, import_primitive5.composeEventHandlers)(onKeyDown, handleKeyPress)
616
+ disabled,
617
+ onChange: (0, import_primitive4.composeEventHandlers)(onChange, (e) => {
618
+ const composerState = useComposer.getState();
619
+ if (!composerState.isEditing)
620
+ return;
621
+ return composerState.setValue(e.target.value);
622
+ }),
623
+ onKeyDown: (0, import_primitive4.composeEventHandlers)(onKeyDown, handleKeyPress)
630
624
  }
631
625
  );
632
626
  });
633
627
 
634
628
  // src/primitives/composer/ComposerSend.tsx
635
- var import_react11 = require("react");
636
629
  var import_react_primitive5 = require("@radix-ui/react-primitive");
637
- var ComposerSend = (0, import_react11.forwardRef)(
630
+ var import_react12 = require("react");
631
+ var ComposerSend = (0, import_react12.forwardRef)(
638
632
  ({ disabled, ...rest }, ref) => {
639
- const input = useThreadContext("Composer.Send", (s) => s.chat.input);
633
+ const { useComposer } = useComposerContext();
634
+ const hasValue = useComposer((c) => c.isEditing && c.value.length > 0);
640
635
  return /* @__PURE__ */ React.createElement(
641
636
  import_react_primitive5.Primitive.button,
642
637
  {
643
638
  type: "submit",
644
639
  ...rest,
645
640
  ref,
646
- disabled: disabled || input.length === 0
641
+ disabled: disabled || !hasValue
647
642
  }
648
643
  );
649
644
  }
650
645
  );
651
646
 
652
- // src/utils/createActionButton.tsx
653
- var import_react12 = require("react");
647
+ // src/primitives/composer/ComposerCancel.tsx
648
+ var import_primitive5 = require("@radix-ui/primitive");
654
649
  var import_react_primitive6 = require("@radix-ui/react-primitive");
655
- var import_primitive6 = require("@radix-ui/primitive");
656
- var createActionButton = (useActionButton) => {
657
- return (0, import_react12.forwardRef)(
658
- (props, forwardedRef) => {
659
- const onClick = useActionButton(props);
660
- return /* @__PURE__ */ React.createElement(
661
- import_react_primitive6.Primitive.button,
662
- {
663
- type: "button",
664
- disabled: !onClick,
665
- ...props,
666
- ref: forwardedRef,
667
- onClick: (0, import_primitive6.composeEventHandlers)(props.onClick, onClick ?? void 0)
668
- }
669
- );
650
+ var import_react13 = require("react");
651
+ var ComposerCancel = (0, import_react13.forwardRef)(({ disabled, onClick, ...rest }, ref) => {
652
+ const { useComposer } = useComposerContext();
653
+ const hasValue = useComposer((c) => c.canCancel);
654
+ const handleClose = () => {
655
+ useComposer.getState().cancel();
656
+ };
657
+ return /* @__PURE__ */ React.createElement(
658
+ import_react_primitive6.Primitive.button,
659
+ {
660
+ type: "button",
661
+ ...rest,
662
+ ref,
663
+ onClick: (0, import_primitive5.composeEventHandlers)(onClick, handleClose),
664
+ disabled: disabled || !hasValue
670
665
  }
671
666
  );
672
- };
673
-
674
- // src/primitives/composer/ComposerStop.tsx
675
- var useComposerStop = () => {
676
- const [isLoading, stop] = useThreadContext("Composer.Stop", (s) => [
677
- s.chat.isLoading,
678
- s.chat.stop
679
- ]);
680
- if (!isLoading)
681
- return null;
682
- return stop;
683
- };
684
- var ComposerStop = createActionButton(useComposerStop);
667
+ });
685
668
 
686
669
  // src/primitives/branchPicker/index.ts
687
670
  var branchPicker_exports = {};
@@ -695,52 +678,64 @@ __export(branchPicker_exports, {
695
678
 
696
679
  // src/actions/useGoToNextBranch.tsx
697
680
  var useGoToNextBranch = () => {
698
- const switchToBranch = useThreadContext(
699
- "BranchPicker.Next",
700
- (s) => s.chat.switchToBranch
681
+ const { useThread, useBranchObserver } = useAssistantContext();
682
+ const { useComposer, useMessage } = useMessageContext();
683
+ const isLoading = useThread((s) => s.isLoading);
684
+ const isEditing = useComposer((s) => s.isEditing);
685
+ const hasNext = useMessage(
686
+ ({ branchState: { branchId, branchCount } }) => branchId + 1 < branchCount
701
687
  );
702
- const context = useMessageContext("BranchPicker.Next", (s) => {
703
- const {
704
- message: message2,
705
- editState: { isEditing },
706
- branchState: { branchId: branchId2, branchCount }
707
- } = s;
708
- if (isEditing || branchCount <= 1 || branchId2 + 1 >= branchCount)
709
- return null;
710
- return { message: message2, branchId: branchId2 };
711
- });
712
- if (!context)
688
+ if (isLoading || isEditing || !hasNext)
713
689
  return null;
714
- const { message, branchId } = context;
715
690
  return () => {
716
- switchToBranch(message, branchId + 1);
691
+ const {
692
+ message,
693
+ branchState: { branchId }
694
+ } = useMessage.getState();
695
+ useBranchObserver.getState().switchToBranch(message, branchId + 1);
717
696
  };
718
697
  };
719
698
 
699
+ // src/utils/createActionButton.tsx
700
+ var import_react14 = require("react");
701
+ var import_react_primitive7 = require("@radix-ui/react-primitive");
702
+ var import_primitive6 = require("@radix-ui/primitive");
703
+ var createActionButton = (useActionButton) => {
704
+ return (0, import_react14.forwardRef)(
705
+ (props, forwardedRef) => {
706
+ const onClick = useActionButton(props);
707
+ return /* @__PURE__ */ React.createElement(
708
+ import_react_primitive7.Primitive.button,
709
+ {
710
+ type: "button",
711
+ disabled: !onClick,
712
+ ...props,
713
+ ref: forwardedRef,
714
+ onClick: (0, import_primitive6.composeEventHandlers)(props.onClick, onClick ?? void 0)
715
+ }
716
+ );
717
+ }
718
+ );
719
+ };
720
+
720
721
  // src/primitives/branchPicker/BranchPickerNext.tsx
721
722
  var BranchPickerNext = createActionButton(useGoToNextBranch);
722
723
 
723
724
  // src/actions/useGoToPreviousBranch.tsx
724
725
  var useGoToPreviousBranch = () => {
725
- const switchToBranch = useThreadContext(
726
- "BranchPicker.Previous",
727
- (s) => s.chat.switchToBranch
728
- );
729
- const context = useMessageContext("BranchPicker.Previous", (s) => {
730
- const {
731
- message: message2,
732
- editState: { isEditing },
733
- branchState: { branchId: branchId2, branchCount }
734
- } = s;
735
- if (isEditing || branchCount <= 1 || branchId2 <= 0)
736
- return null;
737
- return { message: message2, branchId: branchId2 };
738
- });
739
- if (!context)
726
+ const { useThread, useBranchObserver } = useAssistantContext();
727
+ const { useComposer, useMessage } = useMessageContext();
728
+ const isLoading = useThread((s) => s.isLoading);
729
+ const isEditing = useComposer((s) => s.isEditing);
730
+ const hasNext = useMessage(({ branchState: { branchId } }) => branchId > 0);
731
+ if (isLoading || isEditing || !hasNext)
740
732
  return null;
741
- const { message, branchId } = context;
742
733
  return () => {
743
- switchToBranch(message, branchId - 1);
734
+ const {
735
+ message,
736
+ branchState: { branchId }
737
+ } = useMessage.getState();
738
+ useBranchObserver.getState().switchToBranch(message, branchId - 1);
744
739
  };
745
740
  };
746
741
 
@@ -749,27 +744,23 @@ var BranchPickerPrevious = createActionButton(useGoToPreviousBranch);
749
744
 
750
745
  // src/primitives/branchPicker/BranchPickerCount.tsx
751
746
  var BranchPickerCount = () => {
752
- const branchCount = useMessageContext(
753
- "BranchPicker.Count",
754
- (s) => s.branchState.branchCount
755
- );
747
+ const { useMessage } = useMessageContext();
748
+ const branchCount = useMessage((s) => s.branchState.branchCount);
756
749
  return /* @__PURE__ */ React.createElement(React.Fragment, null, branchCount);
757
750
  };
758
751
 
759
752
  // src/primitives/branchPicker/BranchPickerNumber.tsx
760
753
  var BranchPickerNumber = () => {
761
- const branchId = useMessageContext(
762
- "BranchPicker.Number",
763
- (s) => s.branchState.branchId
764
- );
754
+ const { useMessage } = useMessageContext();
755
+ const branchId = useMessage((s) => s.branchState.branchId);
765
756
  return /* @__PURE__ */ React.createElement(React.Fragment, null, branchId + 1);
766
757
  };
767
758
 
768
759
  // src/primitives/branchPicker/BranchPickerRoot.tsx
769
- var import_react_primitive7 = require("@radix-ui/react-primitive");
770
- var import_react13 = require("react");
771
- var BranchPickerRoot = (0, import_react13.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
772
- return /* @__PURE__ */ React.createElement(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0 }, /* @__PURE__ */ React.createElement(import_react_primitive7.Primitive.div, { ...rest, ref }));
760
+ var import_react_primitive8 = require("@radix-ui/react-primitive");
761
+ var import_react15 = require("react");
762
+ var BranchPickerRoot = (0, import_react15.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
763
+ return /* @__PURE__ */ React.createElement(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0 }, /* @__PURE__ */ React.createElement(import_react_primitive8.Primitive.div, { ...rest, ref }));
773
764
  });
774
765
 
775
766
  // src/primitives/actionBar/index.ts
@@ -782,28 +773,47 @@ __export(actionBar_exports, {
782
773
  });
783
774
 
784
775
  // src/primitives/actionBar/ActionBarRoot.tsx
785
- var import_react_primitive8 = require("@radix-ui/react-primitive");
786
- var import_react14 = require("react");
787
- var ActionBarRoot = (0, import_react14.forwardRef)(({ ...rest }, ref) => {
788
- return /* @__PURE__ */ React.createElement(import_react_primitive8.Primitive.div, { ...rest, ref });
776
+ var import_react_primitive9 = require("@radix-ui/react-primitive");
777
+ var import_react16 = require("react");
778
+ var ActionBarRoot = (0, import_react16.forwardRef)(({ hideWhenBusy, autohide, autohideFloat, ...rest }, ref) => {
779
+ const { useThread } = useAssistantContext();
780
+ const { useMessage } = useMessageContext();
781
+ const hideAndfloatStatus = useMessage((m) => {
782
+ const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
783
+ if (!autohideEnabled)
784
+ return "normal" /* Normal */;
785
+ if (!m.isHovering)
786
+ return "hidden" /* Hidden */;
787
+ if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branchState.branchCount <= 1)
788
+ return "floating" /* Floating */;
789
+ return "normal" /* Normal */;
790
+ });
791
+ const busy = useThread((t) => t.isLoading);
792
+ if (hideWhenBusy && busy)
793
+ return null;
794
+ if (hideAndfloatStatus === "hidden" /* Hidden */)
795
+ return null;
796
+ return /* @__PURE__ */ React.createElement(
797
+ import_react_primitive9.Primitive.div,
798
+ {
799
+ "data-floating": hideAndfloatStatus === "floating" /* Floating */,
800
+ ...rest,
801
+ ref
802
+ }
803
+ );
789
804
  });
790
805
 
791
806
  // src/actions/useCopyMessage.tsx
792
807
  var useCopyMessage = ({ copiedDuration = 3e3 }) => {
793
- const context = useMessageContext("ActionBar.Copy", (s) => {
794
- const {
795
- editState: { isEditing },
796
- message: { content: content2 },
797
- setIsCopied: setIsCopied2
798
- } = s;
799
- if (isEditing)
800
- return null;
801
- return { content: content2, setIsCopied: setIsCopied2 };
802
- });
803
- if (!context)
808
+ const { useMessage, useComposer } = useMessageContext();
809
+ const isEditing = useComposer((s) => s.isEditing);
810
+ if (isEditing)
804
811
  return null;
805
- const { content, setIsCopied } = context;
806
812
  return () => {
813
+ const {
814
+ message: { content },
815
+ setIsCopied
816
+ } = useMessage.getState();
807
817
  navigator.clipboard.writeText(content);
808
818
  setIsCopied(true);
809
819
  setTimeout(() => setIsCopied(false), copiedDuration);
@@ -815,19 +825,15 @@ var ActionBarCopy = createActionButton(useCopyMessage);
815
825
 
816
826
  // src/actions/useReloadMessage.tsx
817
827
  var useReloadMessage = () => {
818
- const [isLoading, reloadAt] = useThreadContext("ActionBar.Reload", (s) => [
819
- s.chat.isLoading,
820
- s.chat.reloadAt
821
- ]);
822
- const message = useMessageContext("ActionBar.Reload", (s) => {
823
- const message2 = s.message;
824
- if (message2.role !== "assistant" || isLoading)
825
- return null;
826
- return message2;
827
- });
828
- if (!message)
828
+ const { useThread, useBranchObserver } = useAssistantContext();
829
+ const { useMessage } = useMessageContext();
830
+ const isLoading = useThread((s) => s.isLoading);
831
+ const isAssistant = useMessage((s) => s.message.role === "assistant");
832
+ if (isLoading || !isAssistant)
829
833
  return null;
830
- return () => reloadAt(message);
834
+ return () => {
835
+ useBranchObserver.getState().reloadAt(useMessage.getState().message);
836
+ };
831
837
  };
832
838
 
833
839
  // src/primitives/actionBar/ActionBarReload.tsx
@@ -835,115 +841,137 @@ var ActionBarReload = createActionButton(useReloadMessage);
835
841
 
836
842
  // src/actions/useBeginMessageEdit.tsx
837
843
  var useBeginMessageEdit = () => {
838
- const context = useMessageContext("ActionBar.Edit", (s) => {
839
- const {
840
- message: { content: content2 },
841
- editState: { isEditing },
842
- setEditState: setEditState2
843
- } = s;
844
- if (isEditing)
845
- return null;
846
- return { content: content2, setEditState: setEditState2 };
847
- });
848
- if (!context)
844
+ const { useMessage, useComposer } = useMessageContext();
845
+ const isUser = useMessage((s) => s.message.role === "user");
846
+ const isEditing = useComposer((s) => s.isEditing);
847
+ if (!isUser || isEditing)
849
848
  return null;
850
- const { content, setEditState } = context;
851
849
  return () => {
852
- setEditState({ isEditing: true, value: content });
850
+ const { edit } = useComposer.getState();
851
+ edit();
853
852
  };
854
853
  };
855
854
 
856
855
  // src/primitives/actionBar/ActionBarEdit.tsx
857
856
  var ActionBarEdit = createActionButton(useBeginMessageEdit);
858
857
 
859
- // src/primitives/editBar/index.ts
860
- var editBar_exports = {};
861
- __export(editBar_exports, {
862
- Cancel: () => EditBarCancel,
863
- Root: () => EditBarRoot,
864
- Save: () => EditBarSave
865
- });
866
-
867
- // src/primitives/editBar/EditBarRoot.tsx
868
- var import_react_primitive9 = require("@radix-ui/react-primitive");
869
- var import_react15 = require("react");
870
- var EditBarRoot = (0, import_react15.forwardRef)(
871
- ({ ...rest }, ref) => {
872
- return /* @__PURE__ */ React.createElement(import_react_primitive9.Primitive.div, { ...rest, ref });
873
- }
874
- );
875
-
876
- // src/actions/useSaveMessageEdit.tsx
877
- var useSaveMessageEdit = () => {
878
- const chat = useThreadContext("EditBar.Save", (s) => s.chat);
879
- const context = useMessageContext("EditBar.Save", (s) => {
880
- const { message: message2, editState, setEditState: setEditState2 } = s;
881
- if (!editState.isEditing)
882
- return null;
883
- return { message: message2, content: editState.value, setEditState: setEditState2 };
884
- });
885
- if (!context)
886
- return null;
887
- const { message, content, setEditState } = context;
888
- return () => {
889
- chat.editAt(message, {
890
- ...message,
891
- id: void 0,
892
- // remove id to create a new message
893
- content
894
- });
895
- setEditState({ isEditing: false });
896
- };
897
- };
898
-
899
- // src/primitives/editBar/EditBarSave.tsx
900
- var EditBarSave = createActionButton(useSaveMessageEdit);
901
-
902
- // src/actions/useCancelMessageEdit.tsx
903
- var useCancelMessageEdit = () => {
904
- const context = useMessageContext("EditBar.Cancel", (s) => {
905
- const {
906
- editState: { isEditing },
907
- setEditState: setEditState2
908
- } = s;
909
- if (!isEditing)
910
- return null;
911
- return { setEditState: setEditState2 };
858
+ // src/vercel/VercelAIAssistantProvider.tsx
859
+ var import_react17 = require("react");
860
+ var import_zustand2 = require("zustand");
861
+ var useAIAssistantContext = () => {
862
+ const [context] = (0, import_react17.useState)(() => {
863
+ const useThread = (0, import_zustand2.create)()(() => ({
864
+ messages: [],
865
+ setMessages: () => {
866
+ },
867
+ isLoading: false,
868
+ reload: async () => {
869
+ },
870
+ append: async () => {
871
+ },
872
+ stop: () => {
873
+ }
874
+ }));
875
+ const useComposer = (0, import_zustand2.create)()(() => ({
876
+ isEditing: true,
877
+ canCancel: false,
878
+ value: "",
879
+ setValue: () => {
880
+ },
881
+ edit: () => {
882
+ throw new Error("Not implemented");
883
+ },
884
+ send: () => {
885
+ useThread.getState().append({
886
+ content: useComposer.getState().value,
887
+ role: "user",
888
+ createdAt: /* @__PURE__ */ new Date()
889
+ });
890
+ useComposer.getState().setValue("");
891
+ },
892
+ cancel: () => {
893
+ useThread.getState().stop();
894
+ }
895
+ }));
896
+ const useBranchObserver = (0, import_zustand2.create)()(() => ({
897
+ getBranchState: () => ({
898
+ branchId: 0,
899
+ branchCount: 0
900
+ }),
901
+ switchToBranch: () => {
902
+ },
903
+ editAt: async () => {
904
+ },
905
+ reloadAt: async () => {
906
+ }
907
+ }));
908
+ return { useThread, useComposer, useBranchObserver };
912
909
  });
913
- if (!context)
914
- return null;
915
- const { setEditState } = context;
916
- return () => {
917
- setEditState({ isEditing: false });
918
- };
910
+ return context;
919
911
  };
920
-
921
- // src/primitives/editBar/EditBarCancel.tsx
922
- var EditBarCancel = createActionButton(useCancelMessageEdit);
923
-
924
- // src/vercel/VercelAIThreadProvider.tsx
925
- var VercelAIThreadProvider = ({
912
+ var VercelAIAssistantProvider = ({
926
913
  chat,
927
914
  children
928
915
  }) => {
916
+ const context = useAIAssistantContext();
917
+ (0, import_react17.useMemo)(() => {
918
+ context.useThread.setState(
919
+ {
920
+ messages: chat.messages,
921
+ setMessages: (value) => {
922
+ chat.setMessages(value);
923
+ },
924
+ isLoading: chat.isLoading,
925
+ reload: async () => {
926
+ await chat.reload();
927
+ },
928
+ append: async (message) => {
929
+ await chat.append(message);
930
+ },
931
+ stop: () => {
932
+ const lastMessage = chat.messages.at(-1);
933
+ chat.stop();
934
+ if (lastMessage?.role === "user") {
935
+ chat.setInput(lastMessage.content);
936
+ }
937
+ }
938
+ },
939
+ true
940
+ );
941
+ }, [context, chat]);
942
+ (0, import_react17.useMemo)(() => {
943
+ context.useComposer.setState({
944
+ canCancel: chat.isLoading,
945
+ value: chat.input,
946
+ setValue: chat.setInput
947
+ });
948
+ }, [context, chat.isLoading, chat.input, chat.setInput]);
929
949
  const branches = useChatWithBranches(chat);
930
- return /* @__PURE__ */ React.createElement(ThreadContextProvider, { chat: branches }, children);
950
+ (0, import_react17.useMemo)(() => {
951
+ context.useBranchObserver.setState(
952
+ {
953
+ getBranchState: (message) => branches.getBranchState(message),
954
+ switchToBranch: (message, branchId) => branches.switchToBranch(message, branchId),
955
+ editAt: async (message, newMessage) => branches.editAt(message, newMessage),
956
+ reloadAt: async (message) => branches.reloadAt(message)
957
+ },
958
+ true
959
+ );
960
+ }, [context, branches]);
961
+ return /* @__PURE__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
931
962
  };
932
963
  // Annotate the CommonJS export names for ESM import in node:
933
964
  0 && (module.exports = {
934
965
  ActionBarPrimitive,
935
966
  BranchPickerPrimitive,
936
967
  ComposerPrimitive,
937
- EditBarPrimitive,
938
968
  MessagePrimitive,
939
969
  ThreadPrimitive,
940
970
  VercelAIThreadProvider,
941
971
  unstable_useMessageContext,
942
972
  useBeginMessageEdit,
943
- useCancelMessageEdit,
944
973
  useCopyMessage,
945
974
  useGoToNextBranch,
946
975
  useGoToPreviousBranch,
947
- useReloadMessage,
948
- useSaveMessageEdit
976
+ useReloadMessage
949
977
  });