@assistant-ui/react 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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
  });