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