@assistant-ui/react 0.0.1

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.mjs ADDED
@@ -0,0 +1,852 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/primitives/thread/index.ts
8
+ var thread_exports = {};
9
+ __export(thread_exports, {
10
+ Empty: () => ThreadEmpty,
11
+ If: () => ThreadIf,
12
+ Messages: () => ThreadMessages,
13
+ Provider: () => ThreadProvider,
14
+ Root: () => ThreadRoot,
15
+ Viewport: () => ThreadViewport
16
+ });
17
+
18
+ // src/utils/hooks/useBranches.tsx
19
+ import { useCallback, useMemo, useRef } from "react";
20
+ var ROOT_ID = "__ROOT_ID__";
21
+ var UPCOMING_MESSAGE_ID = "__UPCOMING_MESSAGE_ID__";
22
+ var updateBranchData = (data, messages) => {
23
+ for (let i = 0; i < messages.length; i++) {
24
+ const child = messages[i];
25
+ const childId = child.id;
26
+ const parentId = messages[i - 1]?.id ?? ROOT_ID;
27
+ data.parentMap.set(childId, parentId);
28
+ let parentArray = data.branchMap.get(parentId);
29
+ if (!parentArray) {
30
+ data.branchMap.set(parentId, [childId]);
31
+ } else if (!parentArray.includes(childId)) {
32
+ parentArray.push(childId);
33
+ }
34
+ data.snapshots.set(childId, messages);
35
+ }
36
+ };
37
+ var getParentId = (data, messages, message) => {
38
+ if (message.id === UPCOMING_MESSAGE_ID) {
39
+ const parent = messages.at(-1);
40
+ if (!parent)
41
+ return ROOT_ID;
42
+ return parent.id;
43
+ }
44
+ const parentId = data.parentMap.get(message.id);
45
+ if (!parentId)
46
+ throw new Error("Unexpected: Message parent not found");
47
+ return parentId;
48
+ };
49
+ var getBranchStateImpl = (data, messages, message) => {
50
+ const parentId = getParentId(data, messages, message);
51
+ const branches = data.branchMap.get(parentId) ?? [];
52
+ const branchId = message.id === UPCOMING_MESSAGE_ID ? branches.length : branches.indexOf(message.id);
53
+ if (branchId === -1)
54
+ throw new Error("Unexpected: Message not found in parent children");
55
+ const upcomingOffset = message.id === UPCOMING_MESSAGE_ID ? 1 : 0;
56
+ return {
57
+ branchId,
58
+ branchCount: branches.length + upcomingOffset
59
+ };
60
+ };
61
+ var switchToBranchImpl = (data, messages, message, branchId) => {
62
+ const parentId = getParentId(data, messages, message);
63
+ const branches = data.branchMap.get(parentId);
64
+ if (!branches)
65
+ throw new Error("Unexpected: Parent children not found");
66
+ const newMessageId = branches[branchId];
67
+ if (!newMessageId)
68
+ throw new Error("Unexpected: Requested branch not found");
69
+ if (branchId < 0 || branchId >= branches.length)
70
+ throw new Error("Switch to branch called with a branch index out of range");
71
+ if (newMessageId === message.id)
72
+ return messages;
73
+ const snapshot = data.snapshots.get(newMessageId);
74
+ if (!snapshot)
75
+ throw new Error("Unexpected: Branch snapshot not found");
76
+ return snapshot;
77
+ };
78
+ var sliceMessagesUntil = (messages, message) => {
79
+ if (message.id === UPCOMING_MESSAGE_ID)
80
+ return messages;
81
+ const messageIdx = messages.findIndex((m) => m.id === message.id);
82
+ if (messageIdx === -1)
83
+ throw new Error("Unexpected: Message not found");
84
+ return messages.slice(0, messageIdx);
85
+ };
86
+ var useChatWithBranches = (chat) => {
87
+ const data = useRef({
88
+ parentMap: /* @__PURE__ */ new Map(),
89
+ branchMap: /* @__PURE__ */ new Map(),
90
+ snapshots: /* @__PURE__ */ new Map()
91
+ }).current;
92
+ updateBranchData(data, chat.messages);
93
+ const getBranchState = useCallback(
94
+ (message) => {
95
+ return getBranchStateImpl(data, chat.messages, message);
96
+ },
97
+ [chat.messages]
98
+ );
99
+ const switchToBranch = useCallback(
100
+ (message, branchId) => {
101
+ const newMessages = switchToBranchImpl(
102
+ data,
103
+ chat.messages,
104
+ message,
105
+ branchId
106
+ );
107
+ chat.setMessages(newMessages);
108
+ },
109
+ [chat.messages, chat.setMessages]
110
+ );
111
+ const reloadAt = useCallback(
112
+ async (message) => {
113
+ const newMessages = sliceMessagesUntil(chat.messages, message);
114
+ chat.setMessages(newMessages);
115
+ await chat.reload();
116
+ },
117
+ [chat.messages, chat.setMessages, chat.reload]
118
+ );
119
+ const editAt = useCallback(
120
+ async (message, newMessage) => {
121
+ const newMessages = sliceMessagesUntil(chat.messages, message);
122
+ chat.setMessages(newMessages);
123
+ await chat.append(newMessage);
124
+ },
125
+ [chat.messages, chat.setMessages, chat.append]
126
+ );
127
+ return useMemo(
128
+ () => ({
129
+ ...chat,
130
+ getBranchState,
131
+ switchToBranch,
132
+ editAt,
133
+ reloadAt
134
+ }),
135
+ [chat, getBranchState, switchToBranch, editAt, reloadAt]
136
+ );
137
+ };
138
+
139
+ // src/utils/context/createStoreContext.tsx
140
+ import {
141
+ createContext,
142
+ useContext,
143
+ useMemo as useMemo2,
144
+ useState
145
+ } from "react";
146
+ import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector";
147
+
148
+ // src/utils/context/shallow.ts
149
+ function shallow(objA, objB) {
150
+ if (Object.is(objA, objB)) {
151
+ return true;
152
+ }
153
+ if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) {
154
+ return false;
155
+ }
156
+ const keysA = Object.keys(objA);
157
+ if (keysA.length !== Object.keys(objB).length) {
158
+ return false;
159
+ }
160
+ for (const keyA of keysA) {
161
+ if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) {
162
+ return false;
163
+ }
164
+ }
165
+ return true;
166
+ }
167
+
168
+ // src/utils/context/createStoreContext.tsx
169
+ var createStoreContext = (providerName) => {
170
+ const context = createContext(null);
171
+ const StoreProvider = ({ children, ...rest }) => {
172
+ const unstableContext = rest;
173
+ const [store] = useState(() => {
174
+ let state = unstableContext;
175
+ const listeners = /* @__PURE__ */ new Set();
176
+ return {
177
+ subscribe: (cb) => {
178
+ listeners.add(cb);
179
+ return () => listeners.delete(cb);
180
+ },
181
+ emit: () => {
182
+ listeners.forEach((l) => l());
183
+ },
184
+ snapshot: () => {
185
+ return state;
186
+ },
187
+ setState: (value) => {
188
+ state = value;
189
+ store.emit();
190
+ }
191
+ };
192
+ });
193
+ useMemo2(
194
+ () => store.setState(unstableContext),
195
+ Object.values(unstableContext)
196
+ );
197
+ return /* @__PURE__ */ React.createElement(context.Provider, { value: store }, children);
198
+ };
199
+ const useStoreContext = (consumerName, selector) => {
200
+ const store = useContext(context);
201
+ if (!store)
202
+ throw new Error(
203
+ `${consumerName} can only be used inside ${providerName}.`
204
+ );
205
+ return useSyncExternalStoreWithSelector(
206
+ store.subscribe,
207
+ store.snapshot,
208
+ store.snapshot,
209
+ selector,
210
+ shallow
211
+ );
212
+ };
213
+ return [StoreProvider, useStoreContext];
214
+ };
215
+
216
+ // src/utils/context/ThreadContext.ts
217
+ var [ThreadContextProvider, useThreadContext] = createStoreContext("Thread.Provider");
218
+
219
+ // src/primitives/thread/ThreadProvider.tsx
220
+ var ThreadProvider = ({ chat, children }) => {
221
+ const branches = useChatWithBranches(chat);
222
+ return /* @__PURE__ */ React.createElement(ThreadContextProvider, { chat: branches }, children);
223
+ };
224
+
225
+ // src/primitives/thread/ThreadRoot.tsx
226
+ import { forwardRef } from "react";
227
+ import { Primitive } from "@radix-ui/react-primitive";
228
+ var ThreadRoot = forwardRef(
229
+ (props, ref) => {
230
+ return /* @__PURE__ */ React.createElement(Primitive.div, { ...props, ref });
231
+ }
232
+ );
233
+
234
+ // src/primitives/thread/ThreadIf.tsx
235
+ var useThreadIf = (props) => {
236
+ return useThreadContext("Thread.If", (s) => {
237
+ const thread = s.chat;
238
+ if (props.empty === true && thread.messages.length !== 0)
239
+ return false;
240
+ if (props.empty === false && thread.messages.length === 0)
241
+ return false;
242
+ if (props.busy === true && !thread.isLoading)
243
+ return false;
244
+ if (props.busy === false && thread.isLoading)
245
+ return false;
246
+ return true;
247
+ });
248
+ };
249
+ var ThreadIf = ({ children, ...query }) => {
250
+ const result = useThreadIf(query);
251
+ return result ? children : null;
252
+ };
253
+
254
+ // src/primitives/thread/ThreadEmpty.tsx
255
+ var ThreadEmpty = ({ children }) => {
256
+ return /* @__PURE__ */ React.createElement(ThreadIf, { empty: true }, children);
257
+ };
258
+
259
+ // src/primitives/thread/ThreadViewport.tsx
260
+ import { forwardRef as forwardRef2, useRef as useRef3, useState as useState2 } from "react";
261
+ import { Primitive as Primitive2 } from "@radix-ui/react-primitive";
262
+ import { useComposedRefs } from "@radix-ui/react-compose-refs";
263
+ import { composeEventHandlers } from "@radix-ui/primitive";
264
+
265
+ // src/utils/hooks/useOnResizeContent.tsx
266
+ import { useLayoutEffect, useRef as useRef2 } from "react";
267
+ var useOnResizeContent = (ref, callback) => {
268
+ const callbackRef = useRef2(callback);
269
+ callbackRef.current = callback;
270
+ useLayoutEffect(() => {
271
+ const el = ref.current;
272
+ if (!el)
273
+ return;
274
+ const resizeObserver = new ResizeObserver(() => {
275
+ callbackRef.current();
276
+ });
277
+ const mutationObserver = new MutationObserver((mutations) => {
278
+ mutations.forEach((mutation) => {
279
+ mutation.addedNodes.forEach((node) => {
280
+ if (node instanceof HTMLElement) {
281
+ resizeObserver.observe(node);
282
+ }
283
+ });
284
+ mutation.removedNodes.forEach((node) => {
285
+ if (node instanceof HTMLElement) {
286
+ resizeObserver.unobserve(node);
287
+ }
288
+ });
289
+ });
290
+ callbackRef.current();
291
+ });
292
+ mutationObserver.observe(el, { childList: true });
293
+ return () => {
294
+ resizeObserver.disconnect();
295
+ mutationObserver.disconnect();
296
+ };
297
+ }, []);
298
+ };
299
+
300
+ // src/primitives/thread/ThreadViewport.tsx
301
+ var ThreadViewport = forwardRef2(({ onScroll, children, ...rest }, forwardedRef) => {
302
+ const divRef = useRef3(null);
303
+ const ref = useComposedRefs(forwardedRef, divRef);
304
+ const [isAtBottom, setIsAtBottom] = useState2(true);
305
+ useOnResizeContent(divRef, () => {
306
+ const div = divRef.current;
307
+ if (!div || !isAtBottom)
308
+ return;
309
+ div.scrollTop = div.scrollHeight;
310
+ });
311
+ const handleScroll = () => {
312
+ const div = divRef.current;
313
+ if (!div)
314
+ return;
315
+ setIsAtBottom(div.scrollHeight - div.scrollTop <= div.clientHeight);
316
+ };
317
+ return /* @__PURE__ */ React.createElement(
318
+ Primitive2.div,
319
+ {
320
+ ...rest,
321
+ onScroll: composeEventHandlers(onScroll, handleScroll),
322
+ ref
323
+ },
324
+ children
325
+ );
326
+ });
327
+
328
+ // src/primitives/message/index.ts
329
+ var message_exports = {};
330
+ __export(message_exports, {
331
+ Content: () => MessageContent,
332
+ EditableContent: () => MessageEditableContent,
333
+ If: () => MessageIf,
334
+ Provider: () => MessageProvider,
335
+ Root: () => MessageRoot
336
+ });
337
+
338
+ // src/primitives/message/MessageProvider.tsx
339
+ import { useMemo as useMemo3, useState as useState3 } from "react";
340
+
341
+ // src/utils/context/MessageContext.ts
342
+ var [MessageContextProvider, useMessageContext] = createStoreContext("Thread.Provider");
343
+
344
+ // src/primitives/message/MessageProvider.tsx
345
+ var MessageProvider = ({
346
+ message,
347
+ children
348
+ }) => {
349
+ const getBranchState = useThreadContext(
350
+ "Message.Provider",
351
+ (s) => s.chat.getBranchState
352
+ );
353
+ const [editState, setEditState] = useState3({
354
+ isEditing: false
355
+ });
356
+ const [isCopied, setIsCopied] = useState3(false);
357
+ const [isHovering, setIsHovering] = useState3(false);
358
+ const branchState = useMemo3(
359
+ () => getBranchState(message),
360
+ [getBranchState, message]
361
+ );
362
+ return /* @__PURE__ */ React.createElement(
363
+ MessageContextProvider,
364
+ {
365
+ message,
366
+ editState,
367
+ setEditState,
368
+ branchState,
369
+ isCopied,
370
+ setIsCopied,
371
+ isHovering,
372
+ setIsHovering
373
+ },
374
+ children
375
+ );
376
+ };
377
+
378
+ // src/primitives/message/MessageRoot.tsx
379
+ import { forwardRef as forwardRef3 } from "react";
380
+ import { Primitive as Primitive3 } from "@radix-ui/react-primitive";
381
+ import { composeEventHandlers as composeEventHandlers2 } from "@radix-ui/primitive";
382
+ var MessageRoot = forwardRef3(
383
+ ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
384
+ const setIsHovering = useMessageContext(
385
+ "Message.Root",
386
+ (s) => s.setIsHovering
387
+ );
388
+ const handleMouseEnter = () => {
389
+ setIsHovering(true);
390
+ };
391
+ const handleMouseLeave = () => {
392
+ setIsHovering(false);
393
+ };
394
+ return /* @__PURE__ */ React.createElement(
395
+ Primitive3.div,
396
+ {
397
+ ...rest,
398
+ ref,
399
+ onMouseEnter: composeEventHandlers2(onMouseEnter, handleMouseEnter),
400
+ onMouseLeave: composeEventHandlers2(onMouseLeave, handleMouseLeave)
401
+ }
402
+ );
403
+ }
404
+ );
405
+
406
+ // src/primitives/message/MessageIf.tsx
407
+ var useMessageIf = (props) => {
408
+ const thread = useThreadContext("Message.If", (s) => s.chat);
409
+ return useMessageContext(
410
+ "Message.If",
411
+ ({ message, editState: { isEditing }, isCopied, isHovering }) => {
412
+ const { branchCount } = thread.getBranchState(message);
413
+ if (props.hasBranches === true && branchCount < 2)
414
+ return false;
415
+ if (props.user && message.role !== "user")
416
+ return false;
417
+ if (props.assistant && message.role !== "assistant")
418
+ return false;
419
+ if (props.editing === true && !isEditing)
420
+ return false;
421
+ if (props.editing === false && isEditing)
422
+ return false;
423
+ if (props.unstable_hoveringOrLast === true && !isHovering && thread.messages[thread.messages.length - 1]?.id !== message.id)
424
+ return false;
425
+ if (props.copied === true && !isCopied)
426
+ return false;
427
+ if (props.copied === false && isCopied)
428
+ return false;
429
+ return true;
430
+ }
431
+ );
432
+ };
433
+ var MessageIf = ({ children, ...query }) => {
434
+ const result = useMessageIf(query);
435
+ return result ? children : null;
436
+ };
437
+
438
+ // src/primitives/message/MessageContent.tsx
439
+ var MessageContent = () => {
440
+ const content = useMessageContext(
441
+ "Message.Content",
442
+ (s) => s.message.content
443
+ );
444
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, content);
445
+ };
446
+
447
+ // src/primitives/message/MessageEditableContent.tsx
448
+ import { useComposedRefs as useComposedRefs2 } from "@radix-ui/react-compose-refs";
449
+ import { forwardRef as forwardRef4, useRef as useRef4 } from "react";
450
+
451
+ // src/utils/hooks/useAutosize.tsx
452
+ import { useLayoutEffect as useLayoutEffect2 } from "react";
453
+ var useAutosize = (ref) => {
454
+ const el = ref.current;
455
+ useLayoutEffect2(() => {
456
+ const el2 = ref.current;
457
+ if (!el2)
458
+ return;
459
+ const callback = () => {
460
+ el2.style.height = "0px";
461
+ el2.style.height = el2.scrollHeight + "px";
462
+ };
463
+ el2.addEventListener("input", callback);
464
+ callback();
465
+ return () => {
466
+ el2.removeEventListener("input", callback);
467
+ };
468
+ }, [ref, el]);
469
+ };
470
+
471
+ // src/primitives/message/MessageEditableContent.tsx
472
+ import { composeEventHandlers as composeEventHandlers3 } from "@radix-ui/primitive";
473
+ var MessageEditableContent = forwardRef4(({ onChange, value, ...rest }, forwardedRef) => {
474
+ const textareaRef = useRef4(null);
475
+ const ref = useComposedRefs2(forwardedRef, textareaRef);
476
+ useAutosize(textareaRef);
477
+ const [editState, setEditState] = useMessageContext(
478
+ "Message.EditableContent",
479
+ (s) => [s.editState, s.setEditState]
480
+ );
481
+ const handleChange = (e) => {
482
+ setEditState({ isEditing: true, value: e.target.value });
483
+ };
484
+ if (!editState.isEditing)
485
+ throw new Error(
486
+ "Message.EditableContent may only be rendered when edit mode is enabled. Consider wrapping the component in <Message.If editing>."
487
+ );
488
+ return /* @__PURE__ */ React.createElement(
489
+ "textarea",
490
+ {
491
+ ...rest,
492
+ ref,
493
+ onChange: composeEventHandlers3(onChange, handleChange),
494
+ value: editState.value || value
495
+ }
496
+ );
497
+ });
498
+
499
+ // src/primitives/thread/ThreadMessages.tsx
500
+ var ThreadMessages = ({
501
+ components: { Message }
502
+ }) => {
503
+ const chat = useThreadContext("Thread.Messages", (s) => s.chat);
504
+ const messages = chat.messages;
505
+ if (messages.length === 0)
506
+ return null;
507
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, messages.map((message, index) => {
508
+ return /* @__PURE__ */ React.createElement(MessageProvider, { key: index, message }, /* @__PURE__ */ React.createElement(Message, null));
509
+ }), chat.isLoading && chat.messages[chat.messages.length - 1]?.role !== "assistant" && /* @__PURE__ */ React.createElement(
510
+ MessageProvider,
511
+ {
512
+ message: {
513
+ id: UPCOMING_MESSAGE_ID,
514
+ role: "assistant",
515
+ content: "..."
516
+ }
517
+ },
518
+ /* @__PURE__ */ React.createElement(Message, null)
519
+ ));
520
+ };
521
+
522
+ // src/primitives/composer/index.ts
523
+ var composer_exports = {};
524
+ __export(composer_exports, {
525
+ Input: () => ComposerInput,
526
+ Root: () => ComposerRoot,
527
+ Send: () => ComposerSend,
528
+ Stop: () => ComposerStop
529
+ });
530
+
531
+ // src/primitives/composer/ComposerRoot.tsx
532
+ import { createContext as createContext2, forwardRef as forwardRef5, useContext as useContext2, useMemo as useMemo4, useRef as useRef5 } from "react";
533
+ import { Primitive as Primitive4 } from "@radix-ui/react-primitive";
534
+ import { composeEventHandlers as composeEventHandlers4 } from "@radix-ui/primitive";
535
+ import { useComposedRefs as useComposedRefs3 } from "@radix-ui/react-compose-refs";
536
+ var ComposerContext = createContext2(null);
537
+ var useComposerContext = () => {
538
+ const context = useContext2(ComposerContext);
539
+ if (!context) {
540
+ throw new Error(
541
+ "Composer compound components cannot be rendered outside the Composer component"
542
+ );
543
+ }
544
+ return context;
545
+ };
546
+ var ComposerRoot = forwardRef5(
547
+ ({ onSubmit, ...rest }, forwardedRef) => {
548
+ const handleSubmit = useThreadContext(
549
+ "Composer.Root",
550
+ (s) => s.chat.handleSubmit
551
+ );
552
+ const formRef = useRef5(null);
553
+ const ref = useComposedRefs3(forwardedRef, formRef);
554
+ const composerContextValue = useMemo4(
555
+ () => ({
556
+ submit: () => formRef.current?.dispatchEvent(
557
+ new Event("submit", { cancelable: true, bubbles: true })
558
+ )
559
+ }),
560
+ []
561
+ );
562
+ return /* @__PURE__ */ React.createElement(ComposerContext.Provider, { value: composerContextValue }, /* @__PURE__ */ React.createElement(
563
+ Primitive4.form,
564
+ {
565
+ ...rest,
566
+ ref,
567
+ onSubmit: composeEventHandlers4(onSubmit, handleSubmit)
568
+ }
569
+ ));
570
+ }
571
+ );
572
+
573
+ // src/primitives/composer/ComposerInput.tsx
574
+ import { forwardRef as forwardRef6, useRef as useRef6 } from "react";
575
+ import { Slot } from "@radix-ui/react-slot";
576
+ import { composeEventHandlers as composeEventHandlers5 } from "@radix-ui/primitive";
577
+ import { useComposedRefs as useComposedRefs4 } from "@radix-ui/react-compose-refs";
578
+ var ComposerInput = forwardRef6(({ asChild, onChange, onKeyDown, ...rest }, forwardedRef) => {
579
+ const chat = useThreadContext(
580
+ "Composer.Input",
581
+ ({ chat: { input, handleInputChange, isLoading } }) => ({
582
+ input,
583
+ handleInputChange,
584
+ isLoading
585
+ })
586
+ );
587
+ const Component = asChild ? Slot : "textarea";
588
+ const textareaRef = useRef6(null);
589
+ const ref = useComposedRefs4(forwardedRef, textareaRef);
590
+ useAutosize(textareaRef);
591
+ const composer = useComposerContext();
592
+ const handleKeyPress = (e) => {
593
+ if (chat.isLoading || rest.disabled)
594
+ return;
595
+ if (e.key === "Enter" && e.shiftKey == false) {
596
+ e.preventDefault();
597
+ composer.submit();
598
+ }
599
+ };
600
+ return /* @__PURE__ */ React.createElement(
601
+ Component,
602
+ {
603
+ value: chat.input,
604
+ ...rest,
605
+ ref,
606
+ onChange: composeEventHandlers5(onChange, chat.handleInputChange),
607
+ onKeyDown: composeEventHandlers5(onKeyDown, handleKeyPress)
608
+ }
609
+ );
610
+ });
611
+
612
+ // src/primitives/composer/ComposerSend.tsx
613
+ import { forwardRef as forwardRef7 } from "react";
614
+ import { Primitive as Primitive5 } from "@radix-ui/react-primitive";
615
+ var ComposerSend = forwardRef7(
616
+ ({ disabled, ...rest }, ref) => {
617
+ const input = useThreadContext("Composer.Send", (s) => s.chat.input);
618
+ return /* @__PURE__ */ React.createElement(
619
+ Primitive5.button,
620
+ {
621
+ type: "submit",
622
+ ...rest,
623
+ ref,
624
+ disabled: disabled || input.length === 0
625
+ }
626
+ );
627
+ }
628
+ );
629
+
630
+ // src/utils/createActionButton.tsx
631
+ import { forwardRef as forwardRef8 } from "react";
632
+ import { Primitive as Primitive6 } from "@radix-ui/react-primitive";
633
+ import { composeEventHandlers as composeEventHandlers6 } from "@radix-ui/primitive";
634
+ var createActionButton = (useActionButton) => {
635
+ return forwardRef8(
636
+ (props, forwardedRef) => {
637
+ const onClick = useActionButton(props);
638
+ return /* @__PURE__ */ React.createElement(
639
+ Primitive6.button,
640
+ {
641
+ type: "button",
642
+ disabled: !onClick,
643
+ ...props,
644
+ ref: forwardedRef,
645
+ onClick: composeEventHandlers6(props.onClick, onClick ?? void 0)
646
+ }
647
+ );
648
+ }
649
+ );
650
+ };
651
+
652
+ // src/primitives/composer/ComposerStop.tsx
653
+ var useComposerStop = () => {
654
+ const [isLoading, stop] = useThreadContext("Composer.Stop", (s) => [
655
+ s.chat.isLoading,
656
+ s.chat.stop
657
+ ]);
658
+ if (!isLoading)
659
+ return null;
660
+ return stop;
661
+ };
662
+ var ComposerStop = createActionButton(useComposerStop);
663
+
664
+ // src/primitives/branchPicker/index.ts
665
+ var branchPicker_exports = {};
666
+ __export(branchPicker_exports, {
667
+ Count: () => BranchPickerCount,
668
+ Next: () => BranchPickerNext,
669
+ Number: () => BranchPickerNumber,
670
+ Previous: () => BranchPickerPrevious,
671
+ Root: () => BranchPickerRoot
672
+ });
673
+
674
+ // src/primitives/branchPicker/BranchPickerNext.tsx
675
+ var useBranchPickerNext = () => {
676
+ const switchToBranch = useThreadContext(
677
+ "BranchPicker.Next",
678
+ (s) => s.chat.switchToBranch
679
+ );
680
+ const [message, { branchId, branchCount }] = useMessageContext(
681
+ "BranchPicker.Next",
682
+ (s) => [s.message, s.branchState]
683
+ );
684
+ if (branchCount <= 1 || branchId + 1 >= branchCount)
685
+ return null;
686
+ return () => {
687
+ switchToBranch(message, branchId + 1);
688
+ };
689
+ };
690
+ var BranchPickerNext = createActionButton(useBranchPickerNext);
691
+
692
+ // src/primitives/branchPicker/BranchPickerPrevious.tsx
693
+ var useBranchPickerPrevious = () => {
694
+ const switchToBranch = useThreadContext(
695
+ "BranchPicker.Previous",
696
+ (s) => s.chat.switchToBranch
697
+ );
698
+ const [message, { branchId, branchCount }] = useMessageContext(
699
+ "BranchPicker.Previous",
700
+ (s) => [s.message, s.branchState]
701
+ );
702
+ if (branchCount <= 1 || branchId <= 0)
703
+ return null;
704
+ return () => {
705
+ switchToBranch(message, branchId - 1);
706
+ };
707
+ };
708
+ var BranchPickerPrevious = createActionButton(useBranchPickerPrevious);
709
+
710
+ // src/primitives/branchPicker/BranchPickerCount.tsx
711
+ var BranchPickerCount = () => {
712
+ const branchCount = useMessageContext(
713
+ "BranchPicker.Count",
714
+ (s) => s.branchState.branchCount
715
+ );
716
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, branchCount);
717
+ };
718
+
719
+ // src/primitives/branchPicker/BranchPickerNumber.tsx
720
+ var BranchPickerNumber = () => {
721
+ const branchId = useMessageContext(
722
+ "BranchPicker.Number",
723
+ (s) => s.branchState.branchId
724
+ );
725
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, branchId + 1);
726
+ };
727
+
728
+ // src/primitives/branchPicker/BranchPickerRoot.tsx
729
+ import { forwardRef as forwardRef9 } from "react";
730
+ import { Primitive as Primitive7 } from "@radix-ui/react-primitive";
731
+ var BranchPickerRoot = forwardRef9(({ ...rest }, ref) => {
732
+ return /* @__PURE__ */ React.createElement(Primitive7.div, { ...rest, ref });
733
+ });
734
+
735
+ // src/primitives/actionBar/index.ts
736
+ var actionBar_exports = {};
737
+ __export(actionBar_exports, {
738
+ Copy: () => ActionBarCopy,
739
+ Edit: () => ActionBarEdit,
740
+ Reload: () => ActionBarReload,
741
+ Root: () => ActionBarRoot
742
+ });
743
+
744
+ // src/primitives/actionBar/ActionBarRoot.tsx
745
+ import { forwardRef as forwardRef10 } from "react";
746
+ import { Primitive as Primitive8 } from "@radix-ui/react-primitive";
747
+ var ActionBarRoot = forwardRef10(
748
+ ({ ...rest }, ref) => {
749
+ return /* @__PURE__ */ React.createElement(Primitive8.div, { ...rest, ref });
750
+ }
751
+ );
752
+
753
+ // src/primitives/actionBar/ActionBarCopy.tsx
754
+ var useActionBarCopy = ({ copiedDuration = 3e3 }) => {
755
+ const [messageContent, setIsCopied] = useMessageContext(
756
+ "ActionBar.Copy",
757
+ (s) => [s.message.content, s.setIsCopied]
758
+ );
759
+ return () => {
760
+ navigator.clipboard.writeText(messageContent);
761
+ setIsCopied(true);
762
+ setTimeout(() => setIsCopied(false), copiedDuration);
763
+ };
764
+ };
765
+ var ActionBarCopy = createActionButton(useActionBarCopy);
766
+
767
+ // src/primitives/actionBar/ActionBarReload.tsx
768
+ var useActionBarReload = () => {
769
+ const chat = useThreadContext("ActionBar.Reload", (s) => s.chat);
770
+ const message = useMessageContext("ActionBar.Reload", (s) => s.message);
771
+ if (message.role !== "assistant" || chat.isLoading)
772
+ return null;
773
+ return () => chat.reloadAt(message);
774
+ };
775
+ var ActionBarReload = createActionButton(useActionBarReload);
776
+
777
+ // src/primitives/actionBar/ActionBarEdit.tsx
778
+ var useActionBarEdit = () => {
779
+ const [editState, messageContent, setEditState] = useMessageContext(
780
+ "ActionBar.Edit",
781
+ (s) => [s.editState, s.message.content, s.setEditState]
782
+ );
783
+ if (editState.isEditing)
784
+ return null;
785
+ return () => {
786
+ setEditState({ isEditing: true, value: messageContent });
787
+ };
788
+ };
789
+ var ActionBarEdit = createActionButton(useActionBarEdit);
790
+
791
+ // src/primitives/editBar/index.ts
792
+ var editBar_exports = {};
793
+ __export(editBar_exports, {
794
+ Cancel: () => EditBarCancel,
795
+ Root: () => EditBarRoot,
796
+ Save: () => EditBarSave
797
+ });
798
+
799
+ // src/primitives/editBar/EditBarRoot.tsx
800
+ import { forwardRef as forwardRef11 } from "react";
801
+ import { Primitive as Primitive9 } from "@radix-ui/react-primitive";
802
+ var EditBarRoot = forwardRef11(
803
+ ({ ...rest }, ref) => {
804
+ return /* @__PURE__ */ React.createElement(Primitive9.div, { ...rest, ref });
805
+ }
806
+ );
807
+
808
+ // src/primitives/editBar/EditBarSave.tsx
809
+ var useEditBarSave = () => {
810
+ const chat = useThreadContext("EditBar.Save", (s) => s.chat);
811
+ const [editState, message, setEditState] = useMessageContext(
812
+ "EditBar.Save",
813
+ (s) => [s.editState, s.message, s.setEditState]
814
+ );
815
+ if (!editState.isEditing)
816
+ return null;
817
+ return () => {
818
+ if (!editState.isEditing)
819
+ return;
820
+ chat.editAt(message, {
821
+ ...message,
822
+ id: void 0,
823
+ // remove id to create a new message
824
+ content: editState.value
825
+ });
826
+ setEditState({ isEditing: false });
827
+ };
828
+ };
829
+ var EditBarSave = createActionButton(useEditBarSave);
830
+
831
+ // src/primitives/editBar/EditBarCancel.tsx
832
+ var useEditBarCancel = () => {
833
+ const [isEditing, setEditState] = useMessageContext("EditBar.Cancel", (s) => [
834
+ s.editState.isEditing,
835
+ s.setEditState
836
+ ]);
837
+ if (!isEditing)
838
+ return null;
839
+ return () => {
840
+ setEditState({ isEditing: false });
841
+ };
842
+ };
843
+ var EditBarCancel = createActionButton(useEditBarCancel);
844
+ export {
845
+ actionBar_exports as ActionBar,
846
+ branchPicker_exports as BranchPicker,
847
+ composer_exports as Composer,
848
+ editBar_exports as EditBar,
849
+ message_exports as Message,
850
+ thread_exports as Thread,
851
+ useMessageContext as unstable_useMessageContext
852
+ };