@truefoundry/agent-ui-sdk 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.
Files changed (79) hide show
  1. package/README.md +634 -0
  2. package/dist/index.d.ts +718 -0
  3. package/dist/index.js +2736 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +83 -0
  6. package/src/atoms/AskUserPrompt.tsx +102 -0
  7. package/src/atoms/AttachmentCard.tsx +92 -0
  8. package/src/atoms/AttachmentPickerButton.tsx +19 -0
  9. package/src/atoms/AttachmentPreviewDialog.tsx +34 -0
  10. package/src/atoms/BranchIndicator.tsx +41 -0
  11. package/src/atoms/CodeBlockHeader.tsx +44 -0
  12. package/src/atoms/ComposerShell.tsx +112 -0
  13. package/src/atoms/Markdown.test.tsx +28 -0
  14. package/src/atoms/Markdown.tsx +288 -0
  15. package/src/atoms/McpAuthPrompt.tsx +60 -0
  16. package/src/atoms/MessageActionBar.tsx +60 -0
  17. package/src/atoms/MessageBubble.tsx +80 -0
  18. package/src/atoms/MessageErrorBanner.tsx +26 -0
  19. package/src/atoms/MessageIndicator.tsx +23 -0
  20. package/src/atoms/OpenUIBlock.test.tsx +37 -0
  21. package/src/atoms/OpenUIBlock.tsx +50 -0
  22. package/src/atoms/ReasoningCard.tsx +52 -0
  23. package/src/atoms/SandboxArtifactList.tsx +63 -0
  24. package/src/atoms/ScrollToBottomButton.tsx +34 -0
  25. package/src/atoms/Skeletons.tsx +32 -0
  26. package/src/atoms/ThreadListMisc.tsx +76 -0
  27. package/src/atoms/ThreadListRow.tsx +81 -0
  28. package/src/atoms/ThreadShell.tsx +93 -0
  29. package/src/atoms/Toast.tsx +60 -0
  30. package/src/atoms/ToolApprovalBar.tsx +92 -0
  31. package/src/atoms/ToolCallCard.tsx +186 -0
  32. package/src/atoms/ToolGroupCard.tsx +52 -0
  33. package/src/atoms/UserMessageActionBar.tsx +62 -0
  34. package/src/atoms/WelcomeScreen.tsx +22 -0
  35. package/src/atoms/lib/cn.ts +6 -0
  36. package/src/atoms/primitives/Avatar.tsx +57 -0
  37. package/src/atoms/primitives/Button.tsx +73 -0
  38. package/src/atoms/primitives/Collapsible.tsx +32 -0
  39. package/src/atoms/primitives/Dialog.tsx +152 -0
  40. package/src/atoms/primitives/IconButton.tsx +43 -0
  41. package/src/atoms/primitives/Skeleton.tsx +17 -0
  42. package/src/atoms/primitives/Tooltip.tsx +58 -0
  43. package/src/containers/AskUserContainer.tsx +49 -0
  44. package/src/containers/AssistantMessageContainer.test.tsx +56 -0
  45. package/src/containers/AssistantMessageContainer.tsx +128 -0
  46. package/src/containers/AssistantTextContainer.test.tsx +111 -0
  47. package/src/containers/AssistantTextContainer.tsx +54 -0
  48. package/src/containers/AttachmentsContainer.tsx +83 -0
  49. package/src/containers/ComposerContainer.tsx +66 -0
  50. package/src/containers/ErrorToasterContainer.tsx +76 -0
  51. package/src/containers/McpAuthContainer.test.tsx +100 -0
  52. package/src/containers/McpAuthContainer.tsx +22 -0
  53. package/src/containers/MessageImageContainer.tsx +37 -0
  54. package/src/containers/ReasoningContainer.tsx +32 -0
  55. package/src/containers/RuntimeHarness.tsx +39 -0
  56. package/src/containers/Thread.tsx +9 -0
  57. package/src/containers/ThreadContainer.test.tsx +52 -0
  58. package/src/containers/ThreadContainer.tsx +87 -0
  59. package/src/containers/ThreadListContainer.tsx +63 -0
  60. package/src/containers/ToolCallContainer.test.tsx +135 -0
  61. package/src/containers/ToolCallContainer.tsx +167 -0
  62. package/src/containers/ToolGroupContainer.test.tsx +56 -0
  63. package/src/containers/ToolGroupContainer.tsx +24 -0
  64. package/src/containers/UserEditComposerContainer.test.tsx +52 -0
  65. package/src/containers/UserEditComposerContainer.tsx +86 -0
  66. package/src/containers/UserMessageContainer.test.tsx +99 -0
  67. package/src/containers/UserMessageContainer.tsx +26 -0
  68. package/src/containers/nestedApprovalBridge.ts +20 -0
  69. package/src/containers/useAttachmentPreviewSrc.ts +42 -0
  70. package/src/hooks/useComposerBusyState.test.ts +78 -0
  71. package/src/hooks/useComposerBusyState.ts +69 -0
  72. package/src/index.ts +193 -0
  73. package/src/openui.css +3 -0
  74. package/src/testSetup.ts +42 -0
  75. package/src/theme/SlotsProvider.test.tsx +64 -0
  76. package/src/theme/SlotsProvider.tsx +41 -0
  77. package/src/theme/defaultSlots.ts +131 -0
  78. package/src/theme/tokens.ts +98 -0
  79. package/src/theme/useClassDarkMode.ts +24 -0
