@cloudbase/agent-react-ui 1.0.1-alpha.32

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 (109) hide show
  1. package/README.md +123 -0
  2. package/components.json +21 -0
  3. package/dist/index.css +4241 -0
  4. package/dist/index.css.map +1 -0
  5. package/dist/index.d.mts +59 -0
  6. package/dist/index.d.ts +59 -0
  7. package/dist/index.js +2169 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/index.mjs +2182 -0
  10. package/dist/index.mjs.map +1 -0
  11. package/example/.env.sample +2 -0
  12. package/example/App.tsx +368 -0
  13. package/example/app.css +1 -0
  14. package/example/index.html +12 -0
  15. package/example/main.tsx +9 -0
  16. package/example/vite.config.ts +34 -0
  17. package/package.json +75 -0
  18. package/postcss.config.cjs +3 -0
  19. package/src/components/ai-elements/agent.tsx +140 -0
  20. package/src/components/ai-elements/artifact.tsx +147 -0
  21. package/src/components/ai-elements/attachments.tsx +421 -0
  22. package/src/components/ai-elements/audio-player.tsx +228 -0
  23. package/src/components/ai-elements/canvas.tsx +22 -0
  24. package/src/components/ai-elements/chain-of-thought.tsx +228 -0
  25. package/src/components/ai-elements/checkpoint.tsx +71 -0
  26. package/src/components/ai-elements/code-block.tsx +532 -0
  27. package/src/components/ai-elements/commit.tsx +448 -0
  28. package/src/components/ai-elements/confirmation.tsx +176 -0
  29. package/src/components/ai-elements/connection.tsx +28 -0
  30. package/src/components/ai-elements/context.tsx +408 -0
  31. package/src/components/ai-elements/controls.tsx +18 -0
  32. package/src/components/ai-elements/conversation.tsx +100 -0
  33. package/src/components/ai-elements/edge.tsx +140 -0
  34. package/src/components/ai-elements/environment-variables.tsx +295 -0
  35. package/src/components/ai-elements/file-tree.tsx +258 -0
  36. package/src/components/ai-elements/image.tsx +24 -0
  37. package/src/components/ai-elements/inline-citation.tsx +287 -0
  38. package/src/components/ai-elements/message.tsx +336 -0
  39. package/src/components/ai-elements/mic-selector.tsx +370 -0
  40. package/src/components/ai-elements/model-selector.tsx +211 -0
  41. package/src/components/ai-elements/node.tsx +71 -0
  42. package/src/components/ai-elements/open-in-chat.tsx +365 -0
  43. package/src/components/ai-elements/package-info.tsx +233 -0
  44. package/src/components/ai-elements/panel.tsx +15 -0
  45. package/src/components/ai-elements/persona.tsx +270 -0
  46. package/src/components/ai-elements/plan.tsx +142 -0
  47. package/src/components/ai-elements/prompt-input.tsx +1263 -0
  48. package/src/components/ai-elements/queue.tsx +274 -0
  49. package/src/components/ai-elements/reasoning.tsx +193 -0
  50. package/src/components/ai-elements/sandbox.tsx +126 -0
  51. package/src/components/ai-elements/schema-display.tsx +458 -0
  52. package/src/components/ai-elements/shimmer.tsx +64 -0
  53. package/src/components/ai-elements/snippet.tsx +139 -0
  54. package/src/components/ai-elements/sources.tsx +77 -0
  55. package/src/components/ai-elements/speech-input.tsx +301 -0
  56. package/src/components/ai-elements/stack-trace.tsx +482 -0
  57. package/src/components/ai-elements/suggestion.tsx +53 -0
  58. package/src/components/ai-elements/task.tsx +87 -0
  59. package/src/components/ai-elements/terminal.tsx +261 -0
  60. package/src/components/ai-elements/test-results.tsx +485 -0
  61. package/src/components/ai-elements/tool.tsx +174 -0
  62. package/src/components/ai-elements/toolbar.tsx +16 -0
  63. package/src/components/ai-elements/transcription.tsx +124 -0
  64. package/src/components/ai-elements/voice-selector.tsx +479 -0
  65. package/src/components/ai-elements/web-preview.tsx +263 -0
  66. package/src/components/chat/Chat.tsx +178 -0
  67. package/src/components/chat/Input.tsx +98 -0
  68. package/src/components/chat/Message.tsx +276 -0
  69. package/src/components/chat/index.ts +2 -0
  70. package/src/components/index.ts +1 -0
  71. package/src/components/ui/accordion.tsx +64 -0
  72. package/src/components/ui/alert.tsx +66 -0
  73. package/src/components/ui/avatar.tsx +107 -0
  74. package/src/components/ui/badge.tsx +48 -0
  75. package/src/components/ui/button-group.tsx +83 -0
  76. package/src/components/ui/button.tsx +64 -0
  77. package/src/components/ui/card.tsx +92 -0
  78. package/src/components/ui/carousel.tsx +239 -0
  79. package/src/components/ui/collapsible.tsx +31 -0
  80. package/src/components/ui/command.tsx +184 -0
  81. package/src/components/ui/dialog.tsx +158 -0
  82. package/src/components/ui/dropdown-menu.tsx +257 -0
  83. package/src/components/ui/hover-card.tsx +42 -0
  84. package/src/components/ui/input-group.tsx +168 -0
  85. package/src/components/ui/input.tsx +21 -0
  86. package/src/components/ui/popover.tsx +87 -0
  87. package/src/components/ui/progress.tsx +31 -0
  88. package/src/components/ui/scroll-area.tsx +56 -0
  89. package/src/components/ui/select.tsx +190 -0
  90. package/src/components/ui/separator.tsx +28 -0
  91. package/src/components/ui/spinner.tsx +16 -0
  92. package/src/components/ui/switch.tsx +33 -0
  93. package/src/components/ui/tabs.tsx +91 -0
  94. package/src/components/ui/textarea.tsx +18 -0
  95. package/src/components/ui/tooltip.tsx +61 -0
  96. package/src/css/global.css +123 -0
  97. package/src/css/index.css +1 -0
  98. package/src/hooks/index.ts +1 -0
  99. package/src/hooks/use-copy-to-clipboard.ts +31 -0
  100. package/src/index.ts +4 -0
  101. package/src/lib/utils.ts +6 -0
  102. package/src/locales/context.ts +8 -0
  103. package/src/locales/hooks.ts +20 -0
  104. package/src/locales/index.ts +3 -0
  105. package/src/locales/langs/en.ts +17 -0
  106. package/src/locales/langs/index.ts +12 -0
  107. package/src/locales/langs/zh-cn.ts +18 -0
  108. package/tsconfig.json +21 -0
  109. package/tsup.config.ts +21 -0