package/dist/index.js ADDED
@@ -0,0 +1,2736 @@
1
+ "use client";
2
+
3
+ // src/theme/SlotsProvider.tsx
4
+ import { createContext, useContext, useMemo as useMemo2 } from "react";
5
+
6
+ // src/atoms/primitives/Avatar.tsx
7
+ import { Avatar as AvatarPrimitive } from "radix-ui";
8
+
9
+ // src/atoms/lib/cn.ts
10
+ import { clsx } from "clsx";
11
+ import { twMerge } from "tailwind-merge";
12
+ function cn(...inputs) {
13
+ return twMerge(clsx(inputs));
14
+ }
15
+
16
+ // src/atoms/primitives/Avatar.tsx
17
+ import { jsx } from "react/jsx-runtime";
18
+ function Avatar({ className, size = "default", ...props }) {
19
+ return /* @__PURE__ */ jsx(
20
+ AvatarPrimitive.Root,
21
+ {
22
+ "data-slot": "avatar",
23
+ "data-size": size,
24
+ className: cn(
25
+ "group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
26
+ className
27
+ ),
28
+ ...props
29
+ }
30
+ );
31
+ }
32
+ function AvatarImage({ className, ...props }) {
33
+ return /* @__PURE__ */ jsx(
34
+ AvatarPrimitive.Image,
35
+ {
36
+ "data-slot": "avatar-image",
37
+ className: cn("aspect-square size-full rounded-full object-cover", className),
38
+ ...props
39
+ }
40
+ );
41
+ }
42
+ function AvatarFallback({ className, ...props }) {
43
+ return /* @__PURE__ */ jsx(
44
+ AvatarPrimitive.Fallback,
45
+ {
46
+ "data-slot": "avatar-fallback",
47
+ className: cn(
48
+ "flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
49
+ className
50
+ ),
51
+ ...props
52
+ }
53
+ );
54
+ }
55
+
56
+ // src/atoms/primitives/Button.tsx
57
+ import { cva } from "class-variance-authority";
58
+ import { Slot } from "radix-ui";
59
+ import { jsx as jsx2 } from "react/jsx-runtime";
60
+ var buttonVariants = cva(
61
+ "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
62
+ {
63
+ variants: {
64
+ variant: {
65
+ default: "bg-primary text-primary-foreground hover:bg-primary/80",
66
+ outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
67
+ secondary: "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
68
+ ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
69
+ destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
70
+ link: "text-primary underline-offset-4 hover:underline"
71
+ },
72
+ size: {
73
+ default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
74
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
75
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
76
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
77
+ icon: "size-8",
78
+ "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
79
+ "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
80
+ "icon-lg": "size-9"
81
+ }
82
+ },
83
+ defaultVariants: {
84
+ variant: "default",
85
+ size: "default"
86
+ }
87
+ }
88
+ );
89
+ function Button({
90
+ className,
91
+ variant = "default",
92
+ size = "default",
93
+ asChild = false,
94
+ ...props
95
+ }) {
96
+ const Comp = asChild ? Slot.Root : "button";
97
+ return /* @__PURE__ */ jsx2(
98
+ Comp,
99
+ {
100
+ "data-slot": "button",
101
+ "data-variant": variant,
102
+ "data-size": size,
103
+ className: cn(buttonVariants({ variant, size, className })),
104
+ ...props
105
+ }
106
+ );
107
+ }
108
+
109
+ // src/atoms/primitives/Collapsible.tsx
110
+ import { Collapsible as CollapsiblePrimitive } from "radix-ui";
111
+ import { jsx as jsx3 } from "react/jsx-runtime";
112
+ function Collapsible(props) {
113
+ return /* @__PURE__ */ jsx3(CollapsiblePrimitive.Root, { "data-slot": "collapsible", ...props });
114
+ }
115
+ function CollapsibleTrigger(props) {
116
+ return /* @__PURE__ */ jsx3(CollapsiblePrimitive.CollapsibleTrigger, { "data-slot": "collapsible-trigger", ...props });
117
+ }
118
+ function CollapsibleContent(props) {
119
+ return /* @__PURE__ */ jsx3(CollapsiblePrimitive.CollapsibleContent, { "data-slot": "collapsible-content", ...props });
120
+ }
121
+
122
+ // src/atoms/primitives/Dialog.tsx
123
+ import { Dialog as DialogPrimitive } from "radix-ui";
124
+ import { XIcon } from "lucide-react";
125
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
126
+ function Dialog(props) {
127
+ return /* @__PURE__ */ jsx4(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
128
+ }
129
+ function DialogTrigger(props) {
130
+ return /* @__PURE__ */ jsx4(DialogPrimitive.Trigger, { "data-slot": "dialog-trigger", ...props });
131
+ }
132
+ function DialogPortal(props) {
133
+ return /* @__PURE__ */ jsx4(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
134
+ }
135
+ function DialogClose(props) {
136
+ return /* @__PURE__ */ jsx4(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
137
+ }
138
+ function DialogOverlay({ className, ...props }) {
139
+ return /* @__PURE__ */ jsx4(
140
+ DialogPrimitive.Overlay,
141
+ {
142
+ "data-slot": "dialog-overlay",
143
+ className: cn(
144
+ "fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
145
+ className
146
+ ),
147
+ ...props
148
+ }
149
+ );
150
+ }
151
+ function DialogContent({
152
+ className,
153
+ children,
154
+ showCloseButton = true,
155
+ ...props
156
+ }) {
157
+ return /* @__PURE__ */ jsxs(DialogPortal, { children: [
158
+ /* @__PURE__ */ jsx4(DialogOverlay, {}),
159
+ /* @__PURE__ */ jsxs(
160
+ DialogPrimitive.Content,
161
+ {
162
+ "data-slot": "dialog-content",
163
+ className: cn(
164
+ "fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
165
+ className
166
+ ),
167
+ ...props,
168
+ children: [
169
+ children,
170
+ showCloseButton && /* @__PURE__ */ jsx4(DialogPrimitive.Close, { "data-slot": "dialog-close", asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", className: "absolute top-2 right-2", size: "icon-sm", children: [
171
+ /* @__PURE__ */ jsx4(XIcon, {}),
172
+ /* @__PURE__ */ jsx4("span", { className: "sr-only", children: "Close" })
173
+ ] }) })
174
+ ]
175
+ }
176
+ )
177
+ ] });
178
+ }
179
+ function DialogHeader({ className, ...props }) {
180
+ return /* @__PURE__ */ jsx4("div", { "data-slot": "dialog-header", className: cn("flex flex-col gap-2", className), ...props });
181
+ }
182
+ function DialogFooter({ className, showCloseButton = false, children, ...props }) {
183
+ return /* @__PURE__ */ jsxs(
184
+ "div",
185
+ {
186
+ "data-slot": "dialog-footer",
187
+ className: cn(
188
+ "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end",
189
+ className
190
+ ),
191
+ ...props,
192
+ children: [
193
+ children,
194
+ showCloseButton && /* @__PURE__ */ jsx4(DialogPrimitive.Close, { asChild: true, children: /* @__PURE__ */ jsx4(Button, { variant: "outline", children: "Close" }) })
195
+ ]
196
+ }
197
+ );
198
+ }
199
+ function DialogTitle({ className, ...props }) {
200
+ return /* @__PURE__ */ jsx4(
201
+ DialogPrimitive.Title,
202
+ {
203
+ "data-slot": "dialog-title",
204
+ className: cn("font-heading text-base leading-none font-medium", className),
205
+ ...props
206
+ }
207
+ );
208
+ }
209
+ function DialogDescription({ className, ...props }) {
210
+ return /* @__PURE__ */ jsx4(
211
+ DialogPrimitive.Description,
212
+ {
213
+ "data-slot": "dialog-description",
214
+ className: cn(
215
+ "text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
216
+ className
217
+ ),
218
+ ...props
219
+ }
220
+ );
221
+ }
222
+
223
+ // src/atoms/primitives/IconButton.tsx
224
+ import { forwardRef } from "react";
225
+ import { Slot as Slot2 } from "radix-ui";
226
+
227
+ // src/atoms/primitives/Tooltip.tsx
228
+ import { Tooltip as TooltipPrimitive } from "radix-ui";
229
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
230
+ function TooltipProvider({ delayDuration = 0, ...props }) {
231
+ return /* @__PURE__ */ jsx5(
232
+ TooltipPrimitive.Provider,
233
+ {
234
+ "data-slot": "tooltip-provider",
235
+ delayDuration,
236
+ ...props
237
+ }
238
+ );
239
+ }
240
+ function Tooltip(props) {
241
+ return /* @__PURE__ */ jsx5(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props });
242
+ }
243
+ function TooltipTrigger(props) {
244
+ return /* @__PURE__ */ jsx5(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
245
+ }
246
+ function TooltipContent({ className, sideOffset = 0, children, ...props }) {
247
+ return /* @__PURE__ */ jsx5(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs2(
248
+ TooltipPrimitive.Content,
249
+ {
250
+ "data-slot": "tooltip-content",
251
+ sideOffset,
252
+ className: cn(
253
+ "z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
254
+ className
255
+ ),
256
+ ...props,
257
+ children: [
258
+ children,
259
+ /* @__PURE__ */ jsx5(TooltipPrimitive.Arrow, { className: "z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" })
260
+ ]
261
+ }
262
+ ) });
263
+ }
264
+
265
+ // src/atoms/primitives/IconButton.tsx
266
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
267
+ var IconButton = forwardRef(
268
+ ({ children, tooltip, side = "bottom", className, ...rest }, ref) => {
269
+ return /* @__PURE__ */ jsx6(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs3(Tooltip, { children: [
270
+ /* @__PURE__ */ jsx6(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs3(
271
+ Button,
272
+ {
273
+ variant: "ghost",
274
+ size: "icon",
275
+ ...rest,
276
+ className: cn("aui-button-icon size-6 p-1 active:scale-90", className),
277
+ ref,
278
+ children: [
279
+ /* @__PURE__ */ jsx6(Slot2.Slottable, { children }),
280
+ /* @__PURE__ */ jsx6("span", { className: "aui-sr-only sr-only", children: tooltip })
281
+ ]
282
+ }
283
+ ) }),
284
+ /* @__PURE__ */ jsx6(TooltipContent, { side, children: tooltip })
285
+ ] }) });
286
+ }
287
+ );
288
+ IconButton.displayName = "IconButton";
289
+
290
+ // src/atoms/primitives/Skeleton.tsx
291
+ import { jsx as jsx7 } from "react/jsx-runtime";
292
+ function Skeleton({ className, ...props }) {
293
+ return /* @__PURE__ */ jsx7("div", { "data-slot": "skeleton", className: cn("animate-pulse rounded-md bg-muted", className), ...props });
294
+ }
295
+
296
+ // src/atoms/BranchIndicator.tsx
297
+ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
298
+ import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
299
+ function BranchIndicator({ index, count, onPrevious, onNext, className }) {
300
+ if (count <= 1) return null;
301
+ return /* @__PURE__ */ jsxs4(
302
+ "div",
303
+ {
304
+ className: cn(
305
+ "aui-branch-picker-root text-muted-foreground -ms-2 me-2 inline-flex items-center text-xs",
306
+ className
307
+ ),
308
+ children: [
309
+ /* @__PURE__ */ jsx8(IconButton, { tooltip: "Previous", onClick: onPrevious, children: /* @__PURE__ */ jsx8(ChevronLeftIcon, {}) }),
310
+ /* @__PURE__ */ jsxs4("span", { className: "aui-branch-picker-state font-medium", children: [
311
+ index,
312
+ " / ",
313
+ count
314
+ ] }),
315
+ /* @__PURE__ */ jsx8(IconButton, { tooltip: "Next", onClick: onNext, children: /* @__PURE__ */ jsx8(ChevronRightIcon, {}) })
316
+ ]
317
+ }
318
+ );
319
+ }
320
+
321
+ // src/atoms/CodeBlockHeader.tsx
322
+ import { useState } from "react";
323
+ import { CheckIcon, CopyIcon } from "lucide-react";
324
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
325
+ function CodeBlockHeader({ language, code }) {
326
+ const [isCopied, setIsCopied] = useState(false);
327
+ const onCopy = () => {
328
+ if (!code || isCopied || typeof navigator === "undefined" || !navigator.clipboard) {
329
+ return;
330
+ }
331
+ navigator.clipboard.writeText(code).then(
332
+ () => {
333
+ setIsCopied(true);
334
+ setTimeout(() => setIsCopied(false), 3e3);
335
+ },
336
+ () => {
337
+ }
338
+ );
339
+ };
340
+ return /* @__PURE__ */ jsxs5("div", { className: "aui-code-header-root border-border/50 bg-muted/50 mt-3 flex items-center justify-between rounded-t-xl border border-b-0 px-3.5 py-1.5 text-xs", children: [
341
+ /* @__PURE__ */ jsx9("span", { className: "aui-code-header-language text-muted-foreground font-medium lowercase", children: language }),
342
+ /* @__PURE__ */ jsxs5(IconButton, { tooltip: "Copy", onClick: onCopy, children: [
343
+ !isCopied && /* @__PURE__ */ jsx9(CopyIcon, { className: "animate-in zoom-in-75 fade-in duration-150" }),
344
+ isCopied && /* @__PURE__ */ jsx9(CheckIcon, { className: "animate-in zoom-in-50 fade-in duration-200 ease-out" })
345
+ ] })
346
+ ] });
347
+ }
348
+
349
+ // src/atoms/Markdown.tsx
350
+ import { Children, isValidElement, useMemo } from "react";
351
+ import ReactMarkdown from "react-markdown";
352
+ import remarkGfm from "remark-gfm";
353
+
354
+ // src/atoms/OpenUIBlock.tsx
355
+ import { memo, useId } from "react";
356
+ import { Renderer } from "@openuidev/react-lang";
357
+ import { ThemeProvider } from "@openuidev/react-ui";
358
+ import { openuiLibrary } from "@openuidev/react-ui/genui-lib";
359
+
360
+ // src/theme/useClassDarkMode.ts
361
+ import { useSyncExternalStore } from "react";
362
+ function getThemeMode() {
363
+ if (typeof document === "undefined") {
364
+ return "light";
365
+ }
366
+ return document.documentElement.classList.contains("dark") ? "dark" : "light";
367
+ }
368
+ function subscribe(onStoreChange) {
369
+ const observer = new MutationObserver(onStoreChange);
370
+ observer.observe(document.documentElement, {
371
+ attributes: true,
372
+ attributeFilter: ["class"]
373
+ });
374
+ return () => observer.disconnect();
375
+ }
376
+ function useClassDarkMode() {
377
+ return useSyncExternalStore(subscribe, getThemeMode, () => "light");
378
+ }
379
+
380
+ // src/atoms/OpenUIBlock.tsx
381
+ import { jsx as jsx10 } from "react/jsx-runtime";
382
+ var OpenUIBlock = memo(
383
+ function OpenUIBlock2({ source, isStreaming, className, mode: modeProp }) {
384
+ const detectedMode = useClassDarkMode();
385
+ const mode = modeProp ?? detectedMode;
386
+ const scopeClass = `aui-openui-theme-${useId().replace(/:/g, "")}`;
387
+ return /* @__PURE__ */ jsx10(ThemeProvider, { mode, cssSelector: `.${scopeClass}`, children: /* @__PURE__ */ jsx10(
388
+ "div",
389
+ {
390
+ "data-slot": "openui-block",
391
+ className: cn("aui-openui-block my-3", scopeClass, className),
392
+ style: { contain: "layout paint" },
393
+ children: /* @__PURE__ */ jsx10(
394
+ Renderer,
395
+ {
396
+ response: source,
397
+ library: openuiLibrary,
398
+ isStreaming: !!isStreaming
399
+ }
400
+ )
401
+ }
402
+ ) });
403
+ },
404
+ (prev, next) => prev.source === next.source && !!prev.isStreaming === !!next.isStreaming && prev.mode === next.mode
405
+ );
406
+
407
+ // src/atoms/SandboxArtifactList.tsx
408
+ import { useState as useState2 } from "react";
409
+ import { DownloadIcon, FileIcon, LoaderIcon } from "lucide-react";
410
+ import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
411
+ function SandboxArtifactList({ artifacts, onDownload, className }) {
412
+ const [downloadingPath, setDownloadingPath] = useState2(null);
413
+ const handleClick = async (path) => {
414
+ if (downloadingPath != null) return;
415
+ setDownloadingPath(path);
416
+ try {
417
+ await onDownload?.(path);
418
+ } finally {
419
+ setDownloadingPath(null);
420
+ }
421
+ };
422
+ return /* @__PURE__ */ jsx11(
423
+ "div",
424
+ {
425
+ "data-slot": "aui_sandbox-artifact-list",
426
+ className: cn("aui-sandbox-artifact-list my-3 flex w-fit max-w-full flex-wrap items-stretch text-sm", className),
427
+ children: artifacts.map((artifact, index) => {
428
+ const isDownloading = downloadingPath === artifact.path;
429
+ return /* @__PURE__ */ jsxs6(
430
+ "button",
431
+ {
432
+ type: "button",
433
+ className: cn(
434
+ "aui-sandbox-artifact-button text-muted-foreground hover:text-foreground flex min-w-0 items-center gap-1.5 py-1 pr-2 pl-2 transition-colors disabled:pointer-events-none disabled:opacity-70",
435
+ index > 0 && "border-border/60 ml-1 border-l pl-3"
436
+ ),
437
+ disabled: isDownloading,
438
+ onClick: () => handleClick(artifact.path),
439
+ children: [
440
+ /* @__PURE__ */ jsx11(FileIcon, { className: "size-4 shrink-0", "aria-hidden": true }),
441
+ /* @__PURE__ */ jsx11("span", { className: "max-w-40 truncate", children: artifact.label }),
442
+ isDownloading ? /* @__PURE__ */ jsx11(LoaderIcon, { className: "size-3.5 shrink-0 animate-spin [animation-duration:0.6s]", "aria-hidden": true }) : /* @__PURE__ */ jsx11(DownloadIcon, { className: "size-3.5 shrink-0", "aria-hidden": true })
443
+ ]
444
+ },
445
+ artifact.path
446
+ );
447
+ })
448
+ }
449
+ );
450
+ }
451
+
452
+ // src/atoms/Markdown.tsx
453
+ import { Fragment, jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
454
+ function extractCodeText(node) {
455
+ if (typeof node === "string") return node;
456
+ if (typeof node === "number") return String(node);
457
+ if (Array.isArray(node)) return node.map(extractCodeText).join("");
458
+ if (isValidElement(node)) {
459
+ return extractCodeText(node.props.children);
460
+ }
461
+ return "";
462
+ }
463
+ var SANDBOX_ARTIFACT_TOKEN = "sandbox_artifact";
464
+ function parseSandboxArtifactCodeBlock(code) {
465
+ const links = [];
466
+ for (const match of code.matchAll(/\[([^\]]+)\]\(([^)]+)\)/g)) {
467
+ links.push({ label: match[1], path: match[2] });
468
+ }
469
+ return links.length > 0 ? links : null;
470
+ }
471
+ function parseSandboxArtifactParagraph(children) {
472
+ const items = Children.toArray(children);
473
+ if (items.length === 0) return null;
474
+ const first = items[0];
475
+ if (typeof first !== "string") return null;
476
+ const stripped = first.replace(/^\s*/, "");
477
+ if (!stripped.startsWith(SANDBOX_ARTIFACT_TOKEN)) return null;
478
+ if (!/^\s*$/.test(stripped.slice(SANDBOX_ARTIFACT_TOKEN.length))) return null;
479
+ const links = [];
480
+ for (let i = 1; i < items.length; i++) {
481
+ const item = items[i];
482
+ if (typeof item === "string") {
483
+ const trimmed = item.trim();
484
+ if (trimmed === "" || trimmed === SANDBOX_ARTIFACT_TOKEN) continue;
485
+ return null;
486
+ }
487
+ if (isValidElement(item) && typeof item.props.href === "string") {
488
+ links.push({
489
+ label: extractCodeText(item.props.children),
490
+ path: item.props.href
491
+ });
492
+ continue;
493
+ }
494
+ return null;
495
+ }
496
+ return links.length > 0 ? links : null;
497
+ }
498
+ function createMarkdownComponents(isStreaming, onDownloadArtifact) {
499
+ return {
500
+ h1: ({ className, ...props }) => /* @__PURE__ */ jsx12(
501
+ "h1",
502
+ {
503
+ className: cn(
504
+ "aui-md-h1 mt-5 mb-2 scroll-m-20 text-xl font-semibold first:mt-0 last:mb-0",
505
+ className
506
+ ),
507
+ ...props
508
+ }
509
+ ),
510
+ h2: ({ className, ...props }) => /* @__PURE__ */ jsx12(
511
+ "h2",
512
+ {
513
+ className: cn(
514
+ "aui-md-h2 mt-5 mb-2 scroll-m-20 text-lg font-semibold first:mt-0 last:mb-0",
515
+ className
516
+ ),
517
+ ...props
518
+ }
519
+ ),
520
+ h3: ({ className, ...props }) => /* @__PURE__ */ jsx12(
521
+ "h3",
522
+ {
523
+ className: cn(
524
+ "aui-md-h3 mt-4 mb-1.5 scroll-m-20 text-base font-semibold first:mt-0 last:mb-0",
525
+ className
526
+ ),
527
+ ...props
528
+ }
529
+ ),
530
+ h4: ({ className, ...props }) => /* @__PURE__ */ jsx12(
531
+ "h4",
532
+ {
533
+ className: cn(
534
+ "aui-md-h4 mt-3.5 mb-1 scroll-m-20 text-base font-medium first:mt-0 last:mb-0",
535
+ className
536
+ ),
537
+ ...props
538
+ }
539
+ ),
540
+ h5: ({ className, ...props }) => /* @__PURE__ */ jsx12(
541
+ "h5",
542
+ {
543
+ className: cn("aui-md-h5 mt-3 mb-1 text-sm font-semibold first:mt-0 last:mb-0", className),
544
+ ...props
545
+ }
546
+ ),
547
+ h6: ({ className, ...props }) => /* @__PURE__ */ jsx12(
548
+ "h6",
549
+ {
550
+ className: cn("aui-md-h6 mt-3 mb-1 text-sm font-medium first:mt-0 last:mb-0", className),
551
+ ...props
552
+ }
553
+ ),
554
+ p: ({ className, children, ...props }) => {
555
+ const artifacts = parseSandboxArtifactParagraph(children);
556
+ if (artifacts != null) {
557
+ return /* @__PURE__ */ jsx12(SandboxArtifactList, { artifacts, onDownload: onDownloadArtifact });
558
+ }
559
+ return /* @__PURE__ */ jsx12("p", { className: cn("aui-md-p my-3 leading-relaxed first:mt-0 last:mb-0", className), ...props, children });
560
+ },
561
+ a: ({ className, ...props }) => /* @__PURE__ */ jsx12(
562
+ "a",
563
+ {
564
+ className: cn(
565
+ "aui-md-a text-primary hover:text-primary/80 underline underline-offset-2",
566
+ className
567
+ ),
568
+ target: "_blank",
569
+ rel: "noopener noreferrer",
570
+ ...props
571
+ }
572
+ ),
573
+ blockquote: ({ className, ...props }) => /* @__PURE__ */ jsx12(
574
+ "blockquote",
575
+ {
576
+ className: cn(
577
+ "aui-md-blockquote border-muted-foreground/30 text-muted-foreground my-3 border-s-2 ps-4",
578
+ className
579
+ ),
580
+ ...props
581
+ }
582
+ ),
583
+ ul: ({ className, ...props }) => /* @__PURE__ */ jsx12(
584
+ "ul",
585
+ {
586
+ className: cn(
587
+ "aui-md-ul marker:text-muted-foreground my-3 ms-5 list-disc [&>li]:mt-1",
588
+ className
589
+ ),
590
+ ...props
591
+ }
592
+ ),
593
+ ol: ({ className, ...props }) => /* @__PURE__ */ jsx12(
594
+ "ol",
595
+ {
596
+ className: cn(
597
+ "aui-md-ol marker:text-muted-foreground my-3 ms-5 list-decimal [&>li]:mt-1",
598
+ className
599
+ ),
600
+ ...props
601
+ }
602
+ ),
603
+ hr: ({ className, ...props }) => /* @__PURE__ */ jsx12("hr", { className: cn("aui-md-hr border-muted-foreground/20 my-3", className), ...props }),
604
+ table: ({ className, ...props }) => /* @__PURE__ */ jsx12(
605
+ "table",
606
+ {
607
+ className: cn(
608
+ "aui-md-table my-3 w-full border-separate border-spacing-0 overflow-y-auto",
609
+ className
610
+ ),
611
+ ...props
612
+ }
613
+ ),
614
+ th: ({ className, ...props }) => /* @__PURE__ */ jsx12(
615
+ "th",
616
+ {
617
+ className: cn(
618
+ "aui-md-th bg-muted px-3 py-1.5 text-start font-medium first:rounded-ss-lg last:rounded-se-lg [[align=center]]:text-center [[align=right]]:text-right",
619
+ className
620
+ ),
621
+ ...props
622
+ }
623
+ ),
624
+ td: ({ className, ...props }) => /* @__PURE__ */ jsx12(
625
+ "td",
626
+ {
627
+ className: cn(
628
+ "aui-md-td border-muted-foreground/20 border-s border-b px-3 py-1.5 text-start last:border-e [[align=center]]:text-center [[align=right]]:text-right",
629
+ className
630
+ ),
631
+ ...props
632
+ }
633
+ ),
634
+ tr: ({ className, ...props }) => /* @__PURE__ */ jsx12(
635
+ "tr",
636
+ {
637
+ className: cn(
638
+ "aui-md-tr m-0 border-b p-0 first:border-t [&:last-child>td:first-child]:rounded-es-lg [&:last-child>td:last-child]:rounded-ee-lg",
639
+ className
640
+ ),
641
+ ...props
642
+ }
643
+ ),
644
+ li: ({ className, ...props }) => /* @__PURE__ */ jsx12("li", { className: cn("aui-md-li leading-relaxed", className), ...props }),
645
+ strong: ({ className, ...props }) => /* @__PURE__ */ jsx12("strong", { className: cn("aui-md-strong font-semibold", className), ...props }),
646
+ sup: ({ className, ...props }) => /* @__PURE__ */ jsx12("sup", { className: cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className), ...props }),
647
+ pre: ({ className, children, ...props }) => {
648
+ const codeElement = isValidElement(children) ? children : void 0;
649
+ const codeClassName = codeElement?.props?.className ?? "";
650
+ const language = /language-(\S+)/.exec(codeClassName)?.[1] ?? "text";
651
+ const code = extractCodeText(children);
652
+ if (language === "openui") {
653
+ return /* @__PURE__ */ jsx12(OpenUIBlock, { source: code, isStreaming });
654
+ }
655
+ if (language === "sandbox_artifact" || language === "sandbox_artifacts") {
656
+ const artifacts = parseSandboxArtifactCodeBlock(code);
657
+ if (artifacts != null) {
658
+ return /* @__PURE__ */ jsx12(SandboxArtifactList, { artifacts, onDownload: onDownloadArtifact });
659
+ }
660
+ }
661
+ return /* @__PURE__ */ jsxs7(Fragment, { children: [
662
+ /* @__PURE__ */ jsx12(CodeBlockHeader, { language, code }),
663
+ /* @__PURE__ */ jsx12(
664
+ "pre",
665
+ {
666
+ className: cn(
667
+ "aui-md-pre border-border/50 bg-muted/30 overflow-x-auto rounded-t-none rounded-b-xl border border-t-0 p-3.5 text-[13px] leading-relaxed",
668
+ className
669
+ ),
670
+ ...props,
671
+ children
672
+ }
673
+ )
674
+ ] });
675
+ },
676
+ code: ({ className, ...props }) => {
677
+ const isCodeBlock = /language-/.test(className ?? "");
678
+ return /* @__PURE__ */ jsx12(
679
+ "code",
680
+ {
681
+ className: cn(
682
+ !isCodeBlock && "aui-md-inline-code bg-muted rounded-md px-1.5 py-0.5 font-mono text-[0.85em]",
683
+ className
684
+ ),
685
+ ...props
686
+ }
687
+ );
688
+ }
689
+ };
690
+ }
691
+ function Markdown({ content, className, isStreaming, onDownloadArtifact }) {
692
+ const components = useMemo(
693
+ () => createMarkdownComponents(isStreaming, onDownloadArtifact),
694
+ [isStreaming, onDownloadArtifact]
695
+ );
696
+ return /* @__PURE__ */ jsx12("div", { className: cn("aui-md", className), children: /* @__PURE__ */ jsx12(ReactMarkdown, { remarkPlugins: [remarkGfm], components, children: content }) });
697
+ }
698
+
699
+ // src/atoms/MessageActionBar.tsx
700
+ import { CheckIcon as CheckIcon2, CopyIcon as CopyIcon2, DownloadIcon as DownloadIcon2, MoreHorizontalIcon } from "lucide-react";
701
+ import { DropdownMenu } from "radix-ui";
702
+ import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
703
+ function MessageActionBar({ isCopied, onCopy, onExportMarkdown, className }) {
704
+ return /* @__PURE__ */ jsxs8(
705
+ "div",
706
+ {
707
+ className: cn(
708
+ "aui-assistant-action-bar-root text-muted-foreground animate-in fade-in flex gap-1 duration-200",
709
+ className
710
+ ),
711
+ children: [
712
+ /* @__PURE__ */ jsx13(IconButton, { tooltip: "Copy", onClick: onCopy, children: isCopied ? /* @__PURE__ */ jsx13(CheckIcon2, { className: "animate-in zoom-in-50 fade-in duration-200 ease-out" }) : /* @__PURE__ */ jsx13(CopyIcon2, { className: "animate-in zoom-in-75 fade-in duration-150" }) }),
713
+ /* @__PURE__ */ jsxs8(DropdownMenu.Root, { children: [
714
+ /* @__PURE__ */ jsx13(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx13(IconButton, { tooltip: "More", className: "data-[state=open]:bg-accent", children: /* @__PURE__ */ jsx13(MoreHorizontalIcon, {}) }) }),
715
+ /* @__PURE__ */ jsx13(DropdownMenu.Portal, { children: /* @__PURE__ */ jsx13(
716
+ DropdownMenu.Content,
717
+ {
718
+ side: "bottom",
719
+ align: "start",
720
+ sideOffset: 6,
721
+ className: "aui-action-bar-more-content bg-popover/95 text-popover-foreground data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:animate-out data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-xl border p-1.5 shadow-lg backdrop-blur-sm",
722
+ children: /* @__PURE__ */ jsxs8(
723
+ DropdownMenu.Item,
724
+ {
725
+ onSelect: onExportMarkdown,
726
+ className: "aui-action-bar-more-item hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground flex cursor-pointer items-center gap-2 rounded-lg px-2.5 py-1.5 text-sm outline-none select-none",
727
+ children: [
728
+ /* @__PURE__ */ jsx13(DownloadIcon2, { className: "size-4" }),
729
+ "Export as Markdown"
730
+ ]
731
+ }
732
+ )
733
+ }
734
+ ) })
735
+ ] })
736
+ ]
737
+ }
738
+ );
739
+ }
740
+
741
+ // src/atoms/UserMessageActionBar.tsx
742
+ import { ActionBarPrimitive, useAui, useAuiState } from "@assistant-ui/react";
743
+ import { useActionBarCopy } from "@assistant-ui/core/react";
744
+ import { CheckIcon as CheckIcon3, CopyIcon as CopyIcon3, PencilIcon, RotateCcwIcon } from "lucide-react";
745
+ import { trueFoundryExtras } from "@truefoundry/assistant-ui-runtime";
746
+ import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
747
+ function parseTurnIdFromMessageId(messageId) {
748
+ return messageId.replace(/-user$/, "");
749
+ }
750
+ function UserMessageActionBar({ className }) {
751
+ const IconButton2 = useSlot("IconButton");
752
+ const aui = useAui();
753
+ const messageId = useAuiState((s) => s.message.id);
754
+ const turnId = parseTurnIdFromMessageId(messageId);
755
+ const { isCopied } = useActionBarCopy();
756
+ return /* @__PURE__ */ jsxs9(
757
+ ActionBarPrimitive.Root,
758
+ {
759
+ hideWhenRunning: true,
760
+ className: cn(
761
+ "aui-user-action-bar-root text-muted-foreground animate-in fade-in flex gap-1 duration-200",
762
+ className
763
+ ),
764
+ children: [
765
+ /* @__PURE__ */ jsx14(ActionBarPrimitive.Edit, { asChild: true, children: /* @__PURE__ */ jsx14(IconButton2, { tooltip: "Edit", children: /* @__PURE__ */ jsx14(PencilIcon, {}) }) }),
766
+ /* @__PURE__ */ jsx14(
767
+ IconButton2,
768
+ {
769
+ tooltip: "Reset",
770
+ onClick: () => void trueFoundryExtras.get(aui).resetFromTurn(turnId),
771
+ children: /* @__PURE__ */ jsx14(RotateCcwIcon, {})
772
+ }
773
+ ),
774
+ /* @__PURE__ */ jsx14(ActionBarPrimitive.Copy, { asChild: true, children: /* @__PURE__ */ jsx14(IconButton2, { tooltip: "Copy", children: isCopied ? /* @__PURE__ */ jsx14(CheckIcon3, { className: "animate-in zoom-in-50 fade-in duration-200 ease-out" }) : /* @__PURE__ */ jsx14(CopyIcon3, { className: "animate-in zoom-in-75 fade-in duration-150" }) }) })
775
+ ]
776
+ }
777
+ );
778
+ }
779
+
780
+ // src/atoms/MessageBubble.tsx
781
+ import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
782
+ function MessageBubble(props) {
783
+ if (props.variant === "assistant") {
784
+ const { children: children2, error, branchIndicator: branchIndicator2, actionBar: actionBar2, className: className2 } = props;
785
+ return /* @__PURE__ */ jsxs10(
786
+ "div",
787
+ {
788
+ "data-slot": "aui_assistant-message-root",
789
+ className: cn("fade-in slide-in-from-bottom-1 animate-in relative duration-150", className2),
790
+ children: [
791
+ /* @__PURE__ */ jsxs10(
792
+ "div",
793
+ {
794
+ "data-slot": "aui_assistant-message-content",
795
+ className: "text-foreground flex flex-col gap-3 px-2 leading-relaxed wrap-break-word [contain-intrinsic-size:auto_24px] [content-visibility:auto]",
796
+ children: [
797
+ children2,
798
+ error
799
+ ]
800
+ }
801
+ ),
802
+ /* @__PURE__ */ jsxs10("div", { "data-slot": "aui_assistant-message-footer", className: "ms-2 -mb-7.5 min-h-7.5 pt-1.5 flex items-center", children: [
803
+ branchIndicator2,
804
+ actionBar2
805
+ ] })
806
+ ]
807
+ }
808
+ );
809
+ }
810
+ const { children, attachments, branchIndicator, actionBar, className } = props;
811
+ return /* @__PURE__ */ jsxs10(
812
+ "div",
813
+ {
814
+ "data-slot": "aui_user-message-root",
815
+ className: cn(
816
+ "fade-in slide-in-from-bottom-1 animate-in grid auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] content-start gap-y-2 px-2 duration-150 [contain-intrinsic-size:auto_60px] [content-visibility:auto] [&:where(>*)]:col-start-2",
817
+ className
818
+ ),
819
+ children: [
820
+ attachments,
821
+ /* @__PURE__ */ jsx15("div", { className: "aui-user-message-content-wrapper relative col-start-2 min-w-0", children: /* @__PURE__ */ jsx15("div", { className: "aui-user-message-content bg-muted text-foreground rounded-xl px-4 py-2 wrap-break-word empty:hidden", children }) }),
822
+ (branchIndicator != null || actionBar != null) && /* @__PURE__ */ jsxs10(
823
+ "div",
824
+ {
825
+ "data-slot": "aui_user-message-footer",
826
+ className: "col-span-full col-start-1 row-start-3 -me-1 flex items-center justify-end gap-1",
827
+ children: [
828
+ branchIndicator,
829
+ actionBar
830
+ ]
831
+ }
832
+ )
833
+ ]
834
+ }
835
+ );
836
+ }
837
+
838
+ // src/atoms/MessageErrorBanner.tsx
839
+ import { jsx as jsx16 } from "react/jsx-runtime";
840
+ function MessageErrorBanner({ message, className }) {
841
+ return /* @__PURE__ */ jsx16(
842
+ "div",
843
+ {
844
+ role: "alert",
845
+ className: cn(
846
+ "aui-message-error-root border-destructive bg-destructive/10 text-destructive dark:bg-destructive/5 mt-2 rounded-md border p-3 text-sm dark:text-red-200",
847
+ className
848
+ ),
849
+ children: /* @__PURE__ */ jsx16("span", { className: "aui-message-error-message line-clamp-2", children: message })
850
+ }
851
+ );
852
+ }
853
+
854
+ // src/atoms/MessageIndicator.tsx
855
+ import { jsx as jsx17 } from "react/jsx-runtime";
856
+ function MessageIndicator({ className }) {
857
+ return /* @__PURE__ */ jsx17(
858
+ "span",
859
+ {
860
+ "data-slot": "aui_assistant-message-indicator",
861
+ className: cn("animate-pulse font-sans", className),
862
+ "aria-label": "Assistant is working",
863
+ children: "\u25CF"
864
+ }
865
+ );
866
+ }
867
+
868
+ // src/atoms/ScrollToBottomButton.tsx
869
+ import { forwardRef as forwardRef2 } from "react";
870
+ import { ArrowDownIcon } from "lucide-react";
871
+ import { jsx as jsx18 } from "react/jsx-runtime";
872
+ var ScrollToBottomButton = forwardRef2(
873
+ ({ className, ...rest }, ref) => {
874
+ return /* @__PURE__ */ jsx18(
875
+ IconButton,
876
+ {
877
+ ref,
878
+ tooltip: "Scroll to bottom",
879
+ variant: "outline",
880
+ className: cn(
881
+ "aui-thread-scroll-to-bottom dark:border-border dark:bg-background dark:hover:bg-accent absolute -top-14 z-10 self-center rounded-full p-4 disabled:invisible",
882
+ className
883
+ ),
884
+ ...rest,
885
+ children: /* @__PURE__ */ jsx18(ArrowDownIcon, {})
886
+ }
887
+ );
888
+ }
889
+ );
890
+ ScrollToBottomButton.displayName = "ScrollToBottomButton";
891
+
892
+ // src/atoms/Skeletons.tsx
893
+ import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
894
+ function MessageListSkeleton({ className }) {
895
+ return /* @__PURE__ */ jsxs11(
896
+ "div",
897
+ {
898
+ role: "status",
899
+ "aria-label": "Loading conversation",
900
+ "data-slot": "aui_thread-history-skeleton",
901
+ className: cn("mb-14 flex flex-col gap-y-6", className),
902
+ children: [
903
+ /* @__PURE__ */ jsx19("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx19(Skeleton, { className: "h-10 w-[min(85%,20rem)] rounded-xl" }) }),
904
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-2 px-2", children: [
905
+ /* @__PURE__ */ jsx19(Skeleton, { className: "h-4 w-full max-w-md" }),
906
+ /* @__PURE__ */ jsx19(Skeleton, { className: "h-4 w-full max-w-sm" }),
907
+ /* @__PURE__ */ jsx19(Skeleton, { className: "h-4 w-2/3 max-w-xs" })
908
+ ] })
909
+ ]
910
+ }
911
+ );
912
+ }
913
+
914
+ // src/atoms/ThreadShell.tsx
915
+ import { forwardRef as forwardRef3 } from "react";
916
+ import { jsx as jsx20 } from "react/jsx-runtime";
917
+ var THREAD_CSS_VARS = {
918
+ ["--thread-max-width"]: "44rem",
919
+ ["--composer-bg"]: "color-mix(in oklab, var(--color-muted) 30%, var(--color-background))",
920
+ ["--composer-radius"]: "1.5rem",
921
+ ["--composer-padding"]: "8px"
922
+ };
923
+ var ThreadRootShell = forwardRef3(
924
+ ({ className, style, ...rest }, ref) => /* @__PURE__ */ jsx20(
925
+ "div",
926
+ {
927
+ ref,
928
+ className: cn(
929
+ "aui-root aui-thread-root bg-background @container flex h-full min-h-0 flex-col overflow-hidden",
930
+ className
931
+ ),
932
+ style: { ...THREAD_CSS_VARS, ...style },
933
+ ...rest
934
+ }
935
+ )
936
+ );
937
+ ThreadRootShell.displayName = "ThreadRootShell";
938
+ var ThreadViewportShell = forwardRef3(
939
+ ({ className, isEmpty, children, ...rest }, ref) => /* @__PURE__ */ jsx20(
940
+ "div",
941
+ {
942
+ ref,
943
+ "data-slot": "aui_thread-viewport",
944
+ className: cn("relative flex min-h-0 flex-1 flex-col overflow-x-auto overflow-y-auto scroll-smooth", className),
945
+ ...rest,
946
+ children: /* @__PURE__ */ jsx20(
947
+ "div",
948
+ {
949
+ className: cn(
950
+ "mx-auto flex w-full max-w-(--thread-max-width) flex-col px-4 pt-4 pb-4",
951
+ isEmpty && "min-h-full justify-center"
952
+ ),
953
+ children
954
+ }
955
+ )
956
+ }
957
+ )
958
+ );
959
+ ThreadViewportShell.displayName = "ThreadViewportShell";
960
+ var ThreadComposerAreaShell = forwardRef3(
961
+ ({ className, isEmpty, ...rest }, ref) => /* @__PURE__ */ jsx20(
962
+ "div",
963
+ {
964
+ ref,
965
+ "data-slot": "aui_thread-composer",
966
+ className: cn(
967
+ "aui-thread-composer bg-background relative mx-auto flex w-full max-w-(--thread-max-width) shrink-0 flex-col gap-4 px-4 pb-4 md:pb-6",
968
+ !isEmpty && "rounded-t-(--composer-radius)",
969
+ className
970
+ ),
971
+ ...rest
972
+ }
973
+ )
974
+ );
975
+ ThreadComposerAreaShell.displayName = "ThreadComposerAreaShell";
976
+ var MessageGroup = forwardRef3(({ className, ...rest }, ref) => /* @__PURE__ */ jsx20(
977
+ "div",
978
+ {
979
+ ref,
980
+ "data-slot": "aui_message-group",
981
+ className: cn("flex flex-col gap-y-6 empty:hidden", className),
982
+ ...rest
983
+ }
984
+ ));
985
+ MessageGroup.displayName = "MessageGroup";
986
+
987
+ // src/atoms/AskUserPrompt.tsx
988
+ import { jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
989
+ var OTHER_OPTION_ID = "__other";
990
+ function AskUserPrompt({
991
+ question,
992
+ options,
993
+ allowOther,
994
+ selectedOptionId,
995
+ otherValue,
996
+ disabled,
997
+ onSelectOption,
998
+ onOtherValueChange,
999
+ onSubmit,
1000
+ className
1001
+ }) {
1002
+ const canSubmit = selectedOptionId != null && selectedOptionId !== OTHER_OPTION_ID ? true : selectedOptionId === OTHER_OPTION_ID && otherValue.trim().length > 0;
1003
+ return /* @__PURE__ */ jsxs12(
1004
+ "div",
1005
+ {
1006
+ "data-slot": "aui_ask-user-prompt",
1007
+ className: cn(
1008
+ "border-border/60 flex w-full flex-col gap-3 rounded-[var(--composer-radius,1.5rem)] border bg-[var(--composer-bg,var(--muted))] p-[var(--composer-padding,8px)]",
1009
+ className
1010
+ ),
1011
+ children: [
1012
+ /* @__PURE__ */ jsx21("p", { className: "text-foreground px-2.5 text-sm font-medium", children: question }),
1013
+ /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-1.5 px-2.5", children: [
1014
+ options.map((option) => /* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-2 text-sm", children: [
1015
+ /* @__PURE__ */ jsx21(
1016
+ "input",
1017
+ {
1018
+ type: "radio",
1019
+ name: "ask-user-option",
1020
+ checked: selectedOptionId === option.id,
1021
+ disabled,
1022
+ onChange: () => onSelectOption(option.id)
1023
+ }
1024
+ ),
1025
+ option.label
1026
+ ] }, option.id)),
1027
+ allowOther && /* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-2 text-sm", children: [
1028
+ /* @__PURE__ */ jsx21(
1029
+ "input",
1030
+ {
1031
+ type: "radio",
1032
+ name: "ask-user-option",
1033
+ checked: selectedOptionId === OTHER_OPTION_ID,
1034
+ disabled,
1035
+ onChange: () => onSelectOption(OTHER_OPTION_ID)
1036
+ }
1037
+ ),
1038
+ /* @__PURE__ */ jsx21(
1039
+ "input",
1040
+ {
1041
+ type: "text",
1042
+ value: otherValue,
1043
+ placeholder: "Type your answer...",
1044
+ disabled,
1045
+ onFocus: () => onSelectOption(OTHER_OPTION_ID),
1046
+ onChange: (event) => {
1047
+ onSelectOption(OTHER_OPTION_ID);
1048
+ onOtherValueChange(event.target.value);
1049
+ },
1050
+ "aria-label": "Other answer",
1051
+ className: "border-border/60 flex-1 rounded-md border bg-transparent px-2 py-1 text-sm outline-none"
1052
+ }
1053
+ )
1054
+ ] })
1055
+ ] }),
1056
+ /* @__PURE__ */ jsx21("div", { className: "flex justify-end px-2.5", children: /* @__PURE__ */ jsx21(
1057
+ Button,
1058
+ {
1059
+ type: "button",
1060
+ className: "rounded-full px-4",
1061
+ disabled: disabled || !canSubmit,
1062
+ onClick: onSubmit,
1063
+ children: "Submit"
1064
+ }
1065
+ ) })
1066
+ ]
1067
+ }
1068
+ );
1069
+ }
1070
+
1071
+ // src/atoms/AttachmentCard.tsx
1072
+ import { FileTextIcon, XIcon as XIcon2 } from "lucide-react";
1073
+ import { jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
1074
+ var USER_MESSAGE_ATTACHMENT_PREVIEW_REM = 12;
1075
+ function AttachmentCard({
1076
+ name,
1077
+ previewSrc,
1078
+ isImage = false,
1079
+ size = "chip",
1080
+ previewRem,
1081
+ onRemove,
1082
+ className
1083
+ }) {
1084
+ if (size === "preview" && isImage && previewSrc) {
1085
+ const previewSize = previewRem != null ? { width: `${previewRem}rem`, height: `${previewRem}rem` } : void 0;
1086
+ return /* @__PURE__ */ jsx22(
1087
+ "div",
1088
+ {
1089
+ "data-slot": "aui_attachment-preview",
1090
+ style: previewSize,
1091
+ className: cn(
1092
+ "aui-attachment-preview relative shrink-0 cursor-pointer overflow-hidden rounded-lg border",
1093
+ previewRem == null && "size-24",
1094
+ className
1095
+ ),
1096
+ children: /* @__PURE__ */ jsx22("img", { src: previewSrc, alt: name, className: "h-full w-full object-cover" })
1097
+ }
1098
+ );
1099
+ }
1100
+ return /* @__PURE__ */ jsxs13(
1101
+ "div",
1102
+ {
1103
+ "data-slot": "aui_attachment-chip",
1104
+ style: previewRem != null ? { maxWidth: `${previewRem}rem` } : void 0,
1105
+ className: cn(
1106
+ "aui-attachment-chip bg-muted flex max-w-full shrink-0 items-center gap-2 rounded-lg border px-2 py-1.5",
1107
+ className
1108
+ ),
1109
+ children: [
1110
+ /* @__PURE__ */ jsx22("div", { className: "bg-background flex size-7 shrink-0 items-center justify-center overflow-hidden rounded-md border", children: isImage && previewSrc ? /* @__PURE__ */ jsxs13(Avatar, { className: "size-7 rounded-none", children: [
1111
+ /* @__PURE__ */ jsx22(AvatarImage, { src: previewSrc, alt: name, className: "object-cover" }),
1112
+ /* @__PURE__ */ jsx22(AvatarFallback, { children: /* @__PURE__ */ jsx22(FileTextIcon, { className: "text-muted-foreground size-4" }) })
1113
+ ] }) : /* @__PURE__ */ jsx22(FileTextIcon, { className: "text-muted-foreground size-4" }) }),
1114
+ /* @__PURE__ */ jsx22("span", { className: "text-foreground min-w-0 truncate text-sm", children: name }),
1115
+ onRemove && /* @__PURE__ */ jsx22(
1116
+ IconButton,
1117
+ {
1118
+ tooltip: "Remove file",
1119
+ side: "top",
1120
+ className: "size-6 shrink-0 rounded-full [&_svg]:size-3",
1121
+ onClick: onRemove,
1122
+ children: /* @__PURE__ */ jsx22(XIcon2, {})
1123
+ }
1124
+ )
1125
+ ]
1126
+ }
1127
+ );
1128
+ }
1129
+
1130
+ // src/atoms/AttachmentPickerButton.tsx
1131
+ import { PlusIcon } from "lucide-react";
1132
+ import { jsx as jsx23 } from "react/jsx-runtime";
1133
+ function AttachmentPickerButton(props) {
1134
+ return /* @__PURE__ */ jsx23(IconButton, { tooltip: "Add Attachment", variant: "ghost", className: "size-7 rounded-full p-1", ...props, children: /* @__PURE__ */ jsx23(PlusIcon, { className: "size-4.5" }) });
1135
+ }
1136
+
1137
+ // src/atoms/AttachmentPreviewDialog.tsx
1138
+ import { Fragment as Fragment2, jsx as jsx24, jsxs as jsxs14 } from "react/jsx-runtime";
1139
+ function AttachmentPreviewDialog({ previewSrc, children }) {
1140
+ if (!previewSrc) return /* @__PURE__ */ jsx24(Fragment2, { children });
1141
+ return /* @__PURE__ */ jsxs14(Dialog, { children: [
1142
+ /* @__PURE__ */ jsx24(DialogTrigger, { asChild: true, children }),
1143
+ /* @__PURE__ */ jsxs14(DialogContent, { className: "p-2 sm:max-w-3xl", children: [
1144
+ /* @__PURE__ */ jsx24(DialogTitle, { className: "sr-only", children: "Image Attachment Preview" }),
1145
+ /* @__PURE__ */ jsx24("div", { className: "bg-background relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden", children: /* @__PURE__ */ jsx24(
1146
+ "img",
1147
+ {
1148
+ src: previewSrc,
1149
+ alt: "Attachment preview",
1150
+ className: "block h-auto max-h-[80vh] w-auto max-w-full object-contain"
1151
+ }
1152
+ ) })
1153
+ ] })
1154
+ ] });
1155
+ }
1156
+
1157
+ // src/atoms/ComposerShell.tsx
1158
+ import { ArrowUpIcon, LoaderIcon as LoaderIcon2, PlusIcon as PlusIcon2 } from "lucide-react";
1159
+ import { jsx as jsx25, jsxs as jsxs15 } from "react/jsx-runtime";
1160
+ function ComposerShell({
1161
+ value,
1162
+ placeholder,
1163
+ disabled,
1164
+ isRunning = false,
1165
+ attachments,
1166
+ modelLabel,
1167
+ modelIcon,
1168
+ connectorStatusLabel,
1169
+ onValueChange,
1170
+ onSubmit,
1171
+ onCancel,
1172
+ onAttach,
1173
+ className
1174
+ }) {
1175
+ return /* @__PURE__ */ jsxs15(
1176
+ "div",
1177
+ {
1178
+ "data-slot": "aui_composer-shell",
1179
+ className: cn(
1180
+ "border-border/60 focus-within:border-ring focus-within:ring-ring/20 flex w-full flex-col gap-2 rounded-[var(--composer-radius,1.5rem)] border bg-[var(--composer-bg,var(--muted))] p-[var(--composer-padding,8px)] shadow-sm transition-colors focus-within:ring-3",
1181
+ className
1182
+ ),
1183
+ children: [
1184
+ attachments,
1185
+ /* @__PURE__ */ jsx25(
1186
+ "textarea",
1187
+ {
1188
+ value,
1189
+ placeholder,
1190
+ disabled,
1191
+ rows: 1,
1192
+ "aria-label": "Message input",
1193
+ onChange: (event) => onValueChange(event.target.value),
1194
+ onKeyDown: (event) => {
1195
+ if (event.key === "Enter" && !event.shiftKey) {
1196
+ event.preventDefault();
1197
+ onSubmit();
1198
+ }
1199
+ },
1200
+ className: "placeholder:text-muted-foreground/80 max-h-32 min-h-10 w-full resize-none rounded-lg bg-transparent px-2.5 py-1 text-base outline-none"
1201
+ }
1202
+ ),
1203
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between px-1", children: [
1204
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
1205
+ onAttach && /* @__PURE__ */ jsx25(IconButton, { tooltip: "Attach", onClick: onAttach, children: /* @__PURE__ */ jsx25(PlusIcon2, {}) }),
1206
+ connectorStatusLabel && /* @__PURE__ */ jsx25("span", { className: "text-muted-foreground text-xs", children: connectorStatusLabel })
1207
+ ] }),
1208
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
1209
+ modelLabel && /* @__PURE__ */ jsxs15("span", { className: "bg-muted text-muted-foreground flex items-center gap-1 rounded-full px-2 py-0.5 text-xs", children: [
1210
+ modelIcon,
1211
+ modelLabel
1212
+ ] }),
1213
+ isRunning ? /* @__PURE__ */ jsxs15(
1214
+ Button,
1215
+ {
1216
+ variant: "default",
1217
+ size: "sm",
1218
+ className: "gap-1.5 rounded-full",
1219
+ disabled: !onCancel,
1220
+ onClick: onCancel,
1221
+ children: [
1222
+ /* @__PURE__ */ jsx25(LoaderIcon2, { className: "size-3.5 animate-spin [animation-duration:0.6s]" }),
1223
+ "Cancel"
1224
+ ]
1225
+ }
1226
+ ) : /* @__PURE__ */ jsx25(
1227
+ IconButton,
1228
+ {
1229
+ tooltip: "Send message",
1230
+ variant: "default",
1231
+ disabled: disabled || value.trim().length === 0,
1232
+ onClick: onSubmit,
1233
+ children: /* @__PURE__ */ jsx25(ArrowUpIcon, {})
1234
+ }
1235
+ )
1236
+ ] })
1237
+ ] })
1238
+ ]
1239
+ }
1240
+ );
1241
+ }
1242
+
1243
+ // src/atoms/McpAuthPrompt.tsx
1244
+ import { ExternalLinkIcon } from "lucide-react";
1245
+ import { jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
1246
+ function McpAuthPrompt({ servers, disabled, onContinue, className }) {
1247
+ return /* @__PURE__ */ jsxs16("div", { "data-slot": "aui_mcp-auth-continue-composer", className: cn("flex w-full flex-col gap-3", className), children: [
1248
+ /* @__PURE__ */ jsxs16("div", { className: "border-primary/20 bg-background overflow-hidden rounded-2xl border", children: [
1249
+ /* @__PURE__ */ jsx26("div", { className: "bg-primary/10 border-primary/20 border-b px-4 py-2.5", children: /* @__PURE__ */ jsx26("p", { className: "text-primary text-sm font-semibold", children: "MCP Authentication Required" }) }),
1250
+ /* @__PURE__ */ jsx26("ul", { className: "divide-border/60 divide-y", children: servers.map((server) => /* @__PURE__ */ jsxs16(
1251
+ "li",
1252
+ {
1253
+ className: "flex items-center justify-between gap-3 px-4 py-2.5",
1254
+ children: [
1255
+ /* @__PURE__ */ jsxs16("p", { className: "text-sm", children: [
1256
+ /* @__PURE__ */ jsx26("span", { className: "text-muted-foreground", children: "MCP Server Name" }),
1257
+ /* @__PURE__ */ jsx26("span", { className: "text-muted-foreground mx-1.5", children: ":" }),
1258
+ /* @__PURE__ */ jsx26("span", { className: "font-semibold", children: server.name })
1259
+ ] }),
1260
+ /* @__PURE__ */ jsx26(
1261
+ Button,
1262
+ {
1263
+ asChild: true,
1264
+ size: "sm",
1265
+ className: "bg-foreground text-background hover:bg-foreground/80 rounded-full",
1266
+ children: /* @__PURE__ */ jsxs16("a", { href: server.authUrl, target: "_blank", rel: "noopener noreferrer", children: [
1267
+ "Connect",
1268
+ /* @__PURE__ */ jsx26(ExternalLinkIcon, { className: "size-3.5" })
1269
+ ] })
1270
+ }
1271
+ )
1272
+ ]
1273
+ },
1274
+ server.id
1275
+ )) })
1276
+ ] }),
1277
+ /* @__PURE__ */ jsx26("div", { className: "flex justify-end px-2.5", children: /* @__PURE__ */ jsx26(Button, { type: "button", className: "rounded-full px-4", disabled, onClick: onContinue, children: "Continue" }) })
1278
+ ] });
1279
+ }
1280
+
1281
+ // src/atoms/ReasoningCard.tsx
1282
+ import { BrainIcon, ChevronDownIcon } from "lucide-react";
1283
+ import { jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
1284
+ function ReasoningCard({ streaming, expanded, onToggle, children, className }) {
1285
+ return /* @__PURE__ */ jsxs17(
1286
+ Collapsible,
1287
+ {
1288
+ "data-slot": "reasoning-card",
1289
+ open: expanded,
1290
+ onOpenChange: onToggle,
1291
+ className: cn("aui-reasoning-card w-full rounded-lg border px-3 py-2", className),
1292
+ children: [
1293
+ /* @__PURE__ */ jsxs17(CollapsibleTrigger, { className: "text-muted-foreground hover:text-foreground flex max-w-[75%] origin-left items-center gap-2 py-1.5 text-sm transition-[color,scale] active:scale-[0.98]", children: [
1294
+ /* @__PURE__ */ jsx27(BrainIcon, { className: "size-4 shrink-0" }),
1295
+ /* @__PURE__ */ jsxs17("span", { className: "leading-none tabular-nums", children: [
1296
+ "Reasoning",
1297
+ streaming && /* @__PURE__ */ jsxs17("span", { "aria-hidden": true, className: "shimmer pointer-events-none", children: [
1298
+ " ",
1299
+ "\u2026"
1300
+ ] })
1301
+ ] }),
1302
+ /* @__PURE__ */ jsx27(
1303
+ ChevronDownIcon,
1304
+ {
1305
+ className: cn(
1306
+ "mt-0.5 size-4 shrink-0 transition-transform duration-200 ease-[cubic-bezier(0.32,0.72,0,1)]",
1307
+ expanded ? "rotate-0" : "-rotate-90"
1308
+ )
1309
+ }
1310
+ )
1311
+ ] }),
1312
+ /* @__PURE__ */ jsx27(CollapsibleContent, { className: "text-muted-foreground max-h-64 overflow-y-auto pt-2 pb-1 pl-6 text-sm leading-relaxed", children })
1313
+ ]
1314
+ }
1315
+ );
1316
+ }
1317
+
1318
+ // src/atoms/ToolApprovalBar.tsx
1319
+ import { useState as useState3 } from "react";
1320
+ import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
1321
+ function ToolApprovalBar({ options, onSelectOption, className }) {
1322
+ const [confirmingId, setConfirmingId] = useState3(null);
1323
+ const confirming = confirmingId != null ? options.find((o) => o.id === confirmingId) : void 0;
1324
+ if (confirming) {
1325
+ return /* @__PURE__ */ jsxs18("div", { className: cn("aui-tool-approval-confirm flex flex-col gap-2 pt-1", className), children: [
1326
+ /* @__PURE__ */ jsx28("p", { className: "font-semibold", children: confirming.confirm?.title ?? `${confirming.label}?` }),
1327
+ confirming.confirm?.description && /* @__PURE__ */ jsx28("p", { className: "text-muted-foreground", children: confirming.confirm.description }),
1328
+ confirming.grants && confirming.grants.length > 0 && /* @__PURE__ */ jsx28("ul", { className: "flex flex-col gap-1", children: confirming.grants.map((grant) => /* @__PURE__ */ jsx28("li", { children: /* @__PURE__ */ jsx28("code", { className: "bg-muted rounded px-1.5 py-0.5 text-xs", children: grant }) }, grant)) }),
1329
+ /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", children: [
1330
+ /* @__PURE__ */ jsx28(
1331
+ Button,
1332
+ {
1333
+ size: "sm",
1334
+ className: "active:scale-[0.98]",
1335
+ onClick: () => {
1336
+ onSelectOption(confirming.id);
1337
+ setConfirmingId(null);
1338
+ },
1339
+ children: "Confirm"
1340
+ }
1341
+ ),
1342
+ /* @__PURE__ */ jsx28(
1343
+ Button,
1344
+ {
1345
+ size: "sm",
1346
+ variant: "outline",
1347
+ className: "active:scale-[0.98]",
1348
+ onClick: () => setConfirmingId(null),
1349
+ children: "Back"
1350
+ }
1351
+ )
1352
+ ] })
1353
+ ] });
1354
+ }
1355
+ const firstAllowId = options.find((o) => o.isAllow)?.id;
1356
+ return /* @__PURE__ */ jsx28("div", { className: cn("aui-tool-approval-bar flex flex-wrap items-center gap-2 pt-1", className), children: options.map((option) => /* @__PURE__ */ jsx28(
1357
+ Button,
1358
+ {
1359
+ size: "sm",
1360
+ variant: option.id === firstAllowId ? "default" : "outline",
1361
+ className: "active:scale-[0.98]",
1362
+ onClick: () => option.confirm ? setConfirmingId(option.id) : onSelectOption(option.id),
1363
+ children: option.label
1364
+ },
1365
+ option.id
1366
+ )) });
1367
+ }
1368
+
1369
+ // src/atoms/ThreadListRow.tsx
1370
+ import { ArchiveIcon, MoreHorizontalIcon as MoreHorizontalIcon2, TrashIcon } from "lucide-react";
1371
+ import { DropdownMenu as DropdownMenu2 } from "radix-ui";
1372
+ import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
1373
+ function ThreadListRow({ title, active, onSelect, onArchive, onDelete, className }) {
1374
+ return /* @__PURE__ */ jsxs19(
1375
+ "div",
1376
+ {
1377
+ "data-slot": "aui_thread-list-item",
1378
+ "data-active": active || void 0,
1379
+ className: cn(
1380
+ "group hover:bg-muted data-[active]:bg-muted relative flex min-h-9 items-center rounded-lg transition-colors",
1381
+ className
1382
+ ),
1383
+ children: [
1384
+ /* @__PURE__ */ jsx29(
1385
+ "button",
1386
+ {
1387
+ type: "button",
1388
+ onClick: onSelect,
1389
+ "data-slot": "aui_thread-list-item-trigger",
1390
+ className: "flex min-h-9 min-w-0 flex-1 items-center rounded-lg px-2.5 py-2 text-start text-sm outline-none group-hover:pe-9",
1391
+ children: /* @__PURE__ */ jsx29("span", { className: "min-w-0 flex-1 truncate", children: title })
1392
+ }
1393
+ ),
1394
+ (onArchive || onDelete) && /* @__PURE__ */ jsxs19(DropdownMenu2.Root, { children: [
1395
+ /* @__PURE__ */ jsx29(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx29(
1396
+ IconButton,
1397
+ {
1398
+ tooltip: "More options",
1399
+ className: "absolute end-1.5 top-1/2 size-6 -translate-y-1/2 p-0 opacity-0 group-hover:opacity-100 data-[state=open]:opacity-100",
1400
+ children: /* @__PURE__ */ jsx29(MoreHorizontalIcon2, { className: "size-3.5" })
1401
+ }
1402
+ ) }),
1403
+ /* @__PURE__ */ jsx29(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs19(
1404
+ DropdownMenu2.Content,
1405
+ {
1406
+ side: "right",
1407
+ align: "start",
1408
+ sideOffset: 6,
1409
+ className: "bg-popover/95 text-popover-foreground z-50 min-w-32 overflow-hidden rounded-xl border p-1.5 shadow-lg backdrop-blur-sm",
1410
+ children: [
1411
+ onArchive && /* @__PURE__ */ jsxs19(
1412
+ DropdownMenu2.Item,
1413
+ {
1414
+ onSelect: onArchive,
1415
+ className: "hover:bg-accent hover:text-accent-foreground flex cursor-pointer items-center gap-2 rounded-lg px-2.5 py-1.5 text-sm outline-none select-none",
1416
+ children: [
1417
+ /* @__PURE__ */ jsx29(ArchiveIcon, { className: "size-4" }),
1418
+ "Archive"
1419
+ ]
1420
+ }
1421
+ ),
1422
+ onDelete && /* @__PURE__ */ jsxs19(
1423
+ DropdownMenu2.Item,
1424
+ {
1425
+ onSelect: onDelete,
1426
+ className: "text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-2 rounded-lg px-2.5 py-1.5 text-sm outline-none select-none",
1427
+ children: [
1428
+ /* @__PURE__ */ jsx29(TrashIcon, { className: "size-4" }),
1429
+ "Delete"
1430
+ ]
1431
+ }
1432
+ )
1433
+ ]
1434
+ }
1435
+ ) })
1436
+ ] })
1437
+ ]
1438
+ }
1439
+ );
1440
+ }
1441
+
1442
+ // src/atoms/ThreadListMisc.tsx
1443
+ import { PlusIcon as PlusIcon3 } from "lucide-react";
1444
+ import { jsx as jsx30, jsxs as jsxs20 } from "react/jsx-runtime";
1445
+ function ThreadListNewButton({ className, ...rest }) {
1446
+ return /* @__PURE__ */ jsxs20(
1447
+ Button,
1448
+ {
1449
+ variant: "ghost",
1450
+ "data-slot": "aui_thread-list-new",
1451
+ className: cn("hover:bg-muted h-8 justify-start gap-2 rounded-md px-2.5 text-sm font-normal", className),
1452
+ ...rest,
1453
+ children: [
1454
+ /* @__PURE__ */ jsx30(PlusIcon3, { className: "size-4 shrink-0" }),
1455
+ /* @__PURE__ */ jsx30("span", { className: "whitespace-nowrap", children: "New Thread" })
1456
+ ]
1457
+ }
1458
+ );
1459
+ }
1460
+ function ThreadListRowSkeleton({ count = 5, className }) {
1461
+ return /* @__PURE__ */ jsx30("div", { className: cn("flex flex-col gap-1", className), role: "status", "aria-label": "Loading threads", children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsx30("div", { className: "flex h-8 items-center px-2.5", children: /* @__PURE__ */ jsx30(Skeleton, { className: "h-3.5 w-full" }) }, i)) });
1462
+ }
1463
+ function ThreadListEmptyState({ message = "No threads yet", className }) {
1464
+ return /* @__PURE__ */ jsx30("div", { className: cn("text-muted-foreground flex flex-1 items-center justify-center px-4 text-center text-sm", className), children: message });
1465
+ }
1466
+ function ThreadListShell({ header, children, className }) {
1467
+ return /* @__PURE__ */ jsxs20("div", { className: cn("flex min-h-0 flex-1 flex-col gap-1 overflow-hidden p-3", className), children: [
1468
+ header,
1469
+ /* @__PURE__ */ jsx30("div", { className: "flex min-h-0 flex-1 flex-col gap-3 overflow-y-auto pb-1", children })
1470
+ ] });
1471
+ }
1472
+
1473
+ // src/atoms/Toast.tsx
1474
+ import { XIcon as XIcon3 } from "lucide-react";
1475
+ import { Toast as ToastPrimitive } from "radix-ui";
1476
+ import { jsx as jsx31, jsxs as jsxs21 } from "react/jsx-runtime";
1477
+ function Toast({ title, description, open, onOpenChange, className }) {
1478
+ return /* @__PURE__ */ jsxs21(
1479
+ ToastPrimitive.Root,
1480
+ {
1481
+ open,
1482
+ onOpenChange,
1483
+ className: cn(
1484
+ "border-destructive bg-background text-destructive dark:bg-card pointer-events-auto relative flex max-h-[min(70vh,32rem)] w-full items-start gap-3 rounded-xl border p-4 shadow-lg dark:text-red-200",
1485
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-bottom-4",
1486
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-bottom-4",
1487
+ className
1488
+ ),
1489
+ children: [
1490
+ /* @__PURE__ */ jsxs21("div", { className: "grid min-h-0 min-w-0 flex-1 gap-1 overflow-y-auto pe-6", children: [
1491
+ /* @__PURE__ */ jsx31(ToastPrimitive.Title, { className: "text-sm leading-none font-semibold", children: title }),
1492
+ /* @__PURE__ */ jsx31(ToastPrimitive.Description, { className: "font-mono text-sm break-words whitespace-pre-wrap", children: description })
1493
+ ] }),
1494
+ /* @__PURE__ */ jsxs21(ToastPrimitive.Close, { className: "text-destructive hover:bg-muted focus-visible:ring-ring absolute top-3 right-3 rounded-md p-1 transition-colors focus:outline-none focus-visible:ring-2", children: [
1495
+ /* @__PURE__ */ jsx31(XIcon3, { className: "size-4" }),
1496
+ /* @__PURE__ */ jsx31("span", { className: "sr-only", children: "Close" })
1497
+ ] })
1498
+ ]
1499
+ }
1500
+ );
1501
+ }
1502
+ function ToastStack({ children, duration = Number.POSITIVE_INFINITY }) {
1503
+ return /* @__PURE__ */ jsxs21(ToastPrimitive.Provider, { duration, children: [
1504
+ children,
1505
+ /* @__PURE__ */ jsx31(ToastPrimitive.Viewport, { className: "fixed inset-x-0 bottom-0 z-50 flex max-h-screen flex-col-reverse gap-2 p-4 sm:bottom-4 sm:left-1/2 sm:w-full sm:max-w-2xl sm:-translate-x-1/2" })
1506
+ ] });
1507
+ }
1508
+
1509
+ // src/atoms/ToolCallCard.tsx
1510
+ import { BriefcaseIcon, CheckIcon as CheckIcon4, ChevronDownIcon as ChevronDownIcon2, LoaderIcon as LoaderIcon3, PlugIcon, WrenchIcon, XCircleIcon } from "lucide-react";
1511
+ import { jsx as jsx32, jsxs as jsxs22 } from "react/jsx-runtime";
1512
+ var variantIcon = {
1513
+ tool: WrenchIcon,
1514
+ "sub-agent": BriefcaseIcon,
1515
+ "mcp-listing": PlugIcon
1516
+ };
1517
+ function StatusIcon({ status }) {
1518
+ if (status === "running") {
1519
+ return /* @__PURE__ */ jsx32(
1520
+ LoaderIcon3,
1521
+ {
1522
+ "data-slot": "tool-call-card-status-icon",
1523
+ className: "text-muted-foreground size-4 shrink-0 animate-spin [animation-duration:0.6s]"
1524
+ }
1525
+ );
1526
+ }
1527
+ if (status === "success") {
1528
+ return /* @__PURE__ */ jsx32(
1529
+ CheckIcon4,
1530
+ {
1531
+ "data-slot": "tool-call-card-status-icon",
1532
+ className: "size-4 shrink-0 text-emerald-600 dark:text-emerald-400"
1533
+ }
1534
+ );
1535
+ }
1536
+ return /* @__PURE__ */ jsx32(XCircleIcon, { "data-slot": "tool-call-card-status-icon", className: "text-destructive size-4 shrink-0" });
1537
+ }
1538
+ function IconChip({ Icon }) {
1539
+ return /* @__PURE__ */ jsx32("span", { className: "bg-muted text-muted-foreground flex size-6 shrink-0 items-center justify-center rounded-md", children: /* @__PURE__ */ jsx32(Icon, { className: "size-3.5" }) });
1540
+ }
1541
+ function ToolCallCard(props) {
1542
+ const { variant, name, status, expanded, onToggle, durationText, className } = props;
1543
+ const VariantIcon = variantIcon[variant];
1544
+ return /* @__PURE__ */ jsxs22(
1545
+ Collapsible,
1546
+ {
1547
+ "data-slot": "tool-call-card",
1548
+ "data-variant": variant,
1549
+ open: expanded,
1550
+ onOpenChange: onToggle,
1551
+ className: cn("aui-tool-call-card w-full rounded-lg border py-2", className),
1552
+ children: [
1553
+ /* @__PURE__ */ jsxs22(CollapsibleTrigger, { className: "flex w-full items-center gap-2 px-3 text-left", children: [
1554
+ /* @__PURE__ */ jsx32(IconChip, { Icon: VariantIcon }),
1555
+ variant === "tool" && /* @__PURE__ */ jsx32("span", { className: "text-sm", children: name }),
1556
+ variant === "sub-agent" && /* @__PURE__ */ jsxs22("span", { className: "flex min-w-0 flex-col items-start", children: [
1557
+ /* @__PURE__ */ jsx32("span", { className: "text-muted-foreground text-[10px] font-semibold tracking-wide uppercase", children: "Sub-agent" }),
1558
+ /* @__PURE__ */ jsx32("span", { className: "truncate text-sm font-medium", children: props.agentName })
1559
+ ] }),
1560
+ variant === "mcp-listing" && /* @__PURE__ */ jsxs22("span", { className: "truncate text-sm", children: [
1561
+ "Listing tools \xB7 ",
1562
+ props.serverName
1563
+ ] }),
1564
+ /* @__PURE__ */ jsxs22("span", { className: "ml-auto flex shrink-0 items-center gap-2", children: [
1565
+ durationText && /* @__PURE__ */ jsx32("span", { className: "text-muted-foreground text-xs tabular-nums", children: durationText }),
1566
+ /* @__PURE__ */ jsx32(StatusIcon, { status }),
1567
+ /* @__PURE__ */ jsx32(
1568
+ ChevronDownIcon2,
1569
+ {
1570
+ className: cn(
1571
+ "size-4 shrink-0 transition-transform duration-200 ease-[cubic-bezier(0.32,0.72,0,1)]",
1572
+ expanded ? "rotate-0" : "-rotate-90"
1573
+ )
1574
+ }
1575
+ )
1576
+ ] })
1577
+ ] }),
1578
+ /* @__PURE__ */ jsxs22(CollapsibleContent, { className: "px-3 pt-2", children: [
1579
+ variant === "tool" && /* @__PURE__ */ jsxs22("div", { className: "flex flex-col gap-2", children: [
1580
+ props.approvalSlot,
1581
+ props.argsText && /* @__PURE__ */ jsxs22("div", { children: [
1582
+ /* @__PURE__ */ jsx32("p", { className: "text-muted-foreground text-xs font-medium", children: "Request:" }),
1583
+ /* @__PURE__ */ jsx32("pre", { className: "bg-muted/50 text-foreground/90 mt-1 rounded-md p-2.5 text-xs break-words whitespace-pre-wrap", children: props.argsText })
1584
+ ] }),
1585
+ props.result !== void 0 && /* @__PURE__ */ jsxs22("div", { children: [
1586
+ /* @__PURE__ */ jsx32("p", { className: "text-muted-foreground text-xs font-medium", children: "Result:" }),
1587
+ /* @__PURE__ */ jsx32(
1588
+ "pre",
1589
+ {
1590
+ className: cn(
1591
+ "bg-muted/50 mt-1 rounded-md p-2.5 text-xs break-words whitespace-pre-wrap",
1592
+ props.isError ? "text-destructive" : "text-foreground/90"
1593
+ ),
1594
+ children: props.result
1595
+ }
1596
+ )
1597
+ ] })
1598
+ ] }),
1599
+ variant === "sub-agent" && /* @__PURE__ */ jsxs22("div", { className: "flex flex-col gap-2", children: [
1600
+ /* @__PURE__ */ jsx32("p", { className: "text-muted-foreground line-clamp-2 text-sm", children: props.instruction }),
1601
+ /* @__PURE__ */ jsxs22("p", { className: "text-muted-foreground text-[10px] font-medium tracking-wide uppercase", children: [
1602
+ "Steps \xB7 ",
1603
+ props.stepCount,
1604
+ " steps"
1605
+ ] }),
1606
+ /* @__PURE__ */ jsx32("div", { className: "border-border/60 flex flex-col gap-2 border-l pl-3", children: props.children })
1607
+ ] }),
1608
+ variant === "mcp-listing" && /* @__PURE__ */ jsxs22("div", { className: "flex flex-col gap-2", children: [
1609
+ /* @__PURE__ */ jsx32("p", { className: "text-muted-foreground text-sm", children: props.description }),
1610
+ /* @__PURE__ */ jsxs22("div", { children: [
1611
+ /* @__PURE__ */ jsx32("p", { className: "text-muted-foreground text-xs font-medium", children: "Request:" }),
1612
+ /* @__PURE__ */ jsx32("pre", { className: "bg-muted/50 text-foreground/90 mt-1 rounded-md p-2.5 text-xs break-words whitespace-pre-wrap", children: props.argsText })
1613
+ ] }),
1614
+ /* @__PURE__ */ jsxs22("div", { children: [
1615
+ /* @__PURE__ */ jsx32("p", { className: "text-muted-foreground text-xs font-medium", children: "Result:" }),
1616
+ /* @__PURE__ */ jsx32("pre", { className: "bg-muted/50 text-foreground/90 mt-1 rounded-md p-2.5 text-xs break-words whitespace-pre-wrap", children: props.resultText })
1617
+ ] })
1618
+ ] })
1619
+ ] })
1620
+ ]
1621
+ }
1622
+ );
1623
+ }
1624
+
1625
+ // src/atoms/ToolGroupCard.tsx
1626
+ import { ChevronDownIcon as ChevronDownIcon3, LoaderIcon as LoaderIcon4 } from "lucide-react";
1627
+ import { jsx as jsx33, jsxs as jsxs23 } from "react/jsx-runtime";
1628
+ function ToolGroupCard({
1629
+ toolCallCount,
1630
+ expanded,
1631
+ active = false,
1632
+ onToggle,
1633
+ children,
1634
+ className
1635
+ }) {
1636
+ const label = `${toolCallCount} tool ${toolCallCount === 1 ? "call" : "calls"}`;
1637
+ return /* @__PURE__ */ jsxs23(
1638
+ Collapsible,
1639
+ {
1640
+ "data-slot": "tool-group-card",
1641
+ open: expanded,
1642
+ onOpenChange: onToggle,
1643
+ className: cn("aui-tool-group-card group/tool-group w-full", className),
1644
+ children: [
1645
+ /* @__PURE__ */ jsxs23(CollapsibleTrigger, { className: "text-muted-foreground hover:text-foreground flex origin-left items-center gap-2 py-1.5 text-sm transition-[color,scale] active:scale-[0.98]", children: [
1646
+ active && /* @__PURE__ */ jsx33(LoaderIcon4, { className: "size-3 shrink-0 animate-spin [animation-duration:0.6s]" }),
1647
+ /* @__PURE__ */ jsx33("span", { className: "text-xs font-medium", children: label }),
1648
+ /* @__PURE__ */ jsx33(
1649
+ ChevronDownIcon3,
1650
+ {
1651
+ className: cn(
1652
+ "size-3 shrink-0 transition-transform duration-200 ease-[cubic-bezier(0.32,0.72,0,1)]",
1653
+ expanded ? "rotate-0" : "-rotate-90"
1654
+ )
1655
+ }
1656
+ )
1657
+ ] }),
1658
+ /* @__PURE__ */ jsx33(CollapsibleContent, { className: "mt-1 flex flex-col gap-1", children })
1659
+ ]
1660
+ }
1661
+ );
1662
+ }
1663
+
1664
+ // src/atoms/WelcomeScreen.tsx
1665
+ import { jsx as jsx34 } from "react/jsx-runtime";
1666
+ function WelcomeScreen({ heading = "How can I help you today?", className }) {
1667
+ return /* @__PURE__ */ jsx34("div", { className: cn("aui-thread-welcome-root mb-6 flex flex-col items-center px-4 text-center", className), children: /* @__PURE__ */ jsx34("h1", { className: "aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both text-2xl font-semibold duration-200", children: heading }) });
1668
+ }
1669
+
1670
+ // src/theme/defaultSlots.ts
1671
+ var defaultSlots = {
1672
+ Button,
1673
+ IconButton,
1674
+ Avatar,
1675
+ AvatarImage,
1676
+ AvatarFallback,
1677
+ Tooltip,
1678
+ TooltipProvider,
1679
+ TooltipTrigger,
1680
+ TooltipContent,
1681
+ Dialog,
1682
+ DialogTrigger,
1683
+ DialogPortal,
1684
+ DialogClose,
1685
+ DialogOverlay,
1686
+ DialogContent,
1687
+ DialogHeader,
1688
+ DialogFooter,
1689
+ DialogTitle,
1690
+ DialogDescription,
1691
+ Collapsible,
1692
+ CollapsibleTrigger,
1693
+ CollapsibleContent,
1694
+ Skeleton,
1695
+ Markdown,
1696
+ OpenUIBlock,
1697
+ SandboxArtifactList,
1698
+ CodeBlockHeader,
1699
+ MessageBubble,
1700
+ MessageErrorBanner,
1701
+ MessageActionBar,
1702
+ UserMessageActionBar,
1703
+ BranchIndicator,
1704
+ MessageIndicator,
1705
+ WelcomeScreen,
1706
+ MessageListSkeleton,
1707
+ ScrollToBottomButton,
1708
+ ThreadRootShell,
1709
+ ThreadViewportShell,
1710
+ ThreadComposerAreaShell,
1711
+ MessageGroup,
1712
+ ToolCallCard,
1713
+ ToolApprovalBar,
1714
+ ToolGroupCard,
1715
+ ReasoningCard,
1716
+ ComposerShell,
1717
+ AskUserPrompt,
1718
+ McpAuthPrompt,
1719
+ AttachmentCard,
1720
+ AttachmentPreviewDialog,
1721
+ AttachmentPickerButton,
1722
+ ThreadListRow,
1723
+ ThreadListNewButton,
1724
+ ThreadListRowSkeleton,
1725
+ ThreadListEmptyState,
1726
+ ThreadListShell,
1727
+ Toast,
1728
+ ToastStack
1729
+ };
1730
+
1731
+ // src/theme/SlotsProvider.tsx
1732
+ import { jsx as jsx35 } from "react/jsx-runtime";
1733
+ var SlotsContext = createContext(defaultSlots);
1734
+ function SlotsProvider({
1735
+ overrides,
1736
+ children
1737
+ }) {
1738
+ const parentSlots = useContext(SlotsContext);
1739
+ const resolved = useMemo2(
1740
+ () => ({ ...parentSlots, ...overrides }),
1741
+ [parentSlots, overrides]
1742
+ );
1743
+ return /* @__PURE__ */ jsx35(SlotsContext.Provider, { value: resolved, children });
1744
+ }
1745
+ function useSlot(name) {
1746
+ const slots = useContext(SlotsContext);
1747
+ return slots[name];
1748
+ }
1749
+
1750
+ // src/theme/tokens.ts
1751
+ import { createContext as createContext2, createElement, useContext as useContext2 } from "react";
1752
+ var defaultTokens = {
1753
+ colors: {
1754
+ background: "var(--background)",
1755
+ foreground: "var(--foreground)",
1756
+ muted: "var(--muted)",
1757
+ mutedForeground: "var(--muted-foreground)",
1758
+ border: "var(--border)",
1759
+ primary: "var(--primary)",
1760
+ primaryForeground: "var(--primary-foreground)",
1761
+ destructive: "var(--destructive)",
1762
+ destructiveForeground: "var(--destructive-foreground)",
1763
+ accent: "var(--accent)",
1764
+ accentForeground: "var(--accent-foreground)"
1765
+ },
1766
+ radii: {
1767
+ none: "0px",
1768
+ sm: "calc(var(--radius) - 4px)",
1769
+ md: "calc(var(--radius) - 2px)",
1770
+ lg: "var(--radius)",
1771
+ full: "9999px"
1772
+ },
1773
+ spacing: {
1774
+ none: "0px",
1775
+ xs: "0.25rem",
1776
+ sm: "0.5rem",
1777
+ md: "0.75rem",
1778
+ lg: "1rem",
1779
+ xl: "1.5rem"
1780
+ },
1781
+ typography: {
1782
+ body: { fontSize: "0.875rem", lineHeight: "1.5rem", fontWeight: 400 },
1783
+ bodySmall: { fontSize: "0.75rem", lineHeight: "1.25rem", fontWeight: 400 },
1784
+ label: { fontSize: "0.75rem", lineHeight: "1rem", fontWeight: 500 },
1785
+ heading: { fontSize: "1.25rem", lineHeight: "1.75rem", fontWeight: 600 },
1786
+ code: { fontSize: "0.8125rem", lineHeight: "1.25rem", fontWeight: 400 },
1787
+ caption: { fontSize: "0.6875rem", lineHeight: "1rem", fontWeight: 400 }
1788
+ }
1789
+ };
1790
+ var TokensContext = createContext2(defaultTokens);
1791
+ function TokensProvider({
1792
+ tokens,
1793
+ children
1794
+ }) {
1795
+ return createElement(TokensContext.Provider, { value: tokens }, children);
1796
+ }
1797
+ function useTokens() {
1798
+ return useContext2(TokensContext);
1799
+ }
1800
+
1801
+ // src/containers/ThreadContainer.tsx
1802
+ import { ThreadPrimitive, useAuiState as useAuiState8 } from "@assistant-ui/react";
1803
+
1804
+ // src/hooks/useComposerBusyState.ts
1805
+ import { useThreadIsRunning } from "@assistant-ui/core/react";
1806
+ import { createContext as createContext3, createElement as createElement2, useCallback, useContext as useContext3, useEffect, useState as useState4 } from "react";
1807
+ var ComposerBusyContext = createContext3(null);
1808
+ function useComposerBusyStateValue() {
1809
+ const isRunning = useThreadIsRunning();
1810
+ const [isSubmitting, setIsSubmitting] = useState4(false);
1811
+ useEffect(() => {
1812
+ if (!isRunning) {
1813
+ setIsSubmitting(false);
1814
+ }
1815
+ }, [isRunning]);
1816
+ const isBusy = isRunning || isSubmitting;
1817
+ const resetBusy = useCallback(() => {
1818
+ setIsSubmitting(false);
1819
+ }, []);
1820
+ const send = useCallback((sendFn) => {
1821
+ setIsSubmitting(true);
1822
+ try {
1823
+ const result = sendFn();
1824
+ if (result != null && typeof result.then === "function") {
1825
+ void result.catch(() => {
1826
+ setIsSubmitting(false);
1827
+ });
1828
+ }
1829
+ } catch {
1830
+ setIsSubmitting(false);
1831
+ }
1832
+ }, []);
1833
+ return { isBusy, isRunning, isSubmitting, send, resetBusy };
1834
+ }
1835
+ function ComposerBusyProvider({ children }) {
1836
+ const value = useComposerBusyStateValue();
1837
+ return createElement2(ComposerBusyContext.Provider, { value }, children);
1838
+ }
1839
+ function useComposerBusyState() {
1840
+ const value = useContext3(ComposerBusyContext);
1841
+ if (value == null) {
1842
+ throw new Error("useComposerBusyState must be used within ComposerBusyProvider");
1843
+ }
1844
+ return value;
1845
+ }
1846
+
1847
+ // src/containers/AssistantMessageContainer.tsx
1848
+ import { groupPartByType, MessagePrimitive as MessagePrimitive2, useAui as useAui2 } from "@assistant-ui/react";
1849
+ import {
1850
+ useActionBarCopy as useActionBarCopy2,
1851
+ useMessageBranching,
1852
+ useMessageError,
1853
+ useThreadIsRunning as useThreadIsRunning2
1854
+ } from "@assistant-ui/core/react";
1855
+
1856
+ // src/containers/AssistantTextContainer.tsx
1857
+ import { useCallback as useCallback3 } from "react";
1858
+ import { useAuiState as useAuiState2 } from "@assistant-ui/react";
1859
+ import { useTrueFoundryDownloadSandboxFile } from "@truefoundry/assistant-ui-runtime";
1860
+
1861
+ // src/containers/ErrorToasterContainer.tsx
1862
+ import { createContext as createContext4, useCallback as useCallback2, useContext as useContext4, useMemo as useMemo3, useState as useState5 } from "react";
1863
+ import { TrueFoundryGatewayError } from "truefoundry-gateway-sdk";
1864
+ import { jsx as jsx36, jsxs as jsxs24 } from "react/jsx-runtime";
1865
+ var ErrorToasterContext = createContext4(null);
1866
+ function formatErrorBody(body) {
1867
+ if (body == null) return void 0;
1868
+ if (typeof body === "string") return body;
1869
+ try {
1870
+ return JSON.stringify(body, null, 2);
1871
+ } catch {
1872
+ return String(body);
1873
+ }
1874
+ }
1875
+ function normalizeError(error) {
1876
+ if (error instanceof TrueFoundryGatewayError) {
1877
+ const statusCode = error.statusCode;
1878
+ const title = statusCode != null ? `Request failed (${statusCode})` : "Request failed";
1879
+ const description = formatErrorBody(error.body) ?? (error.message || "The gateway returned an error.");
1880
+ return { title, description };
1881
+ }
1882
+ if (error instanceof Error) {
1883
+ return { title: "Something went wrong", description: error.message || "An unexpected error occurred." };
1884
+ }
1885
+ return { title: "Something went wrong", description: String(error) };
1886
+ }
1887
+ function ErrorToasterProvider({ children }) {
1888
+ const ToastStack2 = useSlot("ToastStack");
1889
+ const Toast2 = useSlot("Toast");
1890
+ const [toast, setToast] = useState5(null);
1891
+ const [open, setOpen] = useState5(false);
1892
+ const showError = useCallback2((error) => {
1893
+ setToast(normalizeError(error));
1894
+ setOpen(true);
1895
+ }, []);
1896
+ const value = useMemo3(() => ({ showError }), [showError]);
1897
+ return /* @__PURE__ */ jsx36(ErrorToasterContext.Provider, { value, children: /* @__PURE__ */ jsxs24(ToastStack2, { children: [
1898
+ children,
1899
+ toast != null && /* @__PURE__ */ jsx36(Toast2, { title: toast.title, description: toast.description, open, onOpenChange: setOpen })
1900
+ ] }) });
1901
+ }
1902
+ function useErrorToaster() {
1903
+ const context = useContext4(ErrorToasterContext);
1904
+ if (context == null) {
1905
+ throw new Error("useErrorToaster must be used within ErrorToasterProvider");
1906
+ }
1907
+ return context;
1908
+ }
1909
+ function useErrorToasterOptional() {
1910
+ return useContext4(ErrorToasterContext);
1911
+ }
1912
+
1913
+ // src/containers/AssistantTextContainer.tsx
1914
+ import { jsx as jsx37 } from "react/jsx-runtime";
1915
+ function filenameFromPath(path) {
1916
+ return path.split("/").pop() || "download";
1917
+ }
1918
+ function triggerBrowserDownload(blob, filename) {
1919
+ if (typeof document === "undefined") return;
1920
+ const url = URL.createObjectURL(blob);
1921
+ const anchor = document.createElement("a");
1922
+ anchor.href = url;
1923
+ anchor.download = filename;
1924
+ anchor.click();
1925
+ URL.revokeObjectURL(url);
1926
+ }
1927
+ function AssistantTextContainer() {
1928
+ const Markdown2 = useSlot("Markdown");
1929
+ const downloadSandboxFile = useTrueFoundryDownloadSandboxFile();
1930
+ const errorToaster = useErrorToasterOptional();
1931
+ const text = useAuiState2(
1932
+ (s) => s.part.type === "text" || s.part.type === "reasoning" ? s.part.text : ""
1933
+ );
1934
+ const isStreaming = useAuiState2((s) => {
1935
+ if (s.message.status?.type !== "running") return false;
1936
+ const lastIndex = s.message.parts.length - 1;
1937
+ if (lastIndex < 0) return false;
1938
+ if (s.part.type !== "text" && s.part.type !== "reasoning") return false;
1939
+ return s.message.parts[lastIndex] === s.part;
1940
+ });
1941
+ const handleDownloadArtifact = useCallback3(
1942
+ (path) => downloadSandboxFile(path).then((blob) => triggerBrowserDownload(blob, filenameFromPath(path))).catch((error) => {
1943
+ if (errorToaster != null) {
1944
+ errorToaster.showError(error);
1945
+ } else {
1946
+ console.error("Failed to download sandbox artifact", error);
1947
+ }
1948
+ }),
1949
+ [downloadSandboxFile, errorToaster]
1950
+ );
1951
+ return /* @__PURE__ */ jsx37(Markdown2, { content: text, isStreaming, onDownloadArtifact: handleDownloadArtifact });
1952
+ }
1953
+
1954
+ // src/containers/MessageImageContainer.tsx
1955
+ import { useAuiState as useAuiState3 } from "@assistant-ui/react";
1956
+ import { jsx as jsx38 } from "react/jsx-runtime";
1957
+ function MessageImageContainer() {
1958
+ const AttachmentPreviewDialog2 = useSlot("AttachmentPreviewDialog");
1959
+ const image = useAuiState3((s) => s.part.type === "image" ? s.part.image : "");
1960
+ const filename = useAuiState3(
1961
+ (s) => s.part.type === "image" ? s.part.filename : void 0
1962
+ );
1963
+ if (!image) {
1964
+ return null;
1965
+ }
1966
+ const card = /* @__PURE__ */ jsx38(
1967
+ AttachmentCard,
1968
+ {
1969
+ name: filename ?? "image",
1970
+ previewSrc: image,
1971
+ isImage: true,
1972
+ size: "preview",
1973
+ previewRem: USER_MESSAGE_ATTACHMENT_PREVIEW_REM
1974
+ }
1975
+ );
1976
+ return /* @__PURE__ */ jsx38(AttachmentPreviewDialog2, { previewSrc: image, children: /* @__PURE__ */ jsx38("div", { className: "aui-message-image", children: card }) });
1977
+ }
1978
+
1979
+ // src/containers/ReasoningContainer.tsx
1980
+ import { useState as useState6 } from "react";
1981
+ import { useAuiState as useAuiState4 } from "@assistant-ui/react";
1982
+ import { jsx as jsx39 } from "react/jsx-runtime";
1983
+ function ReasoningContainer({ group, children }) {
1984
+ const ReasoningCard2 = useSlot("ReasoningCard");
1985
+ const streaming = useAuiState4((s) => {
1986
+ if (s.message.status?.type !== "running") return false;
1987
+ const lastIndex = s.message.parts.length - 1;
1988
+ if (lastIndex < 0) return false;
1989
+ if (s.message.parts[lastIndex]?.type !== "reasoning") return false;
1990
+ return lastIndex >= group.indices[0] && lastIndex <= group.indices[group.indices.length - 1];
1991
+ });
1992
+ const [expanded, setExpanded] = useState6(streaming);
1993
+ const [prevStreaming, setPrevStreaming] = useState6(streaming);
1994
+ if (streaming !== prevStreaming) {
1995
+ setPrevStreaming(streaming);
1996
+ if (streaming) setExpanded(true);
1997
+ }
1998
+ return /* @__PURE__ */ jsx39(ReasoningCard2, { streaming, expanded, onToggle: () => setExpanded((prev) => !prev), children });
1999
+ }
2000
+
2001
+ // src/containers/ToolCallContainer.tsx
2002
+ import { useState as useState7 } from "react";
2003
+ import {
2004
+ MessagePrimitive,
2005
+ useToolCallElapsed
2006
+ } from "@assistant-ui/react";
2007
+ import { useTrueFoundryRespondToToolApproval } from "@truefoundry/assistant-ui-runtime";
2008
+
2009
+ // src/containers/nestedApprovalBridge.ts
2010
+ import { createContext as createContext5, useContext as useContext5 } from "react";
2011
+ var NestedApprovalBridgeContext = createContext5(
2012
+ null
2013
+ );
2014
+ function useNestedApprovalBridge() {
2015
+ return useContext5(NestedApprovalBridgeContext);
2016
+ }
2017
+
2018
+ // src/containers/ToolCallContainer.tsx
2019
+ import { jsx as jsx40 } from "react/jsx-runtime";
2020
+ var SUB_AGENT_TOOL_NAME = "create_sub_agent";
2021
+ var APPROVAL_OPTION_DEFAULT_LABELS = {
2022
+ "allow-once": "Allow",
2023
+ "allow-always": "Always allow",
2024
+ "reject-once": "Deny",
2025
+ "reject-always": "Always deny"
2026
+ };
2027
+ var isAllowKind = (kind) => kind === "allow-once" || kind === "allow-always";
2028
+ function hasPendingToolApproval(approval) {
2029
+ return approval != null && approval.approved === void 0 && approval.resolution === void 0;
2030
+ }
2031
+ function buildApprovalOptions(options) {
2032
+ const declared = options?.filter((o) => Object.hasOwn(APPROVAL_OPTION_DEFAULT_LABELS, o.kind));
2033
+ if (declared && declared.length > 0) {
2034
+ const allow = declared.filter((o) => isAllowKind(o.kind));
2035
+ const reject = declared.filter((o) => !isAllowKind(o.kind));
2036
+ const mapped = [...allow, ...reject].map((o) => ({
2037
+ id: o.id,
2038
+ label: o.label ?? APPROVAL_OPTION_DEFAULT_LABELS[o.kind] ?? o.id,
2039
+ isAllow: isAllowKind(o.kind),
2040
+ grants: o.grants,
2041
+ confirm: o.confirm != null ? typeof o.confirm === "object" ? o.confirm : {} : void 0
2042
+ }));
2043
+ if (reject.length === 0) {
2044
+ mapped.push({ id: "__deny", label: "Deny", isAllow: false });
2045
+ }
2046
+ return mapped;
2047
+ }
2048
+ return [
2049
+ { id: "__allow", label: "Allow", isAllow: true },
2050
+ { id: "__deny", label: "Deny", isAllow: false }
2051
+ ];
2052
+ }
2053
+ function formatDuration(ms) {
2054
+ if (ms < 1e3) return "<1s";
2055
+ const seconds = ms / 1e3;
2056
+ if (seconds < 10) return `${(Math.floor(seconds * 10) / 10).toFixed(1)}s`;
2057
+ if (seconds < 60) return `${Math.floor(seconds)}s`;
2058
+ return `${Math.floor(seconds / 60)}m ${Math.floor(seconds % 60)}s`;
2059
+ }
2060
+ function toStatus(statusType) {
2061
+ if (statusType === "complete") return "success";
2062
+ if (statusType === "incomplete") return "error";
2063
+ return "running";
2064
+ }
2065
+ function ToolApprovalSlot({ part }) {
2066
+ const ToolApprovalBar2 = useSlot("ToolApprovalBar");
2067
+ const nestedBridge = useNestedApprovalBridge();
2068
+ const respond = (response) => {
2069
+ if (nestedBridge) {
2070
+ nestedBridge(response);
2071
+ return;
2072
+ }
2073
+ part.respondToApproval(response);
2074
+ };
2075
+ const onSelectOption = (optionId) => {
2076
+ if (optionId === "__allow") return respond({ approved: true });
2077
+ if (optionId === "__deny") return respond({ approved: false });
2078
+ return respond({ optionId });
2079
+ };
2080
+ return /* @__PURE__ */ jsx40(ToolApprovalBar2, { options: buildApprovalOptions(part.approval?.options), onSelectOption });
2081
+ }
2082
+ var ToolCallContainer = (part) => {
2083
+ const ToolCallCard2 = useSlot("ToolCallCard");
2084
+ const respondToNestedApproval = useTrueFoundryRespondToToolApproval();
2085
+ const elapsedMs = useToolCallElapsed();
2086
+ const isRequiresAction = part.status?.type === "requires-action";
2087
+ const [expanded, setExpanded] = useState7(isRequiresAction);
2088
+ const [prevRequiresAction, setPrevRequiresAction] = useState7(isRequiresAction);
2089
+ if (isRequiresAction !== prevRequiresAction) {
2090
+ setPrevRequiresAction(isRequiresAction);
2091
+ if (isRequiresAction) setExpanded(true);
2092
+ }
2093
+ const showApproval = isRequiresAction && hasPendingToolApproval(part.approval);
2094
+ const isSubAgent = part.toolName === SUB_AGENT_TOOL_NAME;
2095
+ const durationText = elapsedMs === void 0 ? void 0 : formatDuration(elapsedMs);
2096
+ const status = toStatus(part.status?.type);
2097
+ if (isSubAgent) {
2098
+ const firstNested = part.messages?.[0];
2099
+ const subAgent = firstNested?.metadata?.custom?.subAgent;
2100
+ const agentName = subAgent?.title ?? subAgent?.name ?? part.toolName;
2101
+ const instruction = subAgent?.input ?? "";
2102
+ const stepCount = part.messages?.length ?? 0;
2103
+ const bridge = (response) => {
2104
+ if (part.approval == null) return;
2105
+ const approved = "approved" in response ? response.approved : void 0;
2106
+ if (approved === void 0) return;
2107
+ respondToNestedApproval({ approvalId: part.approval.id, approved });
2108
+ };
2109
+ return /* @__PURE__ */ jsx40(
2110
+ ToolCallCard2,
2111
+ {
2112
+ variant: "sub-agent",
2113
+ name: part.toolName,
2114
+ status,
2115
+ expanded,
2116
+ onToggle: () => setExpanded((prev) => !prev),
2117
+ durationText,
2118
+ agentName,
2119
+ instruction,
2120
+ stepCount,
2121
+ children: /* @__PURE__ */ jsx40(NestedApprovalBridgeContext.Provider, { value: bridge, children: /* @__PURE__ */ jsx40(MessagePrimitive.Root, { "data-role": "assistant", children: /* @__PURE__ */ jsx40(MessagePrimitive.Parts, { components: { tools: { Fallback: ToolCallContainer } } }) }) })
2122
+ }
2123
+ );
2124
+ }
2125
+ return /* @__PURE__ */ jsx40(
2126
+ ToolCallCard2,
2127
+ {
2128
+ variant: "tool",
2129
+ name: part.toolName,
2130
+ status,
2131
+ expanded,
2132
+ onToggle: () => setExpanded((prev) => !prev),
2133
+ durationText,
2134
+ argsText: part.argsText,
2135
+ result: part.result === void 0 ? void 0 : typeof part.result === "string" ? part.result : JSON.stringify(part.result, null, 2),
2136
+ isError: part.isError,
2137
+ approvalSlot: showApproval ? /* @__PURE__ */ jsx40(ToolApprovalSlot, { part }) : void 0
2138
+ }
2139
+ );
2140
+ };
2141
+
2142
+ // src/containers/ToolGroupContainer.tsx
2143
+ import { useState as useState8 } from "react";
2144
+ import { jsx as jsx41 } from "react/jsx-runtime";
2145
+ function ToolGroupContainer({ group, children }) {
2146
+ const ToolGroupCard2 = useSlot("ToolGroupCard");
2147
+ const [expanded, setExpanded] = useState8(true);
2148
+ return /* @__PURE__ */ jsx41(
2149
+ ToolGroupCard2,
2150
+ {
2151
+ toolCallCount: group.indices.length,
2152
+ active: group.status.type === "running",
2153
+ expanded,
2154
+ onToggle: () => setExpanded((prev) => !prev),
2155
+ children
2156
+ }
2157
+ );
2158
+ }
2159
+
2160
+ // src/containers/AssistantMessageContainer.tsx
2161
+ import { jsx as jsx42 } from "react/jsx-runtime";
2162
+ function AssistantLeafPartContainer({ part }) {
2163
+ switch (part.type) {
2164
+ case "text":
2165
+ case "reasoning":
2166
+ return /* @__PURE__ */ jsx42(AssistantTextContainer, {});
2167
+ case "image":
2168
+ return /* @__PURE__ */ jsx42(MessageImageContainer, {});
2169
+ case "tool-call":
2170
+ return /* @__PURE__ */ jsx42(ToolCallContainer, { ...part });
2171
+ default:
2172
+ return null;
2173
+ }
2174
+ }
2175
+ function downloadMarkdown(text) {
2176
+ if (typeof document === "undefined") return;
2177
+ const blob = new Blob([text], { type: "text/markdown" });
2178
+ const url = URL.createObjectURL(blob);
2179
+ const anchor = document.createElement("a");
2180
+ anchor.href = url;
2181
+ anchor.download = "message.md";
2182
+ anchor.click();
2183
+ URL.revokeObjectURL(url);
2184
+ }
2185
+ function AssistantMessageContainer() {
2186
+ const MessageBubble2 = useSlot("MessageBubble");
2187
+ const MessageIndicator2 = useSlot("MessageIndicator");
2188
+ const MessageErrorBanner2 = useSlot("MessageErrorBanner");
2189
+ const MessageActionBar2 = useSlot("MessageActionBar");
2190
+ const BranchIndicator2 = useSlot("BranchIndicator");
2191
+ const aui = useAui2();
2192
+ const isRunning = useThreadIsRunning2();
2193
+ const error = useMessageError();
2194
+ const { copy, isCopied } = useActionBarCopy2({
2195
+ copyToClipboard: (text) => navigator.clipboard.writeText(text)
2196
+ });
2197
+ const { branchNumber, branchCount, goToPrev, goToNext } = useMessageBranching();
2198
+ return /* @__PURE__ */ jsx42(MessagePrimitive2.Root, { "data-role": "assistant", children: /* @__PURE__ */ jsx42(
2199
+ MessageBubble2,
2200
+ {
2201
+ variant: "assistant",
2202
+ error: error !== void 0 ? /* @__PURE__ */ jsx42(MessageErrorBanner2, { message: String(error) }) : void 0,
2203
+ branchIndicator: /* @__PURE__ */ jsx42(
2204
+ BranchIndicator2,
2205
+ {
2206
+ index: branchNumber,
2207
+ count: branchCount,
2208
+ onPrevious: goToPrev,
2209
+ onNext: goToNext
2210
+ }
2211
+ ),
2212
+ actionBar: !isRunning ? /* @__PURE__ */ jsx42(
2213
+ MessageActionBar2,
2214
+ {
2215
+ isCopied,
2216
+ onCopy: copy,
2217
+ onExportMarkdown: () => downloadMarkdown(aui.message().getCopyText())
2218
+ }
2219
+ ) : void 0,
2220
+ children: /* @__PURE__ */ jsx42(
2221
+ MessagePrimitive2.GroupedParts,
2222
+ {
2223
+ groupBy: groupPartByType({
2224
+ reasoning: ["group-chainOfThought", "group-reasoning"],
2225
+ "tool-call": ["group-chainOfThought", "group-tool"],
2226
+ "standalone-tool-call": []
2227
+ }),
2228
+ children: ({ part, children }) => {
2229
+ switch (part.type) {
2230
+ case "group-chainOfThought":
2231
+ return /* @__PURE__ */ jsx42("div", { "data-slot": "aui_chain-of-thought", className: "flex flex-col gap-3", children });
2232
+ case "group-tool":
2233
+ return /* @__PURE__ */ jsx42(ToolGroupContainer, { group: part, children });
2234
+ case "group-reasoning":
2235
+ return /* @__PURE__ */ jsx42(ReasoningContainer, { group: part, children });
2236
+ case "text":
2237
+ case "reasoning":
2238
+ case "tool-call":
2239
+ case "image":
2240
+ case "data":
2241
+ return /* @__PURE__ */ jsx42(AssistantLeafPartContainer, { part });
2242
+ case "indicator":
2243
+ return /* @__PURE__ */ jsx42(MessageIndicator2, {});
2244
+ default:
2245
+ return null;
2246
+ }
2247
+ }
2248
+ }
2249
+ )
2250
+ }
2251
+ ) });
2252
+ }
2253
+
2254
+ // src/containers/UserEditComposerContainer.tsx
2255
+ import {
2256
+ ComposerPrimitive as ComposerPrimitive2,
2257
+ MessagePrimitive as MessagePrimitive4,
2258
+ useAuiState as useAuiState7
2259
+ } from "@assistant-ui/react";
2260
+ import { useThreadIsRunning as useThreadIsRunning3 } from "@assistant-ui/core/react";
2261
+
2262
+ // src/containers/AttachmentsContainer.tsx
2263
+ import { ComposerPrimitive, MessagePrimitive as MessagePrimitive3, useAui as useAui3, useAuiState as useAuiState6 } from "@assistant-ui/react";
2264
+
2265
+ // src/containers/useAttachmentPreviewSrc.ts
2266
+ import { useEffect as useEffect2, useState as useState9 } from "react";
2267
+ import { useAuiState as useAuiState5 } from "@assistant-ui/react";
2268
+ function isImageAttachment(type, contentType) {
2269
+ return type === "image" || (contentType?.startsWith("image/") ?? false);
2270
+ }
2271
+ function useFileObjectUrl(file) {
2272
+ const [src, setSrc] = useState9(void 0);
2273
+ useEffect2(() => {
2274
+ if (!file) {
2275
+ setSrc(void 0);
2276
+ return;
2277
+ }
2278
+ const objectUrl = URL.createObjectURL(file);
2279
+ setSrc(objectUrl);
2280
+ return () => URL.revokeObjectURL(objectUrl);
2281
+ }, [file]);
2282
+ return src;
2283
+ }
2284
+ function useAttachmentPreviewSrc() {
2285
+ const type = useAuiState5((s) => s.attachment.type);
2286
+ const contentType = useAuiState5((s) => s.attachment.contentType);
2287
+ const file = useAuiState5((s) => "file" in s.attachment ? s.attachment.file : void 0);
2288
+ const contentSrc = useAuiState5((s) => {
2289
+ if (!isImageAttachment(s.attachment.type, s.attachment.contentType)) return void 0;
2290
+ const imagePart = s.attachment.content?.find((part) => part.type === "image");
2291
+ if (imagePart?.type === "image" && imagePart.image) return imagePart.image;
2292
+ return void 0;
2293
+ });
2294
+ const image = isImageAttachment(type, contentType);
2295
+ const fileSrc = useFileObjectUrl(image ? file : void 0);
2296
+ if (!image) return void 0;
2297
+ return fileSrc ?? contentSrc;
2298
+ }
2299
+
2300
+ // src/containers/AttachmentsContainer.tsx
2301
+ import { jsx as jsx43 } from "react/jsx-runtime";
2302
+ function ComposerAttachmentItem() {
2303
+ const AttachmentCard2 = useSlot("AttachmentCard");
2304
+ const AttachmentPreviewDialog2 = useSlot("AttachmentPreviewDialog");
2305
+ const aui = useAui3();
2306
+ const name = useAuiState6((s) => s.attachment.name);
2307
+ const contentType = useAuiState6((s) => s.attachment.contentType);
2308
+ const type = useAuiState6((s) => s.attachment.type);
2309
+ const isImage = isImageAttachment(type, contentType);
2310
+ const previewSrc = useAttachmentPreviewSrc();
2311
+ return /* @__PURE__ */ jsx43(AttachmentPreviewDialog2, { previewSrc, children: /* @__PURE__ */ jsx43(
2312
+ AttachmentCard2,
2313
+ {
2314
+ name,
2315
+ contentType,
2316
+ previewSrc,
2317
+ isImage,
2318
+ size: "chip",
2319
+ onRemove: () => void aui.attachment().remove()
2320
+ }
2321
+ ) });
2322
+ }
2323
+ function MessageAttachmentItem() {
2324
+ const AttachmentCard2 = useSlot("AttachmentCard");
2325
+ const AttachmentPreviewDialog2 = useSlot("AttachmentPreviewDialog");
2326
+ const name = useAuiState6((s) => s.attachment.name);
2327
+ const contentType = useAuiState6((s) => s.attachment.contentType);
2328
+ const type = useAuiState6((s) => s.attachment.type);
2329
+ const isImage = isImageAttachment(type, contentType);
2330
+ const previewSrc = useAttachmentPreviewSrc();
2331
+ const card = /* @__PURE__ */ jsx43(
2332
+ AttachmentCard2,
2333
+ {
2334
+ name,
2335
+ contentType,
2336
+ previewSrc,
2337
+ isImage,
2338
+ size: isImage ? "preview" : "chip",
2339
+ previewRem: USER_MESSAGE_ATTACHMENT_PREVIEW_REM
2340
+ }
2341
+ );
2342
+ if (isImage && previewSrc) {
2343
+ return /* @__PURE__ */ jsx43(AttachmentPreviewDialog2, { previewSrc, children: card });
2344
+ }
2345
+ return card;
2346
+ }
2347
+ function ComposerAttachmentsContainer() {
2348
+ return /* @__PURE__ */ jsx43("div", { className: "aui-composer-attachments flex w-full flex-row flex-wrap items-center gap-2 empty:hidden", children: /* @__PURE__ */ jsx43(ComposerPrimitive.Attachments, { children: () => /* @__PURE__ */ jsx43(ComposerAttachmentItem, {}) }) });
2349
+ }
2350
+ function MessageAttachmentsContainer() {
2351
+ return /* @__PURE__ */ jsx43("div", { className: "aui-user-message-attachments-end col-span-full col-start-1 row-start-1 flex w-full flex-row justify-end gap-2", children: /* @__PURE__ */ jsx43(MessagePrimitive3.Attachments, { children: () => /* @__PURE__ */ jsx43(MessageAttachmentItem, {}) }) });
2352
+ }
2353
+ function ComposerAttachmentPickerContainer() {
2354
+ const AttachmentPickerButton2 = useSlot("AttachmentPickerButton");
2355
+ return /* @__PURE__ */ jsx43(ComposerPrimitive.AddAttachment, { asChild: true, children: /* @__PURE__ */ jsx43(AttachmentPickerButton2, {}) });
2356
+ }
2357
+
2358
+ // src/containers/UserEditComposerContainer.tsx
2359
+ import { jsx as jsx44, jsxs as jsxs25 } from "react/jsx-runtime";
2360
+ function ReadOnlyMessageAttachments() {
2361
+ const hasAttachments = useAuiState7(
2362
+ (s) => (s.message.attachments?.length ?? 0) > 0
2363
+ );
2364
+ if (!hasAttachments) {
2365
+ return null;
2366
+ }
2367
+ return /* @__PURE__ */ jsx44("div", { className: "pointer-events-none opacity-90", children: /* @__PURE__ */ jsx44(MessageAttachmentsContainer, {}) });
2368
+ }
2369
+ function UserEditComposerContainer() {
2370
+ const Button2 = useSlot("Button");
2371
+ const isRunning = useThreadIsRunning3();
2372
+ return /* @__PURE__ */ jsx44(MessagePrimitive4.Root, { "data-role": "user", children: /* @__PURE__ */ jsxs25(
2373
+ "div",
2374
+ {
2375
+ "data-slot": "aui_user-edit-composer-root",
2376
+ className: "fade-in slide-in-from-bottom-1 animate-in grid auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] content-start gap-y-2 px-2 duration-150 [&:where(>*)]:col-start-2",
2377
+ children: [
2378
+ /* @__PURE__ */ jsx44(ReadOnlyMessageAttachments, {}),
2379
+ /* @__PURE__ */ jsx44(ComposerPrimitive2.Root, { asChild: true, children: /* @__PURE__ */ jsx44("div", { className: "aui-user-edit-composer col-start-2 min-w-0", children: /* @__PURE__ */ jsxs25("div", { className: "ring-primary/60 bg-muted rounded-xl px-4 py-3 ring-2", children: [
2380
+ /* @__PURE__ */ jsx44(
2381
+ ComposerPrimitive2.Input,
2382
+ {
2383
+ disabled: isRunning,
2384
+ className: "placeholder:text-muted-foreground/80 max-h-32 min-h-10 w-full resize-none bg-transparent text-base outline-none"
2385
+ }
2386
+ ),
2387
+ /* @__PURE__ */ jsxs25("div", { className: "mt-3 flex items-end justify-between gap-3", children: [
2388
+ /* @__PURE__ */ jsxs25("p", { className: "text-muted-foreground text-xs", children: [
2389
+ /* @__PURE__ */ jsx44("kbd", { className: "bg-background/60 rounded border px-1 py-0.5 font-sans text-[10px]", children: "Enter" }),
2390
+ " ",
2391
+ "to save",
2392
+ " ",
2393
+ /* @__PURE__ */ jsx44("kbd", { className: "bg-background/60 rounded border px-1 py-0.5 font-sans text-[10px]", children: "Esc" }),
2394
+ " ",
2395
+ "to cancel"
2396
+ ] }),
2397
+ /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
2398
+ /* @__PURE__ */ jsx44(ComposerPrimitive2.Cancel, { asChild: true, children: /* @__PURE__ */ jsx44(
2399
+ Button2,
2400
+ {
2401
+ type: "button",
2402
+ variant: "ghost",
2403
+ size: "sm",
2404
+ className: "text-muted-foreground h-8 px-2",
2405
+ children: "Cancel"
2406
+ }
2407
+ ) }),
2408
+ /* @__PURE__ */ jsx44(ComposerPrimitive2.Send, { asChild: true, children: /* @__PURE__ */ jsx44(
2409
+ Button2,
2410
+ {
2411
+ type: "button",
2412
+ size: "sm",
2413
+ className: cn("h-8 rounded-lg px-3"),
2414
+ disabled: isRunning,
2415
+ children: "Send"
2416
+ }
2417
+ ) })
2418
+ ] })
2419
+ ] })
2420
+ ] }) }) })
2421
+ ]
2422
+ }
2423
+ ) });
2424
+ }
2425
+
2426
+ // src/containers/UserMessageContainer.tsx
2427
+ import { MessagePrimitive as MessagePrimitive5 } from "@assistant-ui/react";
2428
+ import { useThreadIsRunning as useThreadIsRunning4 } from "@assistant-ui/core/react";
2429
+ import { jsx as jsx45 } from "react/jsx-runtime";
2430
+ function UserMessageContainer() {
2431
+ const MessageBubble2 = useSlot("MessageBubble");
2432
+ const UserMessageActionBar2 = useSlot("UserMessageActionBar");
2433
+ const isRunning = useThreadIsRunning4();
2434
+ return /* @__PURE__ */ jsx45(MessagePrimitive5.Root, { "data-role": "user", children: /* @__PURE__ */ jsx45(
2435
+ MessageBubble2,
2436
+ {
2437
+ variant: "user",
2438
+ attachments: /* @__PURE__ */ jsx45(MessageAttachmentsContainer, {}),
2439
+ actionBar: !isRunning ? /* @__PURE__ */ jsx45(UserMessageActionBar2, {}) : void 0,
2440
+ children: /* @__PURE__ */ jsx45(MessagePrimitive5.Parts, { components: { Text: AssistantTextContainer } })
2441
+ }
2442
+ ) });
2443
+ }
2444
+
2445
+ // src/containers/ThreadContainer.tsx
2446
+ import { jsx as jsx46, jsxs as jsxs26 } from "react/jsx-runtime";
2447
+ var isNewChatView = (s) => s.thread.messages.length === 0 && (!s.thread.isLoading || s.threads.isLoading);
2448
+ function ThreadMessage({ isEditing }) {
2449
+ const role = useAuiState8((s) => s.message.role);
2450
+ if (role === "user") {
2451
+ if (isEditing) {
2452
+ return /* @__PURE__ */ jsx46(UserEditComposerContainer, {});
2453
+ }
2454
+ return /* @__PURE__ */ jsx46(UserMessageContainer, {});
2455
+ }
2456
+ return /* @__PURE__ */ jsx46(AssistantMessageContainer, {});
2457
+ }
2458
+ function ThreadContainer({ composer }) {
2459
+ const ThreadRootShell2 = useSlot("ThreadRootShell");
2460
+ const ThreadViewportShell2 = useSlot("ThreadViewportShell");
2461
+ const ThreadComposerAreaShell2 = useSlot("ThreadComposerAreaShell");
2462
+ const MessageGroup2 = useSlot("MessageGroup");
2463
+ const WelcomeScreen2 = useSlot("WelcomeScreen");
2464
+ const MessageListSkeleton2 = useSlot("MessageListSkeleton");
2465
+ const ScrollToBottomButton2 = useSlot("ScrollToBottomButton");
2466
+ const isEmpty = useAuiState8(isNewChatView);
2467
+ const isLoading = useAuiState8((s) => s.thread.isLoading);
2468
+ return /* @__PURE__ */ jsx46(ComposerBusyProvider, { children: /* @__PURE__ */ jsx46(ThreadPrimitive.Root, { asChild: true, children: /* @__PURE__ */ jsxs26(ThreadRootShell2, { children: [
2469
+ /* @__PURE__ */ jsx46(ThreadPrimitive.Viewport, { asChild: true, turnAnchor: "top", autoScroll: true, children: /* @__PURE__ */ jsxs26(ThreadViewportShell2, { isEmpty, children: [
2470
+ isEmpty && /* @__PURE__ */ jsx46(WelcomeScreen2, {}),
2471
+ isLoading ? /* @__PURE__ */ jsx46(MessageListSkeleton2, {}) : /* @__PURE__ */ jsx46(MessageGroup2, { children: /* @__PURE__ */ jsx46(ThreadPrimitive.Messages, { children: ({ message }) => /* @__PURE__ */ jsx46(
2472
+ ThreadMessage,
2473
+ {
2474
+ isEditing: message.role === "user" && message.composer.isEditing
2475
+ }
2476
+ ) }) })
2477
+ ] }) }),
2478
+ !isLoading && /* @__PURE__ */ jsxs26(ThreadComposerAreaShell2, { isEmpty, children: [
2479
+ /* @__PURE__ */ jsx46(ThreadPrimitive.ScrollToBottom, { asChild: true, children: /* @__PURE__ */ jsx46(ScrollToBottomButton2, {}) }),
2480
+ composer
2481
+ ] })
2482
+ ] }) }) });
2483
+ }
2484
+
2485
+ // src/containers/ComposerContainer.tsx
2486
+ import { useRef } from "react";
2487
+ import { useAui as useAui4, useAuiState as useAuiState9 } from "@assistant-ui/react";
2488
+ import { useTrueFoundryCancel, useTrueFoundryToolResponses as useTrueFoundryToolResponses2 } from "@truefoundry/assistant-ui-runtime";
2489
+
2490
+ // src/containers/AskUserContainer.tsx
2491
+ import { useState as useState10 } from "react";
2492
+ import { useTrueFoundryToolResponses } from "@truefoundry/assistant-ui-runtime";
2493
+ import { useThreadIsRunning as useThreadIsRunning5 } from "@assistant-ui/core/react";
2494
+ import { jsx as jsx47 } from "react/jsx-runtime";
2495
+ var OTHER_OPTION_ID2 = "__other";
2496
+ function AskUserContainer() {
2497
+ const AskUserPrompt2 = useSlot("AskUserPrompt");
2498
+ const { pending, respond } = useTrueFoundryToolResponses();
2499
+ const isRunning = useThreadIsRunning5();
2500
+ const [selectedOptionId, setSelectedOptionId] = useState10(void 0);
2501
+ const [otherValue, setOtherValue] = useState10("");
2502
+ const item = pending[0];
2503
+ if (item == null) return null;
2504
+ const options = (item.options ?? []).map((option) => ({ id: option, label: option }));
2505
+ const allowOther = options.length === 0;
2506
+ const onSubmit = () => {
2507
+ const content = selectedOptionId != null && selectedOptionId !== OTHER_OPTION_ID2 ? options.find((o) => o.id === selectedOptionId)?.label ?? "" : otherValue.trim();
2508
+ if (!content) return;
2509
+ respond({ toolCallId: item.toolCallId, content });
2510
+ setSelectedOptionId(void 0);
2511
+ setOtherValue("");
2512
+ };
2513
+ return /* @__PURE__ */ jsx47(
2514
+ AskUserPrompt2,
2515
+ {
2516
+ question: item.question ?? "Answer required",
2517
+ options,
2518
+ allowOther,
2519
+ selectedOptionId,
2520
+ otherValue,
2521
+ disabled: isRunning,
2522
+ onSelectOption: setSelectedOptionId,
2523
+ onOtherValueChange: setOtherValue,
2524
+ onSubmit
2525
+ }
2526
+ );
2527
+ }
2528
+
2529
+ // src/containers/McpAuthContainer.tsx
2530
+ import { useTrueFoundryMcpAuth } from "@truefoundry/assistant-ui-runtime";
2531
+ import { useThreadIsRunning as useThreadIsRunning6 } from "@assistant-ui/core/react";
2532
+ import { jsx as jsx48 } from "react/jsx-runtime";
2533
+ function McpAuthContainer() {
2534
+ const McpAuthPrompt2 = useSlot("McpAuthPrompt");
2535
+ const { pending, resume } = useTrueFoundryMcpAuth();
2536
+ const isRunning = useThreadIsRunning6();
2537
+ if (!pending) return null;
2538
+ return /* @__PURE__ */ jsx48(
2539
+ McpAuthPrompt2,
2540
+ {
2541
+ servers: pending.mcpServers,
2542
+ disabled: isRunning,
2543
+ onContinue: () => void resume()
2544
+ }
2545
+ );
2546
+ }
2547
+
2548
+ // src/containers/ComposerContainer.tsx
2549
+ import { Fragment as Fragment3, jsx as jsx49, jsxs as jsxs27 } from "react/jsx-runtime";
2550
+ var threadHasPendingMcpAuth = (s) => {
2551
+ const messages = s.thread.messages;
2552
+ const last = messages[messages.length - 1];
2553
+ if (last?.role !== "assistant") return false;
2554
+ if (last.status?.type !== "requires-action") return false;
2555
+ return last.metadata?.custom?.pendingMcpAuth === true;
2556
+ };
2557
+ function ComposerContainer() {
2558
+ const ComposerShell2 = useSlot("ComposerShell");
2559
+ const aui = useAui4();
2560
+ const text = useAuiState9((s) => s.composer.text);
2561
+ const { isBusy, send, resetBusy } = useComposerBusyState();
2562
+ const mcpPending = useAuiState9(threadHasPendingMcpAuth);
2563
+ const { pending: toolResponsesPending } = useTrueFoundryToolResponses2();
2564
+ const cancel = useTrueFoundryCancel();
2565
+ const fileInputRef = useRef(null);
2566
+ if (mcpPending) {
2567
+ return /* @__PURE__ */ jsx49(McpAuthContainer, {});
2568
+ }
2569
+ if (toolResponsesPending.length > 0) {
2570
+ return /* @__PURE__ */ jsx49(AskUserContainer, {});
2571
+ }
2572
+ return /* @__PURE__ */ jsxs27(Fragment3, { children: [
2573
+ /* @__PURE__ */ jsx49(
2574
+ "input",
2575
+ {
2576
+ ref: fileInputRef,
2577
+ type: "file",
2578
+ hidden: true,
2579
+ onChange: (event) => {
2580
+ const file = event.target.files?.[0];
2581
+ if (file) void aui.composer().addAttachment(file);
2582
+ event.target.value = "";
2583
+ }
2584
+ }
2585
+ ),
2586
+ /* @__PURE__ */ jsx49(
2587
+ ComposerShell2,
2588
+ {
2589
+ attachments: /* @__PURE__ */ jsx49(ComposerAttachmentsContainer, {}),
2590
+ value: text,
2591
+ placeholder: "Ask anything... (Shift+Enter for new line)",
2592
+ disabled: isBusy,
2593
+ isRunning: isBusy,
2594
+ onValueChange: (value) => aui.composer().setText(value),
2595
+ onSubmit: () => send(() => aui.composer().send()),
2596
+ onCancel: () => {
2597
+ resetBusy();
2598
+ void cancel();
2599
+ },
2600
+ onAttach: () => fileInputRef.current?.click()
2601
+ }
2602
+ )
2603
+ ] });
2604
+ }
2605
+
2606
+ // src/containers/ThreadListContainer.tsx
2607
+ import { useAui as useAui5, useAuiState as useAuiState10 } from "@assistant-ui/react";
2608
+ import { jsx as jsx50, jsxs as jsxs28 } from "react/jsx-runtime";
2609
+ function ThreadListContainer() {
2610
+ const ThreadListShell2 = useSlot("ThreadListShell");
2611
+ const ThreadListNewButton2 = useSlot("ThreadListNewButton");
2612
+ const ThreadListRow2 = useSlot("ThreadListRow");
2613
+ const ThreadListRowSkeleton2 = useSlot("ThreadListRowSkeleton");
2614
+ const ThreadListEmptyState2 = useSlot("ThreadListEmptyState");
2615
+ const Button2 = useSlot("Button");
2616
+ const aui = useAui5();
2617
+ const isLoading = useAuiState10((s) => s.threads.isLoading);
2618
+ const isLoadingMore = useAuiState10((s) => s.threads.isLoadingMore);
2619
+ const hasMore = useAuiState10((s) => s.threads.hasMore);
2620
+ const threadIds = useAuiState10((s) => s.threads.threadIds);
2621
+ const threadItems = useAuiState10((s) => s.threads.threadItems);
2622
+ const mainThreadId = useAuiState10((s) => s.threads.mainThreadId);
2623
+ const itemsById = new Map(threadItems.map((item) => [item.id, item]));
2624
+ return /* @__PURE__ */ jsxs28(ThreadListShell2, { header: /* @__PURE__ */ jsx50(ThreadListNewButton2, { onClick: () => aui.threads().switchToNewThread() }), children: [
2625
+ isLoading ? /* @__PURE__ */ jsx50(ThreadListRowSkeleton2, {}) : threadIds.length === 0 ? /* @__PURE__ */ jsx50(ThreadListEmptyState2, {}) : threadIds.map((id) => {
2626
+ const item = itemsById.get(id);
2627
+ return /* @__PURE__ */ jsx50(
2628
+ ThreadListRow2,
2629
+ {
2630
+ title: item?.title ?? "New Chat",
2631
+ active: id === mainThreadId,
2632
+ onSelect: () => aui.threads().switchToThread(id),
2633
+ onArchive: () => aui.threads().item({ id }).archive(),
2634
+ onDelete: () => aui.threads().item({ id }).delete()
2635
+ },
2636
+ id
2637
+ );
2638
+ }),
2639
+ !isLoading && hasMore && (isLoadingMore ? /* @__PURE__ */ jsx50(ThreadListRowSkeleton2, { count: 1 }) : /* @__PURE__ */ jsx50(Button2, { variant: "ghost", onClick: () => void aui.threads().loadMore(), children: "Load more" }))
2640
+ ] });
2641
+ }
2642
+
2643
+ // src/containers/Thread.tsx
2644
+ import { jsx as jsx51 } from "react/jsx-runtime";
2645
+ function Thread() {
2646
+ return /* @__PURE__ */ jsx51(ThreadContainer, { composer: /* @__PURE__ */ jsx51(ComposerContainer, {}) });
2647
+ }
2648
+ export {
2649
+ AskUserContainer,
2650
+ AskUserPrompt,
2651
+ AssistantMessageContainer,
2652
+ AssistantTextContainer,
2653
+ AttachmentCard,
2654
+ AttachmentPickerButton,
2655
+ AttachmentPreviewDialog,
2656
+ Avatar,
2657
+ AvatarFallback,
2658
+ AvatarImage,
2659
+ BranchIndicator,
2660
+ Button,
2661
+ CodeBlockHeader,
2662
+ Collapsible,
2663
+ CollapsibleContent,
2664
+ CollapsibleTrigger,
2665
+ ComposerAttachmentPickerContainer,
2666
+ ComposerAttachmentsContainer,
2667
+ ComposerBusyProvider,
2668
+ ComposerContainer,
2669
+ ComposerShell,
2670
+ Dialog,
2671
+ DialogClose,
2672
+ DialogContent,
2673
+ DialogDescription,
2674
+ DialogFooter,
2675
+ DialogHeader,
2676
+ DialogOverlay,
2677
+ DialogPortal,
2678
+ DialogTitle,
2679
+ DialogTrigger,
2680
+ ErrorToasterProvider,
2681
+ IconButton,
2682
+ Markdown,
2683
+ McpAuthContainer,
2684
+ McpAuthPrompt,
2685
+ MessageActionBar,
2686
+ MessageAttachmentsContainer,
2687
+ MessageBubble,
2688
+ MessageErrorBanner,
2689
+ MessageGroup,
2690
+ MessageIndicator,
2691
+ MessageListSkeleton,
2692
+ OpenUIBlock,
2693
+ ReasoningCard,
2694
+ ReasoningContainer,
2695
+ SandboxArtifactList,
2696
+ ScrollToBottomButton,
2697
+ Skeleton,
2698
+ SlotsProvider,
2699
+ Thread,
2700
+ ThreadComposerAreaShell,
2701
+ ThreadContainer,
2702
+ ThreadListContainer,
2703
+ ThreadListEmptyState,
2704
+ ThreadListNewButton,
2705
+ ThreadListRow,
2706
+ ThreadListRowSkeleton,
2707
+ ThreadListShell,
2708
+ ThreadRootShell,
2709
+ ThreadViewportShell,
2710
+ Toast,
2711
+ ToastStack,
2712
+ TokensProvider,
2713
+ ToolApprovalBar,
2714
+ ToolCallCard,
2715
+ ToolCallContainer,
2716
+ ToolGroupCard,
2717
+ ToolGroupContainer,
2718
+ Tooltip,
2719
+ TooltipContent,
2720
+ TooltipProvider,
2721
+ TooltipTrigger,
2722
+ USER_MESSAGE_ATTACHMENT_PREVIEW_REM,
2723
+ UserEditComposerContainer,
2724
+ UserMessageActionBar,
2725
+ UserMessageContainer,
2726
+ WelcomeScreen,
2727
+ buttonVariants,
2728
+ defaultSlots,
2729
+ defaultTokens,
2730
+ useComposerBusyState,
2731
+ useErrorToaster,
2732
+ useErrorToasterOptional,
2733
+ useSlot,
2734
+ useTokens
2735
+ };
2736
+ //# sourceMappingURL=index.js.map