package/dist/index.js ADDED
@@ -0,0 +1,2169 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AgKitUI: () => AgKitUI
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/components/ui/button.tsx
28
+ var import_class_variance_authority = require("class-variance-authority");
29
+ var import_radix_ui = require("radix-ui");
30
+
31
+ // src/lib/utils.ts
32
+ var import_clsx = require("clsx");
33
+ var import_tailwind_merge = require("tailwind-merge");
34
+ function cn(...inputs) {
35
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
36
+ }
37
+
38
+ // src/components/ui/button.tsx
39
+ var import_jsx_runtime = require("react/jsx-runtime");
40
+ var buttonVariants = (0, import_class_variance_authority.cva)(
41
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
42
+ {
43
+ variants: {
44
+ variant: {
45
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
46
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
47
+ outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
48
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
49
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
50
+ link: "text-primary underline-offset-4 hover:underline"
51
+ },
52
+ size: {
53
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
54
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
55
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
56
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
57
+ icon: "size-9",
58
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
59
+ "icon-sm": "size-8",
60
+ "icon-lg": "size-10"
61
+ }
62
+ },
63
+ defaultVariants: {
64
+ variant: "default",
65
+ size: "default"
66
+ }
67
+ }
68
+ );
69
+ function Button({
70
+ className,
71
+ variant = "default",
72
+ size = "default",
73
+ asChild = false,
74
+ ...props
75
+ }) {
76
+ const Comp = asChild ? import_radix_ui.Slot.Root : "button";
77
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
78
+ Comp,
79
+ {
80
+ "data-slot": "button",
81
+ "data-variant": variant,
82
+ "data-size": size,
83
+ className: cn(buttonVariants({ variant, size, className })),
84
+ ...props
85
+ }
86
+ );
87
+ }
88
+
89
+ // src/components/ui/hover-card.tsx
90
+ var import_radix_ui2 = require("radix-ui");
91
+ var import_jsx_runtime2 = require("react/jsx-runtime");
92
+
93
+ // src/components/ai-elements/attachments.tsx
94
+ var import_lucide_react = require("lucide-react");
95
+ var import_react = require("react");
96
+ var import_jsx_runtime3 = require("react/jsx-runtime");
97
+ var getMediaCategory = (data) => {
98
+ if (data.type === "source-document") {
99
+ return "source";
100
+ }
101
+ const mediaType = data.mediaType ?? "";
102
+ if (mediaType.startsWith("image/")) {
103
+ return "image";
104
+ }
105
+ if (mediaType.startsWith("video/")) {
106
+ return "video";
107
+ }
108
+ if (mediaType.startsWith("audio/")) {
109
+ return "audio";
110
+ }
111
+ if (mediaType.startsWith("application/") || mediaType.startsWith("text/")) {
112
+ return "document";
113
+ }
114
+ return "unknown";
115
+ };
116
+ var AttachmentsContext = (0, import_react.createContext)(null);
117
+ var AttachmentContext = (0, import_react.createContext)(null);
118
+ var useAttachmentsContext = () => (0, import_react.useContext)(AttachmentsContext) ?? { variant: "grid" };
119
+ var useAttachmentContext = () => {
120
+ const ctx = (0, import_react.useContext)(AttachmentContext);
121
+ if (!ctx) {
122
+ throw new Error("Attachment components must be used within <Attachment>");
123
+ }
124
+ return ctx;
125
+ };
126
+ var Attachments = ({
127
+ variant = "grid",
128
+ className,
129
+ children,
130
+ ...props
131
+ }) => {
132
+ const contextValue = (0, import_react.useMemo)(() => ({ variant }), [variant]);
133
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AttachmentsContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
134
+ "div",
135
+ {
136
+ className: cn(
137
+ "flex items-start",
138
+ variant === "list" ? "flex-col gap-2" : "flex-wrap gap-2",
139
+ variant === "grid" && "ml-auto w-fit",
140
+ className
141
+ ),
142
+ ...props,
143
+ children
144
+ }
145
+ ) });
146
+ };
147
+ var Attachment = ({
148
+ data,
149
+ onRemove,
150
+ className,
151
+ children,
152
+ ...props
153
+ }) => {
154
+ const { variant } = useAttachmentsContext();
155
+ const mediaCategory = getMediaCategory(data);
156
+ const contextValue = (0, import_react.useMemo)(
157
+ () => ({ data, mediaCategory, onRemove, variant }),
158
+ [data, mediaCategory, onRemove, variant]
159
+ );
160
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AttachmentContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
161
+ "div",
162
+ {
163
+ className: cn(
164
+ "group relative",
165
+ variant === "grid" && "size-24 overflow-hidden rounded-lg",
166
+ variant === "inline" && [
167
+ "flex h-8 cursor-pointer select-none items-center gap-1.5",
168
+ "rounded-md border border-border px-1.5",
169
+ "font-medium text-sm transition-all",
170
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50"
171
+ ],
172
+ variant === "list" && [
173
+ "flex w-full items-center gap-3 rounded-lg border p-3",
174
+ "hover:bg-accent/50"
175
+ ],
176
+ className
177
+ ),
178
+ ...props,
179
+ children
180
+ }
181
+ ) });
182
+ };
183
+ var AttachmentPreview = ({
184
+ fallbackIcon,
185
+ className,
186
+ ...props
187
+ }) => {
188
+ const { data, mediaCategory, variant } = useAttachmentContext();
189
+ const iconSize = variant === "inline" ? "size-3" : "size-4";
190
+ const renderImage = (url, filename, isGrid) => isGrid ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
191
+ "img",
192
+ {
193
+ alt: filename || "Image",
194
+ className: "size-full object-cover",
195
+ height: 96,
196
+ src: url,
197
+ width: 96
198
+ }
199
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
200
+ "img",
201
+ {
202
+ alt: filename || "Image",
203
+ className: "size-full rounded object-cover",
204
+ height: 20,
205
+ src: url,
206
+ width: 20
207
+ }
208
+ );
209
+ const renderIcon = (Icon) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { className: cn(iconSize, "text-muted-foreground") });
210
+ const renderContent = () => {
211
+ if (mediaCategory === "image" && data.type === "file" && data.url) {
212
+ return renderImage(data.url, data.filename, variant === "grid");
213
+ }
214
+ if (mediaCategory === "video" && data.type === "file" && data.url) {
215
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("video", { className: "size-full object-cover", muted: true, src: data.url });
216
+ }
217
+ const iconMap = {
218
+ image: import_lucide_react.ImageIcon,
219
+ video: import_lucide_react.VideoIcon,
220
+ audio: import_lucide_react.Music2Icon,
221
+ source: import_lucide_react.GlobeIcon,
222
+ document: import_lucide_react.FileTextIcon,
223
+ unknown: import_lucide_react.PaperclipIcon
224
+ };
225
+ const Icon = iconMap[mediaCategory];
226
+ return fallbackIcon ?? renderIcon(Icon);
227
+ };
228
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
229
+ "div",
230
+ {
231
+ className: cn(
232
+ "flex shrink-0 items-center justify-center overflow-hidden",
233
+ variant === "grid" && "size-full bg-muted",
234
+ variant === "inline" && "size-5 rounded bg-background",
235
+ variant === "list" && "size-12 rounded bg-muted",
236
+ className
237
+ ),
238
+ ...props,
239
+ children: renderContent()
240
+ }
241
+ );
242
+ };
243
+ var AttachmentRemove = ({
244
+ label = "Remove",
245
+ className,
246
+ children,
247
+ ...props
248
+ }) => {
249
+ const { onRemove, variant } = useAttachmentContext();
250
+ if (!onRemove) {
251
+ return null;
252
+ }
253
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
254
+ Button,
255
+ {
256
+ "aria-label": label,
257
+ className: cn(
258
+ variant === "grid" && [
259
+ "absolute top-2 right-2 size-6 rounded-full p-0",
260
+ "bg-background/80 backdrop-blur-sm",
261
+ "opacity-0 transition-opacity group-hover:opacity-100",
262
+ "hover:bg-background",
263
+ "[&>svg]:size-3"
264
+ ],
265
+ variant === "inline" && [
266
+ "size-5 rounded p-0",
267
+ "opacity-0 transition-opacity group-hover:opacity-100",
268
+ "[&>svg]:size-2.5"
269
+ ],
270
+ variant === "list" && ["size-8 shrink-0 rounded p-0", "[&>svg]:size-4"],
271
+ className
272
+ ),
273
+ onClick: (e) => {
274
+ e.stopPropagation();
275
+ onRemove();
276
+ },
277
+ type: "button",
278
+ variant: "ghost",
279
+ ...props,
280
+ children: [
281
+ children ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.XIcon, {}),
282
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "sr-only", children: label })
283
+ ]
284
+ }
285
+ );
286
+ };
287
+
288
+ // src/components/ui/command.tsx
289
+ var import_cmdk = require("cmdk");
290
+ var import_lucide_react3 = require("lucide-react");
291
+
292
+ // src/components/ui/dialog.tsx
293
+ var import_lucide_react2 = require("lucide-react");
294
+ var import_radix_ui3 = require("radix-ui");
295
+ var import_jsx_runtime4 = require("react/jsx-runtime");
296
+
297
+ // src/components/ui/command.tsx
298
+ var import_jsx_runtime5 = require("react/jsx-runtime");
299
+
300
+ // src/components/ui/dropdown-menu.tsx
301
+ var import_lucide_react4 = require("lucide-react");
302
+ var import_radix_ui4 = require("radix-ui");
303
+ var import_jsx_runtime6 = require("react/jsx-runtime");
304
+
305
+ // src/components/ui/input-group.tsx
306
+ var import_class_variance_authority2 = require("class-variance-authority");
307
+
308
+ // src/components/ui/input.tsx
309
+ var import_jsx_runtime7 = require("react/jsx-runtime");
310
+
311
+ // src/components/ui/textarea.tsx
312
+ var import_jsx_runtime8 = require("react/jsx-runtime");
313
+ function Textarea({ className, ...props }) {
314
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
315
+ "textarea",
316
+ {
317
+ "data-slot": "textarea",
318
+ className: cn(
319
+ "border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
320
+ className
321
+ ),
322
+ ...props
323
+ }
324
+ );
325
+ }
326
+
327
+ // src/components/ui/input-group.tsx
328
+ var import_jsx_runtime9 = require("react/jsx-runtime");
329
+ function InputGroup({ className, ...props }) {
330
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
331
+ "div",
332
+ {
333
+ "data-slot": "input-group",
334
+ role: "group",
335
+ className: cn(
336
+ "group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none",
337
+ "h-9 min-w-0 has-[>textarea]:h-auto",
338
+ // Variants based on alignment.
339
+ "has-[>[data-align=inline-start]]:[&>input]:pl-2",
340
+ "has-[>[data-align=inline-end]]:[&>input]:pr-2",
341
+ "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
342
+ "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
343
+ // Focus state.
344
+ "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]",
345
+ // Error state.
346
+ "has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
347
+ className
348
+ ),
349
+ ...props
350
+ }
351
+ );
352
+ }
353
+ var inputGroupAddonVariants = (0, import_class_variance_authority2.cva)(
354
+ "text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
355
+ {
356
+ variants: {
357
+ align: {
358
+ "inline-start": "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
359
+ "inline-end": "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
360
+ "block-start": "order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5",
361
+ "block-end": "order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5"
362
+ }
363
+ },
364
+ defaultVariants: {
365
+ align: "inline-start"
366
+ }
367
+ }
368
+ );
369
+ function InputGroupAddon({
370
+ className,
371
+ align = "inline-start",
372
+ ...props
373
+ }) {
374
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
375
+ "div",
376
+ {
377
+ role: "group",
378
+ "data-slot": "input-group-addon",
379
+ "data-align": align,
380
+ className: cn(inputGroupAddonVariants({ align }), className),
381
+ onClick: (e) => {
382
+ if (e.target.closest("button")) {
383
+ return;
384
+ }
385
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
386
+ },
387
+ ...props
388
+ }
389
+ );
390
+ }
391
+ var inputGroupButtonVariants = (0, import_class_variance_authority2.cva)(
392
+ "text-sm shadow-none flex gap-2 items-center",
393
+ {
394
+ variants: {
395
+ size: {
396
+ xs: "h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2",
397
+ sm: "h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5",
398
+ "icon-xs": "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
399
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0"
400
+ }
401
+ },
402
+ defaultVariants: {
403
+ size: "xs"
404
+ }
405
+ }
406
+ );
407
+ function InputGroupButton({
408
+ className,
409
+ type = "button",
410
+ variant = "ghost",
411
+ size = "xs",
412
+ ...props
413
+ }) {
414
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
415
+ Button,
416
+ {
417
+ type,
418
+ "data-size": size,
419
+ variant,
420
+ className: cn(inputGroupButtonVariants({ size }), className),
421
+ ...props
422
+ }
423
+ );
424
+ }
425
+ function InputGroupTextarea({
426
+ className,
427
+ ...props
428
+ }) {
429
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
430
+ Textarea,
431
+ {
432
+ "data-slot": "input-group-control",
433
+ className: cn(
434
+ "flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",
435
+ className
436
+ ),
437
+ ...props
438
+ }
439
+ );
440
+ }
441
+
442
+ // src/components/ui/select.tsx
443
+ var import_lucide_react5 = require("lucide-react");
444
+ var import_radix_ui5 = require("radix-ui");
445
+ var import_jsx_runtime10 = require("react/jsx-runtime");
446
+
447
+ // src/components/ui/spinner.tsx
448
+ var import_lucide_react6 = require("lucide-react");
449
+ var import_jsx_runtime11 = require("react/jsx-runtime");
450
+ function Spinner({ className, ...props }) {
451
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
452
+ import_lucide_react6.Loader2Icon,
453
+ {
454
+ role: "status",
455
+ "aria-label": "Loading",
456
+ className: cn("size-4 animate-spin", className),
457
+ ...props
458
+ }
459
+ );
460
+ }
461
+
462
+ // src/components/ai-elements/prompt-input.tsx
463
+ var import_lucide_react7 = require("lucide-react");
464
+ var import_nanoid = require("nanoid");
465
+ var import_react2 = require("react");
466
+ var import_jsx_runtime12 = require("react/jsx-runtime");
467
+ var PromptInputController = (0, import_react2.createContext)(
468
+ null
469
+ );
470
+ var ProviderAttachmentsContext = (0, import_react2.createContext)(
471
+ null
472
+ );
473
+ var useOptionalPromptInputController = () => (0, import_react2.useContext)(PromptInputController);
474
+ var useOptionalProviderAttachments = () => (0, import_react2.useContext)(ProviderAttachmentsContext);
475
+ function PromptInputProvider({
476
+ initialInput: initialTextInput = "",
477
+ children
478
+ }) {
479
+ const [textInput, setTextInput] = (0, import_react2.useState)(initialTextInput);
480
+ const clearInput = (0, import_react2.useCallback)(() => setTextInput(""), []);
481
+ const [attachmentFiles, setAttachmentFiles] = (0, import_react2.useState)([]);
482
+ const fileInputRef = (0, import_react2.useRef)(null);
483
+ const openRef = (0, import_react2.useRef)(() => void 0);
484
+ const add = (0, import_react2.useCallback)((files) => {
485
+ const incoming = Array.from(files);
486
+ if (incoming.length === 0) {
487
+ return;
488
+ }
489
+ setAttachmentFiles(
490
+ (prev) => prev.concat(
491
+ incoming.map((file) => ({
492
+ id: (0, import_nanoid.nanoid)(),
493
+ type: "file",
494
+ url: URL.createObjectURL(file),
495
+ mediaType: file.type,
496
+ filename: file.name
497
+ }))
498
+ )
499
+ );
500
+ }, []);
501
+ const remove = (0, import_react2.useCallback)((id) => {
502
+ setAttachmentFiles((prev) => {
503
+ const found = prev.find((f) => f.id === id);
504
+ if (found?.url) {
505
+ URL.revokeObjectURL(found.url);
506
+ }
507
+ return prev.filter((f) => f.id !== id);
508
+ });
509
+ }, []);
510
+ const clear = (0, import_react2.useCallback)(() => {
511
+ setAttachmentFiles((prev) => {
512
+ for (const f of prev) {
513
+ if (f.url) {
514
+ URL.revokeObjectURL(f.url);
515
+ }
516
+ }
517
+ return [];
518
+ });
519
+ }, []);
520
+ const attachmentsRef = (0, import_react2.useRef)(attachmentFiles);
521
+ attachmentsRef.current = attachmentFiles;
522
+ (0, import_react2.useEffect)(
523
+ () => () => {
524
+ for (const f of attachmentsRef.current) {
525
+ if (f.url) {
526
+ URL.revokeObjectURL(f.url);
527
+ }
528
+ }
529
+ },
530
+ []
531
+ );
532
+ const openFileDialog = (0, import_react2.useCallback)(() => {
533
+ openRef.current?.();
534
+ }, []);
535
+ const attachments = (0, import_react2.useMemo)(
536
+ () => ({
537
+ files: attachmentFiles,
538
+ add,
539
+ remove,
540
+ clear,
541
+ openFileDialog,
542
+ fileInputRef
543
+ }),
544
+ [attachmentFiles, add, remove, clear, openFileDialog]
545
+ );
546
+ const __registerFileInput = (0, import_react2.useCallback)(
547
+ (ref, open) => {
548
+ fileInputRef.current = ref.current;
549
+ openRef.current = open;
550
+ },
551
+ []
552
+ );
553
+ const controller = (0, import_react2.useMemo)(
554
+ () => ({
555
+ textInput: {
556
+ value: textInput,
557
+ setInput: setTextInput,
558
+ clear: clearInput
559
+ },
560
+ attachments,
561
+ __registerFileInput
562
+ }),
563
+ [textInput, clearInput, attachments, __registerFileInput]
564
+ );
565
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PromptInputController.Provider, { value: controller, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ProviderAttachmentsContext.Provider, { value: attachments, children }) });
566
+ }
567
+ var LocalAttachmentsContext = (0, import_react2.createContext)(null);
568
+ var usePromptInputAttachments = () => {
569
+ const provider = useOptionalProviderAttachments();
570
+ const local = (0, import_react2.useContext)(LocalAttachmentsContext);
571
+ const context = local ?? provider;
572
+ if (!context) {
573
+ throw new Error(
574
+ "usePromptInputAttachments must be used within a PromptInput or PromptInputProvider"
575
+ );
576
+ }
577
+ return context;
578
+ };
579
+ var LocalReferencedSourcesContext = (0, import_react2.createContext)(null);
580
+ var PromptInput = ({
581
+ className,
582
+ accept,
583
+ multiple,
584
+ globalDrop,
585
+ syncHiddenInput,
586
+ maxFiles,
587
+ maxFileSize,
588
+ onError,
589
+ onSubmit,
590
+ children,
591
+ ...props
592
+ }) => {
593
+ const controller = useOptionalPromptInputController();
594
+ const usingProvider = !!controller;
595
+ const inputRef = (0, import_react2.useRef)(null);
596
+ const formRef = (0, import_react2.useRef)(null);
597
+ const [items, setItems] = (0, import_react2.useState)([]);
598
+ const files = usingProvider ? controller.attachments.files : items;
599
+ const [referencedSources, setReferencedSources] = (0, import_react2.useState)([]);
600
+ const filesRef = (0, import_react2.useRef)(files);
601
+ filesRef.current = files;
602
+ const openFileDialogLocal = (0, import_react2.useCallback)(() => {
603
+ inputRef.current?.click();
604
+ }, []);
605
+ const matchesAccept = (0, import_react2.useCallback)(
606
+ (f) => {
607
+ if (!accept || accept.trim() === "") {
608
+ return true;
609
+ }
610
+ const patterns = accept.split(",").map((s) => s.trim()).filter(Boolean);
611
+ return patterns.some((pattern) => {
612
+ if (pattern.endsWith("/*")) {
613
+ const prefix = pattern.slice(0, -1);
614
+ return f.type.startsWith(prefix);
615
+ }
616
+ return f.type === pattern;
617
+ });
618
+ },
619
+ [accept]
620
+ );
621
+ const addLocal = (0, import_react2.useCallback)(
622
+ (fileList) => {
623
+ const incoming = Array.from(fileList);
624
+ const accepted = incoming.filter((f) => matchesAccept(f));
625
+ if (incoming.length && accepted.length === 0) {
626
+ onError?.({
627
+ code: "accept",
628
+ message: "No files match the accepted types."
629
+ });
630
+ return;
631
+ }
632
+ const withinSize = (f) => maxFileSize ? f.size <= maxFileSize : true;
633
+ const sized = accepted.filter(withinSize);
634
+ if (accepted.length > 0 && sized.length === 0) {
635
+ onError?.({
636
+ code: "max_file_size",
637
+ message: "All files exceed the maximum size."
638
+ });
639
+ return;
640
+ }
641
+ setItems((prev) => {
642
+ const capacity = typeof maxFiles === "number" ? Math.max(0, maxFiles - prev.length) : void 0;
643
+ const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized;
644
+ if (typeof capacity === "number" && sized.length > capacity) {
645
+ onError?.({
646
+ code: "max_files",
647
+ message: "Too many files. Some were not added."
648
+ });
649
+ }
650
+ const next = [];
651
+ for (const file of capped) {
652
+ next.push({
653
+ id: (0, import_nanoid.nanoid)(),
654
+ type: "file",
655
+ url: URL.createObjectURL(file),
656
+ mediaType: file.type,
657
+ filename: file.name
658
+ });
659
+ }
660
+ return prev.concat(next);
661
+ });
662
+ },
663
+ [matchesAccept, maxFiles, maxFileSize, onError]
664
+ );
665
+ const removeLocal = (0, import_react2.useCallback)(
666
+ (id) => setItems((prev) => {
667
+ const found = prev.find((file) => file.id === id);
668
+ if (found?.url) {
669
+ URL.revokeObjectURL(found.url);
670
+ }
671
+ return prev.filter((file) => file.id !== id);
672
+ }),
673
+ []
674
+ );
675
+ const addWithProviderValidation = (0, import_react2.useCallback)(
676
+ (fileList) => {
677
+ const incoming = Array.from(fileList);
678
+ const accepted = incoming.filter((f) => matchesAccept(f));
679
+ if (incoming.length && accepted.length === 0) {
680
+ onError?.({
681
+ code: "accept",
682
+ message: "No files match the accepted types."
683
+ });
684
+ return;
685
+ }
686
+ const withinSize = (f) => maxFileSize ? f.size <= maxFileSize : true;
687
+ const sized = accepted.filter(withinSize);
688
+ if (accepted.length > 0 && sized.length === 0) {
689
+ onError?.({
690
+ code: "max_file_size",
691
+ message: "All files exceed the maximum size."
692
+ });
693
+ return;
694
+ }
695
+ const currentCount = files.length;
696
+ const capacity = typeof maxFiles === "number" ? Math.max(0, maxFiles - currentCount) : void 0;
697
+ const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized;
698
+ if (typeof capacity === "number" && sized.length > capacity) {
699
+ onError?.({
700
+ code: "max_files",
701
+ message: "Too many files. Some were not added."
702
+ });
703
+ }
704
+ if (capped.length > 0) {
705
+ controller?.attachments.add(capped);
706
+ }
707
+ },
708
+ [matchesAccept, maxFileSize, maxFiles, onError, files.length, controller]
709
+ );
710
+ const clearAttachments = (0, import_react2.useCallback)(
711
+ () => usingProvider ? controller?.attachments.clear() : setItems((prev) => {
712
+ for (const file of prev) {
713
+ if (file.url) {
714
+ URL.revokeObjectURL(file.url);
715
+ }
716
+ }
717
+ return [];
718
+ }),
719
+ [usingProvider, controller]
720
+ );
721
+ const clearReferencedSources = (0, import_react2.useCallback)(
722
+ () => setReferencedSources([]),
723
+ []
724
+ );
725
+ const add = usingProvider ? addWithProviderValidation : addLocal;
726
+ const remove = usingProvider ? controller.attachments.remove : removeLocal;
727
+ const openFileDialog = usingProvider ? controller.attachments.openFileDialog : openFileDialogLocal;
728
+ const clear = (0, import_react2.useCallback)(() => {
729
+ clearAttachments();
730
+ clearReferencedSources();
731
+ }, [clearAttachments, clearReferencedSources]);
732
+ (0, import_react2.useEffect)(() => {
733
+ if (!usingProvider) {
734
+ return;
735
+ }
736
+ controller.__registerFileInput(inputRef, () => inputRef.current?.click());
737
+ }, [usingProvider, controller]);
738
+ (0, import_react2.useEffect)(() => {
739
+ if (syncHiddenInput && inputRef.current && files.length === 0) {
740
+ inputRef.current.value = "";
741
+ }
742
+ }, [files, syncHiddenInput]);
743
+ (0, import_react2.useEffect)(() => {
744
+ const form = formRef.current;
745
+ if (!form) {
746
+ return;
747
+ }
748
+ if (globalDrop) {
749
+ return;
750
+ }
751
+ const onDragOver = (e) => {
752
+ if (e.dataTransfer?.types?.includes("Files")) {
753
+ e.preventDefault();
754
+ }
755
+ };
756
+ const onDrop = (e) => {
757
+ if (e.dataTransfer?.types?.includes("Files")) {
758
+ e.preventDefault();
759
+ }
760
+ if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {
761
+ add(e.dataTransfer.files);
762
+ }
763
+ };
764
+ form.addEventListener("dragover", onDragOver);
765
+ form.addEventListener("drop", onDrop);
766
+ return () => {
767
+ form.removeEventListener("dragover", onDragOver);
768
+ form.removeEventListener("drop", onDrop);
769
+ };
770
+ }, [add, globalDrop]);
771
+ (0, import_react2.useEffect)(() => {
772
+ if (!globalDrop) {
773
+ return;
774
+ }
775
+ const onDragOver = (e) => {
776
+ if (e.dataTransfer?.types?.includes("Files")) {
777
+ e.preventDefault();
778
+ }
779
+ };
780
+ const onDrop = (e) => {
781
+ if (e.dataTransfer?.types?.includes("Files")) {
782
+ e.preventDefault();
783
+ }
784
+ if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {
785
+ add(e.dataTransfer.files);
786
+ }
787
+ };
788
+ document.addEventListener("dragover", onDragOver);
789
+ document.addEventListener("drop", onDrop);
790
+ return () => {
791
+ document.removeEventListener("dragover", onDragOver);
792
+ document.removeEventListener("drop", onDrop);
793
+ };
794
+ }, [add, globalDrop]);
795
+ (0, import_react2.useEffect)(
796
+ () => () => {
797
+ if (!usingProvider) {
798
+ for (const f of filesRef.current) {
799
+ if (f.url) {
800
+ URL.revokeObjectURL(f.url);
801
+ }
802
+ }
803
+ }
804
+ },
805
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- cleanup only on unmount; filesRef always current
806
+ [usingProvider]
807
+ );
808
+ const handleChange = (event) => {
809
+ if (event.currentTarget.files) {
810
+ add(event.currentTarget.files);
811
+ }
812
+ event.currentTarget.value = "";
813
+ };
814
+ const convertBlobUrlToDataUrl = async (url) => {
815
+ try {
816
+ const response = await fetch(url);
817
+ const blob = await response.blob();
818
+ return new Promise((resolve) => {
819
+ const reader = new FileReader();
820
+ reader.onloadend = () => resolve(reader.result);
821
+ reader.onerror = () => resolve(null);
822
+ reader.readAsDataURL(blob);
823
+ });
824
+ } catch {
825
+ return null;
826
+ }
827
+ };
828
+ const attachmentsCtx = (0, import_react2.useMemo)(
829
+ () => ({
830
+ files: files.map((item) => ({ ...item, id: item.id })),
831
+ add,
832
+ remove,
833
+ clear: clearAttachments,
834
+ openFileDialog,
835
+ fileInputRef: inputRef
836
+ }),
837
+ [files, add, remove, clearAttachments, openFileDialog]
838
+ );
839
+ const refsCtx = (0, import_react2.useMemo)(
840
+ () => ({
841
+ sources: referencedSources,
842
+ add: (incoming) => {
843
+ const array = Array.isArray(incoming) ? incoming : [incoming];
844
+ setReferencedSources(
845
+ (prev) => prev.concat(array.map((s) => ({ ...s, id: (0, import_nanoid.nanoid)() })))
846
+ );
847
+ },
848
+ remove: (id) => {
849
+ setReferencedSources((prev) => prev.filter((s) => s.id !== id));
850
+ },
851
+ clear: clearReferencedSources
852
+ }),
853
+ [referencedSources, clearReferencedSources]
854
+ );
855
+ const handleSubmit = (event) => {
856
+ event.preventDefault();
857
+ const form = event.currentTarget;
858
+ const text = usingProvider ? controller.textInput.value : (() => {
859
+ const formData = new FormData(form);
860
+ return formData.get("message") || "";
861
+ })();
862
+ if (!usingProvider) {
863
+ form.reset();
864
+ }
865
+ Promise.all(
866
+ files.map(async ({ id, ...item }) => {
867
+ if (item.url?.startsWith("blob:")) {
868
+ const dataUrl = await convertBlobUrlToDataUrl(item.url);
869
+ return {
870
+ ...item,
871
+ url: dataUrl ?? item.url
872
+ };
873
+ }
874
+ return item;
875
+ })
876
+ ).then((convertedFiles) => {
877
+ try {
878
+ const result = onSubmit({ text, files: convertedFiles }, event);
879
+ if (result instanceof Promise) {
880
+ result.then(() => {
881
+ clear();
882
+ if (usingProvider) {
883
+ controller.textInput.clear();
884
+ }
885
+ }).catch(() => {
886
+ });
887
+ } else {
888
+ clear();
889
+ if (usingProvider) {
890
+ controller.textInput.clear();
891
+ }
892
+ }
893
+ } catch {
894
+ }
895
+ }).catch(() => {
896
+ });
897
+ };
898
+ const inner = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
899
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
900
+ "input",
901
+ {
902
+ accept,
903
+ "aria-label": "Upload files",
904
+ className: "hidden",
905
+ multiple,
906
+ onChange: handleChange,
907
+ ref: inputRef,
908
+ title: "Upload files",
909
+ type: "file"
910
+ }
911
+ ),
912
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
913
+ "form",
914
+ {
915
+ className: cn("w-full", className),
916
+ onSubmit: handleSubmit,
917
+ ref: formRef,
918
+ ...props,
919
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(InputGroup, { className: "overflow-hidden", children })
920
+ }
921
+ )
922
+ ] });
923
+ const withReferencedSources = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
924
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
925
+ };
926
+ var PromptInputBody = ({
927
+ className,
928
+ ...props
929
+ }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: cn("contents", className), ...props });
930
+ var PromptInputTextarea = ({
931
+ onChange,
932
+ onKeyDown,
933
+ className,
934
+ placeholder = "What would you like to know?",
935
+ ...props
936
+ }) => {
937
+ const controller = useOptionalPromptInputController();
938
+ const attachments = usePromptInputAttachments();
939
+ const [isComposing, setIsComposing] = (0, import_react2.useState)(false);
940
+ const handleKeyDown = (e) => {
941
+ onKeyDown?.(e);
942
+ if (e.defaultPrevented) {
943
+ return;
944
+ }
945
+ if (e.key === "Enter") {
946
+ if (isComposing || e.nativeEvent.isComposing) {
947
+ return;
948
+ }
949
+ if (e.shiftKey) {
950
+ return;
951
+ }
952
+ e.preventDefault();
953
+ const form = e.currentTarget.form;
954
+ const submitButton = form?.querySelector(
955
+ 'button[type="submit"]'
956
+ );
957
+ if (submitButton?.disabled) {
958
+ return;
959
+ }
960
+ form?.requestSubmit();
961
+ }
962
+ if (e.key === "Backspace" && e.currentTarget.value === "" && attachments.files.length > 0) {
963
+ e.preventDefault();
964
+ const lastAttachment = attachments.files[attachments.files.length - 1];
965
+ if (lastAttachment) {
966
+ attachments.remove(lastAttachment.id);
967
+ }
968
+ }
969
+ };
970
+ const handlePaste = (event) => {
971
+ const items = event.clipboardData?.items;
972
+ if (!items) {
973
+ return;
974
+ }
975
+ const files = [];
976
+ for (const item of items) {
977
+ if (item.kind === "file") {
978
+ const file = item.getAsFile();
979
+ if (file) {
980
+ files.push(file);
981
+ }
982
+ }
983
+ }
984
+ if (files.length > 0) {
985
+ event.preventDefault();
986
+ attachments.add(files);
987
+ }
988
+ };
989
+ const controlledProps = controller ? {
990
+ value: controller.textInput.value,
991
+ onChange: (e) => {
992
+ controller.textInput.setInput(e.currentTarget.value);
993
+ onChange?.(e);
994
+ }
995
+ } : {
996
+ onChange
997
+ };
998
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
999
+ InputGroupTextarea,
1000
+ {
1001
+ className: cn("field-sizing-content max-h-48 min-h-16", className),
1002
+ name: "message",
1003
+ onCompositionEnd: () => setIsComposing(false),
1004
+ onCompositionStart: () => setIsComposing(true),
1005
+ onKeyDown: handleKeyDown,
1006
+ onPaste: handlePaste,
1007
+ placeholder,
1008
+ ...props,
1009
+ ...controlledProps
1010
+ }
1011
+ );
1012
+ };
1013
+ var PromptInputFooter = ({
1014
+ className,
1015
+ ...props
1016
+ }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1017
+ InputGroupAddon,
1018
+ {
1019
+ align: "block-end",
1020
+ className: cn("justify-between gap-1", className),
1021
+ ...props
1022
+ }
1023
+ );
1024
+ var PromptInputTools = ({
1025
+ className,
1026
+ ...props
1027
+ }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: cn("flex items-center gap-1", className), ...props });
1028
+ var PromptInputSubmit = ({
1029
+ className,
1030
+ variant = "default",
1031
+ size = "icon-sm",
1032
+ status,
1033
+ onStop,
1034
+ onClick,
1035
+ children,
1036
+ ...props
1037
+ }) => {
1038
+ const isGenerating = status === "submitted" || status === "streaming";
1039
+ let Icon = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react7.CornerDownLeftIcon, { className: "size-4" });
1040
+ if (status === "submitted") {
1041
+ Icon = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Spinner, {});
1042
+ } else if (status === "streaming") {
1043
+ Icon = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react7.SquareIcon, { className: "size-4" });
1044
+ } else if (status === "error") {
1045
+ Icon = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react7.XIcon, { className: "size-4" });
1046
+ }
1047
+ const handleClick = (e) => {
1048
+ if (isGenerating && onStop) {
1049
+ e.preventDefault();
1050
+ onStop();
1051
+ return;
1052
+ }
1053
+ onClick?.(e);
1054
+ };
1055
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1056
+ InputGroupButton,
1057
+ {
1058
+ "aria-label": isGenerating ? "Stop" : "Submit",
1059
+ className: cn(className),
1060
+ onClick: handleClick,
1061
+ size,
1062
+ type: isGenerating && onStop ? "button" : "submit",
1063
+ variant,
1064
+ ...props,
1065
+ children: children ?? Icon
1066
+ }
1067
+ );
1068
+ };
1069
+
1070
+ // src/locales/context.ts
1071
+ var import_react3 = require("react");
1072
+ var LocaleContext = (0, import_react3.createContext)({
1073
+ locale: "zh-CN"
1074
+ });
1075
+
1076
+ // src/locales/hooks.ts
1077
+ var import_react4 = require("react");
1078
+
1079
+ // src/locales/langs/zh-cn.ts
1080
+ var zhCN = {
1081
+ chat: {
1082
+ emptyTitle: "\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u5230\u4F60\uFF1F"
1083
+ },
1084
+ input: {
1085
+ placeholder: "\u8BF7\u8F93\u5165\u6D88\u606F...",
1086
+ disclaimer: "\u5185\u5BB9\u7531 AI \u751F\u6210\uFF0C\u4EC5\u4F9B\u53C2\u8003"
1087
+ },
1088
+ message: {
1089
+ copy: "\u590D\u5236",
1090
+ copied: "\u5DF2\u590D\u5236",
1091
+ copyToClipboard: "\u590D\u5236",
1092
+ paused: "\u5DF2\u6682\u505C\u751F\u6210"
1093
+ }
1094
+ };
1095
+
1096
+ // src/locales/langs/en.ts
1097
+ var en = {
1098
+ chat: {
1099
+ emptyTitle: "How can I help you?"
1100
+ },
1101
+ input: {
1102
+ placeholder: "Type your message...",
1103
+ disclaimer: "AI-generated content, for reference only"
1104
+ },
1105
+ message: {
1106
+ copy: "Copy",
1107
+ copied: "Copied!",
1108
+ copyToClipboard: "Copy to clipboard",
1109
+ paused: "Generation paused"
1110
+ }
1111
+ };
1112
+
1113
+ // src/locales/langs/index.ts
1114
+ var locales = {
1115
+ "zh-CN": zhCN,
1116
+ "en-US": en
1117
+ };
1118
+
1119
+ // src/locales/hooks.ts
1120
+ function useLocale(name) {
1121
+ const { locale: localeCode } = (0, import_react4.useContext)(LocaleContext);
1122
+ const locale = (0, import_react4.useMemo)(() => {
1123
+ const fullLocale = locales[localeCode] ?? locales["zh-CN"];
1124
+ return name ? fullLocale[name] : fullLocale;
1125
+ }, [localeCode, name]);
1126
+ return { locale, localeCode };
1127
+ }
1128
+
1129
+ // src/components/chat/Input.tsx
1130
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1131
+ var PromptInputAttachmentsDisplay = () => {
1132
+ const attachments = usePromptInputAttachments();
1133
+ if (attachments.files.length === 0) {
1134
+ return null;
1135
+ }
1136
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Attachments, { variant: "inline", children: attachments.files.map((attachment) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1137
+ Attachment,
1138
+ {
1139
+ data: attachment,
1140
+ onRemove: () => attachments.remove(attachment.id),
1141
+ children: [
1142
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AttachmentPreview, {}),
1143
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AttachmentRemove, {})
1144
+ ]
1145
+ },
1146
+ attachment.id
1147
+ )) });
1148
+ };
1149
+ function Input2({
1150
+ onSend,
1151
+ onStop,
1152
+ placeholder,
1153
+ streaming = false
1154
+ }) {
1155
+ const { locale } = useLocale("input");
1156
+ const status = streaming ? "streaming" : "ready";
1157
+ const handleSubmit = (message) => {
1158
+ const text = message.text.trim();
1159
+ const hasText = Boolean(text);
1160
+ const hasAttachments = Boolean(message.files?.length);
1161
+ if (!(hasText || hasAttachments)) {
1162
+ return;
1163
+ }
1164
+ onSend?.({
1165
+ ...message,
1166
+ text
1167
+ });
1168
+ };
1169
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "pb-4", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(PromptInputProvider, { children: [
1170
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(PromptInput, { globalDrop: true, multiple: true, onSubmit: handleSubmit, children: [
1171
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PromptInputAttachmentsDisplay, {}),
1172
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PromptInputBody, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1173
+ PromptInputTextarea,
1174
+ {
1175
+ placeholder: placeholder ?? locale.placeholder
1176
+ }
1177
+ ) }),
1178
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(PromptInputFooter, { children: [
1179
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PromptInputTools, {}),
1180
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PromptInputSubmit, { status, onStop })
1181
+ ] })
1182
+ ] }),
1183
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "text-center text-xs opacity-50 mt-3", children: locale.disclaimer })
1184
+ ] }) });
1185
+ }
1186
+
1187
+ // src/components/chat/Chat.tsx
1188
+ var import_agent_react_core = require("@cloudbase/agent-react-core");
1189
+
1190
+ // src/components/ui/button-group.tsx
1191
+ var import_class_variance_authority3 = require("class-variance-authority");
1192
+ var import_radix_ui7 = require("radix-ui");
1193
+
1194
+ // src/components/ui/separator.tsx
1195
+ var import_radix_ui6 = require("radix-ui");
1196
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1197
+
1198
+ // src/components/ui/button-group.tsx
1199
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1200
+ var buttonGroupVariants = (0, import_class_variance_authority3.cva)(
1201
+ "flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2",
1202
+ {
1203
+ variants: {
1204
+ orientation: {
1205
+ horizontal: "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none",
1206
+ vertical: "flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none"
1207
+ }
1208
+ },
1209
+ defaultVariants: {
1210
+ orientation: "horizontal"
1211
+ }
1212
+ }
1213
+ );
1214
+
1215
+ // src/components/ui/tooltip.tsx
1216
+ var import_radix_ui8 = require("radix-ui");
1217
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1218
+ function TooltipProvider({
1219
+ delayDuration = 0,
1220
+ ...props
1221
+ }) {
1222
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1223
+ import_radix_ui8.Tooltip.Provider,
1224
+ {
1225
+ "data-slot": "tooltip-provider",
1226
+ delayDuration,
1227
+ ...props
1228
+ }
1229
+ );
1230
+ }
1231
+ function Tooltip({
1232
+ ...props
1233
+ }) {
1234
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_radix_ui8.Tooltip.Root, { "data-slot": "tooltip", ...props }) });
1235
+ }
1236
+ function TooltipTrigger({
1237
+ ...props
1238
+ }) {
1239
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_radix_ui8.Tooltip.Trigger, { "data-slot": "tooltip-trigger", ...props });
1240
+ }
1241
+ function TooltipContent({
1242
+ className,
1243
+ sideOffset = 0,
1244
+ children,
1245
+ ...props
1246
+ }) {
1247
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_radix_ui8.Tooltip.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
1248
+ import_radix_ui8.Tooltip.Content,
1249
+ {
1250
+ "data-slot": "tooltip-content",
1251
+ sideOffset,
1252
+ className: cn(
1253
+ "bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 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 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
1254
+ className
1255
+ ),
1256
+ ...props,
1257
+ children: [
1258
+ children,
1259
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_radix_ui8.Tooltip.Arrow, { className: "bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })
1260
+ ]
1261
+ }
1262
+ ) });
1263
+ }
1264
+
1265
+ // src/components/ai-elements/message.tsx
1266
+ var import_cjk = require("@streamdown/cjk");
1267
+ var import_code = require("@streamdown/code");
1268
+ var import_math = require("@streamdown/math");
1269
+ var import_mermaid = require("@streamdown/mermaid");
1270
+ var import_lucide_react8 = require("lucide-react");
1271
+ var import_react5 = require("react");
1272
+ var import_streamdown = require("streamdown");
1273
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1274
+ var Message = ({ className, from, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1275
+ "div",
1276
+ {
1277
+ className: cn(
1278
+ "group flex w-full max-w-[95%] flex-col gap-2",
1279
+ from === "user" ? "is-user ml-auto justify-end" : "is-assistant",
1280
+ className
1281
+ ),
1282
+ ...props
1283
+ }
1284
+ );
1285
+ var MessageContent = ({
1286
+ children,
1287
+ className,
1288
+ ...props
1289
+ }) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1290
+ "div",
1291
+ {
1292
+ className: cn(
1293
+ "is-user:dark flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-sm",
1294
+ "group-[.is-user]:ml-auto group-[.is-user]:rounded-lg group-[.is-user]:bg-secondary group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-foreground",
1295
+ "group-[.is-assistant]:text-foreground",
1296
+ className
1297
+ ),
1298
+ ...props,
1299
+ children
1300
+ }
1301
+ );
1302
+ var MessageActions = ({
1303
+ className,
1304
+ children,
1305
+ ...props
1306
+ }) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: cn("flex items-center gap-1", className), ...props, children });
1307
+ var MessageAction = ({
1308
+ tooltip,
1309
+ children,
1310
+ label,
1311
+ variant = "ghost",
1312
+ size = "icon-sm",
1313
+ ...props
1314
+ }) => {
1315
+ const button = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Button, { size, type: "button", variant, ...props, children: [
1316
+ children,
1317
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "sr-only", children: label || tooltip })
1318
+ ] });
1319
+ if (tooltip) {
1320
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Tooltip, { children: [
1321
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TooltipTrigger, { asChild: true, children: button }),
1322
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(TooltipContent, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { children: tooltip }) })
1323
+ ] }) });
1324
+ }
1325
+ return button;
1326
+ };
1327
+ var MessageBranchContext = (0, import_react5.createContext)(
1328
+ null
1329
+ );
1330
+ var MessageResponse = (0, import_react5.memo)(
1331
+ ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1332
+ import_streamdown.Streamdown,
1333
+ {
1334
+ className: cn(
1335
+ "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
1336
+ className
1337
+ ),
1338
+ plugins: { code: import_code.code, mermaid: import_mermaid.mermaid, math: import_math.math, cjk: import_cjk.cjk },
1339
+ ...props
1340
+ }
1341
+ ),
1342
+ (prevProps, nextProps) => prevProps.children === nextProps.children
1343
+ );
1344
+ MessageResponse.displayName = "MessageResponse";
1345
+
1346
+ // src/components/ui/badge.tsx
1347
+ var import_class_variance_authority4 = require("class-variance-authority");
1348
+ var import_radix_ui9 = require("radix-ui");
1349
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1350
+ var badgeVariants = (0, import_class_variance_authority4.cva)(
1351
+ "inline-flex items-center justify-center rounded-full border border-transparent px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
1352
+ {
1353
+ variants: {
1354
+ variant: {
1355
+ default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
1356
+ secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
1357
+ destructive: "bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
1358
+ outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
1359
+ ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
1360
+ link: "text-primary underline-offset-4 [a&]:hover:underline"
1361
+ }
1362
+ },
1363
+ defaultVariants: {
1364
+ variant: "default"
1365
+ }
1366
+ }
1367
+ );
1368
+ function Badge({
1369
+ className,
1370
+ variant = "default",
1371
+ asChild = false,
1372
+ ...props
1373
+ }) {
1374
+ const Comp = asChild ? import_radix_ui9.Slot.Root : "span";
1375
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1376
+ Comp,
1377
+ {
1378
+ "data-slot": "badge",
1379
+ "data-variant": variant,
1380
+ className: cn(badgeVariants({ variant }), className),
1381
+ ...props
1382
+ }
1383
+ );
1384
+ }
1385
+
1386
+ // src/components/ui/collapsible.tsx
1387
+ var import_radix_ui10 = require("radix-ui");
1388
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1389
+ function Collapsible({
1390
+ ...props
1391
+ }) {
1392
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_radix_ui10.Collapsible.Root, { "data-slot": "collapsible", ...props });
1393
+ }
1394
+ function CollapsibleTrigger({
1395
+ ...props
1396
+ }) {
1397
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1398
+ import_radix_ui10.Collapsible.CollapsibleTrigger,
1399
+ {
1400
+ "data-slot": "collapsible-trigger",
1401
+ ...props
1402
+ }
1403
+ );
1404
+ }
1405
+ function CollapsibleContent({
1406
+ ...props
1407
+ }) {
1408
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1409
+ import_radix_ui10.Collapsible.CollapsibleContent,
1410
+ {
1411
+ "data-slot": "collapsible-content",
1412
+ ...props
1413
+ }
1414
+ );
1415
+ }
1416
+
1417
+ // src/components/ai-elements/tool.tsx
1418
+ var import_lucide_react10 = require("lucide-react");
1419
+ var import_react7 = require("react");
1420
+
1421
+ // src/components/ai-elements/code-block.tsx
1422
+ var import_lucide_react9 = require("lucide-react");
1423
+ var import_react6 = require("react");
1424
+ var import_shiki = require("shiki");
1425
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1426
+ var isItalic = (fontStyle) => fontStyle && fontStyle & 1;
1427
+ var isBold = (fontStyle) => fontStyle && fontStyle & 2;
1428
+ var isUnderline = (fontStyle) => (
1429
+ // biome-ignore lint/suspicious/noBitwiseOperators: shiki bitflag check
1430
+ fontStyle && fontStyle & 4
1431
+ );
1432
+ var addKeysToTokens = (lines) => lines.map((line, lineIdx) => ({
1433
+ key: `line-${lineIdx}`,
1434
+ tokens: line.map((token, tokenIdx) => ({
1435
+ token,
1436
+ key: `line-${lineIdx}-${tokenIdx}`
1437
+ }))
1438
+ }));
1439
+ var TokenSpan = ({ token }) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1440
+ "span",
1441
+ {
1442
+ className: "dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)]",
1443
+ style: {
1444
+ color: token.color,
1445
+ backgroundColor: token.bgColor,
1446
+ ...token.htmlStyle,
1447
+ fontStyle: isItalic(token.fontStyle) ? "italic" : void 0,
1448
+ fontWeight: isBold(token.fontStyle) ? "bold" : void 0,
1449
+ textDecoration: isUnderline(token.fontStyle) ? "underline" : void 0
1450
+ },
1451
+ children: token.content
1452
+ }
1453
+ );
1454
+ var LineSpan = ({
1455
+ keyedLine,
1456
+ showLineNumbers
1457
+ }) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: showLineNumbers ? LINE_NUMBER_CLASSES : "block", children: keyedLine.tokens.length === 0 ? "\n" : keyedLine.tokens.map(({ token, key }) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TokenSpan, { token }, key)) });
1458
+ var CodeBlockContext = (0, import_react6.createContext)({
1459
+ code: ""
1460
+ });
1461
+ var highlighterCache = /* @__PURE__ */ new Map();
1462
+ var tokensCache = /* @__PURE__ */ new Map();
1463
+ var subscribers = /* @__PURE__ */ new Map();
1464
+ var getTokensCacheKey = (code2, language) => {
1465
+ const start = code2.slice(0, 100);
1466
+ const end = code2.length > 100 ? code2.slice(-100) : "";
1467
+ return `${language}:${code2.length}:${start}:${end}`;
1468
+ };
1469
+ var getHighlighter = (language) => {
1470
+ const cached = highlighterCache.get(language);
1471
+ if (cached) {
1472
+ return cached;
1473
+ }
1474
+ const highlighterPromise = (0, import_shiki.createHighlighter)({
1475
+ themes: ["github-light", "github-dark"],
1476
+ langs: [language]
1477
+ });
1478
+ highlighterCache.set(language, highlighterPromise);
1479
+ return highlighterPromise;
1480
+ };
1481
+ var createRawTokens = (code2) => ({
1482
+ tokens: code2.split("\n").map(
1483
+ (line) => line === "" ? [] : [
1484
+ {
1485
+ content: line,
1486
+ color: "inherit"
1487
+ }
1488
+ ]
1489
+ ),
1490
+ fg: "inherit",
1491
+ bg: "transparent"
1492
+ });
1493
+ function highlightCode(code2, language, callback) {
1494
+ const tokensCacheKey = getTokensCacheKey(code2, language);
1495
+ const cached = tokensCache.get(tokensCacheKey);
1496
+ if (cached) {
1497
+ return cached;
1498
+ }
1499
+ if (callback) {
1500
+ if (!subscribers.has(tokensCacheKey)) {
1501
+ subscribers.set(tokensCacheKey, /* @__PURE__ */ new Set());
1502
+ }
1503
+ subscribers.get(tokensCacheKey)?.add(callback);
1504
+ }
1505
+ getHighlighter(language).then((highlighter) => {
1506
+ const availableLangs = highlighter.getLoadedLanguages();
1507
+ const langToUse = availableLangs.includes(language) ? language : "text";
1508
+ const result = highlighter.codeToTokens(code2, {
1509
+ lang: langToUse,
1510
+ themes: {
1511
+ light: "github-light",
1512
+ dark: "github-dark"
1513
+ }
1514
+ });
1515
+ const tokenized = {
1516
+ tokens: result.tokens,
1517
+ fg: result.fg ?? "inherit",
1518
+ bg: result.bg ?? "transparent"
1519
+ };
1520
+ tokensCache.set(tokensCacheKey, tokenized);
1521
+ const subs = subscribers.get(tokensCacheKey);
1522
+ if (subs) {
1523
+ for (const sub of subs) {
1524
+ sub(tokenized);
1525
+ }
1526
+ subscribers.delete(tokensCacheKey);
1527
+ }
1528
+ }).catch((error) => {
1529
+ console.error("Failed to highlight code:", error);
1530
+ subscribers.delete(tokensCacheKey);
1531
+ });
1532
+ return null;
1533
+ }
1534
+ var LINE_NUMBER_CLASSES = cn(
1535
+ "block",
1536
+ "before:content-[counter(line)]",
1537
+ "before:inline-block",
1538
+ "before:[counter-increment:line]",
1539
+ "before:w-8",
1540
+ "before:mr-4",
1541
+ "before:text-right",
1542
+ "before:text-muted-foreground/50",
1543
+ "before:font-mono",
1544
+ "before:select-none"
1545
+ );
1546
+ var CodeBlockBody = (0, import_react6.memo)(
1547
+ ({
1548
+ tokenized,
1549
+ showLineNumbers,
1550
+ className
1551
+ }) => {
1552
+ const preStyle = (0, import_react6.useMemo)(
1553
+ () => ({
1554
+ backgroundColor: tokenized.bg,
1555
+ color: tokenized.fg
1556
+ }),
1557
+ [tokenized.bg, tokenized.fg]
1558
+ );
1559
+ const keyedLines = (0, import_react6.useMemo)(
1560
+ () => addKeysToTokens(tokenized.tokens),
1561
+ [tokenized.tokens]
1562
+ );
1563
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1564
+ "pre",
1565
+ {
1566
+ className: cn(
1567
+ "dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)] m-0 p-4 text-sm",
1568
+ className
1569
+ ),
1570
+ style: preStyle,
1571
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1572
+ "code",
1573
+ {
1574
+ className: cn(
1575
+ "font-mono text-sm",
1576
+ showLineNumbers && "[counter-increment:line_0] [counter-reset:line]"
1577
+ ),
1578
+ children: keyedLines.map((keyedLine) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1579
+ LineSpan,
1580
+ {
1581
+ keyedLine,
1582
+ showLineNumbers
1583
+ },
1584
+ keyedLine.key
1585
+ ))
1586
+ }
1587
+ )
1588
+ }
1589
+ );
1590
+ },
1591
+ (prevProps, nextProps) => prevProps.tokenized === nextProps.tokenized && prevProps.showLineNumbers === nextProps.showLineNumbers && prevProps.className === nextProps.className
1592
+ );
1593
+ var CodeBlockContainer = ({
1594
+ className,
1595
+ language,
1596
+ style,
1597
+ ...props
1598
+ }) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1599
+ "div",
1600
+ {
1601
+ className: cn(
1602
+ "group relative w-full overflow-hidden rounded-md border bg-background text-foreground",
1603
+ className
1604
+ ),
1605
+ "data-language": language,
1606
+ style: {
1607
+ contentVisibility: "auto",
1608
+ containIntrinsicSize: "auto 200px",
1609
+ ...style
1610
+ },
1611
+ ...props
1612
+ }
1613
+ );
1614
+ var CodeBlockContent = ({
1615
+ code: code2,
1616
+ language,
1617
+ showLineNumbers = false
1618
+ }) => {
1619
+ const rawTokens = (0, import_react6.useMemo)(() => createRawTokens(code2), [code2]);
1620
+ const [tokenized, setTokenized] = (0, import_react6.useState)(
1621
+ () => highlightCode(code2, language) ?? rawTokens
1622
+ );
1623
+ (0, import_react6.useEffect)(() => {
1624
+ setTokenized(highlightCode(code2, language) ?? rawTokens);
1625
+ highlightCode(code2, language, setTokenized);
1626
+ }, [code2, language, rawTokens]);
1627
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "relative overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CodeBlockBody, { showLineNumbers, tokenized }) });
1628
+ };
1629
+ var CodeBlock = ({
1630
+ code: code2,
1631
+ language,
1632
+ showLineNumbers = false,
1633
+ className,
1634
+ children,
1635
+ ...props
1636
+ }) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CodeBlockContext.Provider, { value: { code: code2 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(CodeBlockContainer, { className, language, ...props, children: [
1637
+ children,
1638
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1639
+ CodeBlockContent,
1640
+ {
1641
+ code: code2,
1642
+ language,
1643
+ showLineNumbers
1644
+ }
1645
+ )
1646
+ ] }) });
1647
+
1648
+ // src/components/ai-elements/tool.tsx
1649
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1650
+ var Tool = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1651
+ Collapsible,
1652
+ {
1653
+ className: cn("group not-prose mb-4 w-full rounded-md border", className),
1654
+ ...props
1655
+ }
1656
+ );
1657
+ var getStatusBadge = (status) => {
1658
+ const labels = {
1659
+ "input-streaming": "Pending",
1660
+ "input-available": "Running",
1661
+ "approval-requested": "Awaiting Approval",
1662
+ "approval-responded": "Responded",
1663
+ "output-available": "Completed",
1664
+ "output-error": "Error",
1665
+ "output-denied": "Denied"
1666
+ };
1667
+ const icons = {
1668
+ "input-streaming": /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.CircleIcon, { className: "size-4" }),
1669
+ "input-available": /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.ClockIcon, { className: "size-4 animate-pulse" }),
1670
+ "approval-requested": /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.ClockIcon, { className: "size-4 text-yellow-600" }),
1671
+ "approval-responded": /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.CheckCircleIcon, { className: "size-4 text-blue-600" }),
1672
+ "output-available": /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.CheckCircleIcon, { className: "size-4 text-green-600" }),
1673
+ "output-error": /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.XCircleIcon, { className: "size-4 text-red-600" }),
1674
+ "output-denied": /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.XCircleIcon, { className: "size-4 text-orange-600" })
1675
+ };
1676
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Badge, { className: "gap-1.5 rounded-full text-xs", variant: "secondary", children: [
1677
+ icons[status],
1678
+ labels[status]
1679
+ ] });
1680
+ };
1681
+ var ToolHeader = ({
1682
+ className,
1683
+ title,
1684
+ type,
1685
+ state,
1686
+ toolName,
1687
+ ...props
1688
+ }) => {
1689
+ const derivedName = type === "dynamic-tool" ? toolName : type.split("-").slice(1).join("-");
1690
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1691
+ CollapsibleTrigger,
1692
+ {
1693
+ className: cn(
1694
+ "flex w-full items-center justify-between gap-4 p-3",
1695
+ className
1696
+ ),
1697
+ ...props,
1698
+ children: [
1699
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2", children: [
1700
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.WrenchIcon, { className: "size-4 text-muted-foreground" }),
1701
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "font-medium text-sm", children: title ?? derivedName }),
1702
+ getStatusBadge(state)
1703
+ ] }),
1704
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.ChevronDownIcon, { className: "size-4 text-muted-foreground transition-transform group-data-[state=open]:rotate-180" })
1705
+ ]
1706
+ }
1707
+ );
1708
+ };
1709
+ var ToolContent = ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1710
+ CollapsibleContent,
1711
+ {
1712
+ className: cn(
1713
+ "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 space-y-4 p-4 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
1714
+ className
1715
+ ),
1716
+ ...props
1717
+ }
1718
+ );
1719
+ var ToolInput = ({ className, input, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: cn("space-y-2 overflow-hidden", className), ...props, children: [
1720
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h4", { className: "font-medium text-muted-foreground text-xs uppercase tracking-wide", children: "Parameters" }),
1721
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "rounded-md bg-muted/50", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CodeBlock, { code: JSON.stringify(input, null, 2), language: "json" }) })
1722
+ ] });
1723
+ var ToolOutput = ({
1724
+ className,
1725
+ output,
1726
+ errorText,
1727
+ ...props
1728
+ }) => {
1729
+ if (!(output || errorText)) {
1730
+ return null;
1731
+ }
1732
+ let Output = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { children: output });
1733
+ if (typeof output === "object" && !(0, import_react7.isValidElement)(output)) {
1734
+ Output = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CodeBlock, { code: JSON.stringify(output, null, 2), language: "json" });
1735
+ } else if (typeof output === "string") {
1736
+ Output = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CodeBlock, { code: output, language: "json" });
1737
+ }
1738
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: cn("space-y-2", className), ...props, children: [
1739
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h4", { className: "font-medium text-muted-foreground text-xs uppercase tracking-wide", children: errorText ? "Error" : "Result" }),
1740
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1741
+ "div",
1742
+ {
1743
+ className: cn(
1744
+ "overflow-x-auto rounded-md text-xs [&_table]:w-full",
1745
+ errorText ? "bg-destructive/10 text-destructive" : "bg-muted/50 text-foreground"
1746
+ ),
1747
+ children: [
1748
+ errorText && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { children: errorText }),
1749
+ Output
1750
+ ]
1751
+ }
1752
+ )
1753
+ ] });
1754
+ };
1755
+
1756
+ // src/hooks/use-copy-to-clipboard.ts
1757
+ var import_react8 = require("react");
1758
+ function useCopyToClipboard({
1759
+ timeout = 2e3
1760
+ } = {}) {
1761
+ const [isCopied, setIsCopied] = (0, import_react8.useState)(false);
1762
+ const copyToClipboard = (value) => {
1763
+ if (typeof window === "undefined" || !navigator.clipboard?.writeText) {
1764
+ return;
1765
+ }
1766
+ if (!value) {
1767
+ return;
1768
+ }
1769
+ navigator.clipboard.writeText(value).then(() => {
1770
+ setIsCopied(true);
1771
+ setTimeout(() => {
1772
+ setIsCopied(false);
1773
+ }, timeout);
1774
+ });
1775
+ };
1776
+ return { isCopied, copyToClipboard };
1777
+ }
1778
+
1779
+ // src/components/chat/Message.tsx
1780
+ var import_lucide_react11 = require("lucide-react");
1781
+ var import_react9 = require("react");
1782
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1783
+ var CopyAction = (0, import_react9.memo)(({ content }) => {
1784
+ const { locale } = useLocale("message");
1785
+ const { isCopied, copyToClipboard } = useCopyToClipboard();
1786
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1787
+ MessageAction,
1788
+ {
1789
+ label: locale.copy,
1790
+ onClick: () => copyToClipboard(content),
1791
+ tooltip: isCopied ? locale.copied : locale.copyToClipboard,
1792
+ children: isCopied ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react11.CheckIcon, { className: "size-4" }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react11.CopyIcon, { className: "size-4" })
1793
+ }
1794
+ );
1795
+ });
1796
+ CopyAction.displayName = "CopyAction";
1797
+ var mapToolStatus = (status) => {
1798
+ const statusMap = {
1799
+ pending: "input-streaming",
1800
+ ready: "input-available",
1801
+ executing: "input-available",
1802
+ completed: "output-available",
1803
+ failed: "output-error"
1804
+ };
1805
+ return statusMap[status];
1806
+ };
1807
+ var ToolCallRenderer = (0, import_react9.memo)(
1808
+ ({ toolPart, actions, onRespond }) => {
1809
+ const action = actions?.[toolPart.name];
1810
+ if (action && (action.type === "client-tool" && action.render || action.type === "tool-call")) {
1811
+ const respond = (result) => {
1812
+ onRespond?.(toolPart.toolCallId, result);
1813
+ };
1814
+ const render = action.render;
1815
+ const rendered = render?.({
1816
+ toolCall: toolPart,
1817
+ respond
1818
+ });
1819
+ if (typeof rendered === "string") {
1820
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { children: rendered });
1821
+ }
1822
+ return rendered;
1823
+ }
1824
+ const state = mapToolStatus(toolPart.status);
1825
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tool, { children: [
1826
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1827
+ ToolHeader,
1828
+ {
1829
+ type: "dynamic-tool",
1830
+ state,
1831
+ toolName: toolPart.name
1832
+ }
1833
+ ),
1834
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(ToolContent, { children: [
1835
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolInput, { input: toolPart.args }),
1836
+ (toolPart.status === "completed" || toolPart.status === "failed") && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1837
+ ToolOutput,
1838
+ {
1839
+ output: toolPart.result,
1840
+ errorText: toolPart.error?.message
1841
+ }
1842
+ )
1843
+ ] })
1844
+ ] });
1845
+ }
1846
+ );
1847
+ ToolCallRenderer.displayName = "ToolCallRenderer";
1848
+ function ChatMessage({
1849
+ messages,
1850
+ isLoading = false,
1851
+ actions,
1852
+ onRespond
1853
+ }) {
1854
+ const { locale } = useLocale("message");
1855
+ const visibleMessages = messages.filter(
1856
+ (message) => message.role === "user" || message.role === "assistant"
1857
+ );
1858
+ const lastVisibleMessage = visibleMessages[visibleMessages.length - 1];
1859
+ const shouldRenderAssistantPlaceholder = isLoading && (!lastVisibleMessage || lastVisibleMessage.role === "user");
1860
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col gap-4", children: [
1861
+ visibleMessages.map((message) => {
1862
+ const role = message.role;
1863
+ const isLoadingAssistantMessage = isLoading && role === "assistant" && message.id === lastVisibleMessage?.id;
1864
+ const allTextContent = message.parts.filter((part) => part.type === "text").map((part) => part.text).join("");
1865
+ const hasAssistantText = role === "assistant" && allTextContent.trim().length > 0;
1866
+ const isPausedAssistantNoText = role === "assistant" && Boolean(message.paused) && !hasAssistantText;
1867
+ const copyContent = allTextContent || (isPausedAssistantNoText ? locale.paused : "");
1868
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Message, { from: role, children: [
1869
+ message.parts.map((part) => {
1870
+ if (part.type === "text") {
1871
+ const textPart = part;
1872
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MessageContent, { children: role === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MessageResponse, { children: textPart.text }) : textPart.text }, textPart.id);
1873
+ }
1874
+ if (part.type === "tool") {
1875
+ const toolPart = part;
1876
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1877
+ ToolCallRenderer,
1878
+ {
1879
+ toolPart,
1880
+ actions,
1881
+ onRespond
1882
+ },
1883
+ toolPart.id
1884
+ );
1885
+ }
1886
+ return null;
1887
+ }),
1888
+ isLoadingAssistantMessage && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MessageContent, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "inline-flex items-center text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SvgSpinners3DotsFade, { className: "text-xl" }) }) }),
1889
+ isPausedAssistantNoText && !isLoadingAssistantMessage && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MessageContent, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "text-muted-foreground", children: locale.paused }) }),
1890
+ role === "assistant" && copyContent && !isLoadingAssistantMessage && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MessageActions, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CopyAction, { content: copyContent }) })
1891
+ ] }, message.id);
1892
+ }),
1893
+ shouldRenderAssistantPlaceholder && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Message, { from: "assistant", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MessageContent, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "inline-flex items-center text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SvgSpinners3DotsFade, { className: "text-xl" }) }) }) })
1894
+ ] });
1895
+ }
1896
+ function SvgSpinners3DotsFade(props) {
1897
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
1898
+ "svg",
1899
+ {
1900
+ xmlns: "http://www.w3.org/2000/svg",
1901
+ width: "1em",
1902
+ height: "1em",
1903
+ viewBox: "0 0 24 24",
1904
+ ...props,
1905
+ children: [
1906
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "4", cy: "12", r: "3", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1907
+ "animate",
1908
+ {
1909
+ id: "dotFadeFirst",
1910
+ fill: "freeze",
1911
+ attributeName: "opacity",
1912
+ begin: "0;dotFadeThird.end-0.25s",
1913
+ dur: "0.75s",
1914
+ values: "1;.2"
1915
+ }
1916
+ ) }),
1917
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "12", cy: "12", r: "3", fill: "currentColor", opacity: ".4", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1918
+ "animate",
1919
+ {
1920
+ fill: "freeze",
1921
+ attributeName: "opacity",
1922
+ begin: "dotFadeFirst.begin+0.15s",
1923
+ dur: "0.75s",
1924
+ values: "1;.2"
1925
+ }
1926
+ ) }),
1927
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "20", cy: "12", r: "3", fill: "currentColor", opacity: ".3", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1928
+ "animate",
1929
+ {
1930
+ id: "dotFadeThird",
1931
+ fill: "freeze",
1932
+ attributeName: "opacity",
1933
+ begin: "dotFadeFirst.begin+0.3s",
1934
+ dur: "0.75s",
1935
+ values: "1;.2"
1936
+ }
1937
+ ) })
1938
+ ]
1939
+ }
1940
+ );
1941
+ }
1942
+
1943
+ // src/components/chat/Chat.tsx
1944
+ var import_uuid = require("uuid");
1945
+ var import_react10 = require("react");
1946
+
1947
+ // src/components/ui/scroll-area.tsx
1948
+ var import_radix_ui11 = require("radix-ui");
1949
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1950
+ function ScrollArea({
1951
+ className,
1952
+ children,
1953
+ ...props
1954
+ }) {
1955
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
1956
+ import_radix_ui11.ScrollArea.Root,
1957
+ {
1958
+ "data-slot": "scroll-area",
1959
+ className: cn("relative", className),
1960
+ ...props,
1961
+ children: [
1962
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1963
+ import_radix_ui11.ScrollArea.Viewport,
1964
+ {
1965
+ "data-slot": "scroll-area-viewport",
1966
+ className: "focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1",
1967
+ children
1968
+ }
1969
+ ),
1970
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ScrollBar, {}),
1971
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_radix_ui11.ScrollArea.Corner, {})
1972
+ ]
1973
+ }
1974
+ );
1975
+ }
1976
+ function ScrollBar({
1977
+ className,
1978
+ orientation = "vertical",
1979
+ ...props
1980
+ }) {
1981
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1982
+ import_radix_ui11.ScrollArea.ScrollAreaScrollbar,
1983
+ {
1984
+ "data-slot": "scroll-area-scrollbar",
1985
+ orientation,
1986
+ className: cn(
1987
+ "flex touch-none p-px transition-colors select-none",
1988
+ orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent",
1989
+ orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent",
1990
+ className
1991
+ ),
1992
+ ...props,
1993
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1994
+ import_radix_ui11.ScrollArea.ScrollAreaThumb,
1995
+ {
1996
+ "data-slot": "scroll-area-thumb",
1997
+ className: "bg-border relative flex-1 rounded-full"
1998
+ }
1999
+ )
2000
+ }
2001
+ );
2002
+ }
2003
+
2004
+ // src/components/ai-elements/suggestion.tsx
2005
+ var import_jsx_runtime24 = require("react/jsx-runtime");
2006
+ var Suggestions = ({
2007
+ className,
2008
+ children,
2009
+ ...props
2010
+ }) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(ScrollArea, { className: "w-full overflow-x-auto whitespace-nowrap", ...props, children: [
2011
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("flex w-max flex-nowrap items-center gap-2", className), children }),
2012
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ScrollBar, { className: "hidden", orientation: "horizontal" })
2013
+ ] });
2014
+ var Suggestion = ({
2015
+ suggestion,
2016
+ onClick,
2017
+ className,
2018
+ variant = "outline",
2019
+ size = "sm",
2020
+ children,
2021
+ ...props
2022
+ }) => {
2023
+ const handleClick = () => {
2024
+ onClick?.(suggestion);
2025
+ };
2026
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2027
+ Button,
2028
+ {
2029
+ className: cn("cursor-pointer rounded-full px-4", className),
2030
+ onClick: handleClick,
2031
+ size,
2032
+ type: "button",
2033
+ variant,
2034
+ ...props,
2035
+ children: children || suggestion
2036
+ }
2037
+ );
2038
+ };
2039
+
2040
+ // src/components/chat/Chat.tsx
2041
+ var import_jsx_runtime25 = require("react/jsx-runtime");
2042
+ function AgKitUI({
2043
+ className = "h-full min-h-0 mx-auto max-w-225 flex flex-col overflow-hidden px-4",
2044
+ containerClassName,
2045
+ messagesClassName,
2046
+ inputClassName,
2047
+ emptyTitleClassName,
2048
+ inputPlaceholder,
2049
+ suggestions,
2050
+ onSuggestionClick,
2051
+ locale = "zh-CN"
2052
+ }) {
2053
+ const contextValue = (0, import_react10.useMemo)(() => ({ locale }), [locale]);
2054
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(LocaleContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2055
+ AgKitUIContent,
2056
+ {
2057
+ className,
2058
+ containerClassName,
2059
+ messagesClassName,
2060
+ inputClassName,
2061
+ emptyTitleClassName,
2062
+ inputPlaceholder,
2063
+ suggestions,
2064
+ onSuggestionClick
2065
+ }
2066
+ ) });
2067
+ }
2068
+ function AgKitUIContent({
2069
+ className,
2070
+ containerClassName,
2071
+ messagesClassName,
2072
+ inputClassName,
2073
+ emptyTitleClassName,
2074
+ inputPlaceholder,
2075
+ suggestions,
2076
+ onSuggestionClick
2077
+ }) {
2078
+ const { locale } = useLocale("chat");
2079
+ const { uiMessages, streaming, sendMessage, actions, abort } = (0, import_agent_react_core.useChat)();
2080
+ const handleRespond = (toolCallId, result) => {
2081
+ sendMessage({
2082
+ id: (0, import_uuid.v4)(),
2083
+ role: "tool",
2084
+ content: result,
2085
+ toolCallId
2086
+ });
2087
+ };
2088
+ const visibleMessages = uiMessages.filter(
2089
+ (message) => message.role === "user" || message.role === "assistant"
2090
+ );
2091
+ const isEmpty = visibleMessages.length === 0;
2092
+ const inputNode = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: inputClassName, children: [
2093
+ isEmpty && suggestions && suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Suggestions, { className: "mb-3", children: suggestions.map((suggestion) => suggestion.trim()).filter(Boolean).map((suggestion, index) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2094
+ Suggestion,
2095
+ {
2096
+ suggestion,
2097
+ variant: "ghost",
2098
+ className: "max-w-full border border-border/80 text-muted-foreground",
2099
+ disabled: streaming,
2100
+ onClick: (value) => {
2101
+ onSuggestionClick?.(value);
2102
+ sendMessage(value);
2103
+ }
2104
+ },
2105
+ `${suggestion}-${index}`
2106
+ )) }),
2107
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2108
+ Input2,
2109
+ {
2110
+ placeholder: inputPlaceholder,
2111
+ streaming,
2112
+ onStop: () => {
2113
+ if (!streaming) return;
2114
+ abort();
2115
+ },
2116
+ onSend: (message) => {
2117
+ sendMessage(message.text);
2118
+ }
2119
+ }
2120
+ )
2121
+ ] });
2122
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2123
+ "div",
2124
+ {
2125
+ className: cn(
2126
+ "bg-background text-foreground h-full min-h-0 w-full",
2127
+ containerClassName
2128
+ ),
2129
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: cn(className), children: isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex min-h-0 flex-1 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "w-full", children: [
2130
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2131
+ "div",
2132
+ {
2133
+ className: cn(
2134
+ "mb-5 text-center text-xl font-medium text-foreground",
2135
+ emptyTitleClassName
2136
+ ),
2137
+ children: locale.emptyTitle
2138
+ }
2139
+ ),
2140
+ inputNode
2141
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
2142
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2143
+ "div",
2144
+ {
2145
+ className: cn(
2146
+ "min-h-0 flex-1 overflow-y-auto py-4",
2147
+ messagesClassName
2148
+ ),
2149
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2150
+ ChatMessage,
2151
+ {
2152
+ messages: uiMessages,
2153
+ isLoading: streaming,
2154
+ actions,
2155
+ onRespond: handleRespond
2156
+ }
2157
+ )
2158
+ }
2159
+ ),
2160
+ inputNode
2161
+ ] }) })
2162
+ }
2163
+ );
2164
+ }
2165
+ // Annotate the CommonJS export names for ESM import in node:
2166
+ 0 && (module.exports = {
2167
+ AgKitUI
2168
+ });
2169
+ //# sourceMappingURL=index.js.map