@uix-ai/agent 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2653 @@
1
+ // src/components/Avatar.tsx
2
+ import * as React from "react";
3
+
4
+ // src/utils.ts
5
+ import { clsx } from "clsx";
6
+ import { twMerge } from "tailwind-merge";
7
+ function cn(...inputs) {
8
+ return twMerge(clsx(inputs));
9
+ }
10
+ function throttle(fn, waitMs) {
11
+ let lastCallTime = 0;
12
+ let timeoutId = null;
13
+ let lastArgs = null;
14
+ const throttled = (...args) => {
15
+ const now = Date.now();
16
+ const remaining = waitMs - (now - lastCallTime);
17
+ lastArgs = args;
18
+ if (remaining <= 0) {
19
+ if (timeoutId) {
20
+ clearTimeout(timeoutId);
21
+ timeoutId = null;
22
+ }
23
+ lastCallTime = now;
24
+ fn(...args);
25
+ } else if (!timeoutId) {
26
+ timeoutId = setTimeout(() => {
27
+ lastCallTime = Date.now();
28
+ timeoutId = null;
29
+ if (lastArgs) {
30
+ fn(...lastArgs);
31
+ }
32
+ }, remaining);
33
+ }
34
+ };
35
+ return throttled;
36
+ }
37
+ function formatRelativeTime(date) {
38
+ const d = typeof date === "string" ? new Date(date) : date;
39
+ const now = /* @__PURE__ */ new Date();
40
+ const diff = now.getTime() - d.getTime();
41
+ const minutes = Math.floor(diff / 6e4);
42
+ const hours = Math.floor(diff / 36e5);
43
+ const days = Math.floor(diff / 864e5);
44
+ if (minutes < 1) return "\u521A\u521A";
45
+ if (minutes < 60) return `${minutes}\u5206\u949F\u524D`;
46
+ if (hours < 24) return `${hours}\u5C0F\u65F6\u524D`;
47
+ if (days < 7) return `${days}\u5929\u524D`;
48
+ return d.toLocaleDateString("zh-CN", { month: "short", day: "numeric" });
49
+ }
50
+
51
+ // src/components/Avatar.tsx
52
+ import { jsx, jsxs } from "react/jsx-runtime";
53
+ var AvatarContext = React.createContext(null);
54
+ function useAvatarContext() {
55
+ const context = React.useContext(AvatarContext);
56
+ if (!context) {
57
+ throw new Error("Avatar components must be used within <Avatar>");
58
+ }
59
+ return context;
60
+ }
61
+ var sizeClasses = {
62
+ xs: "w-5 h-5 text-[10px]",
63
+ sm: "w-6 h-6 text-xs",
64
+ md: "w-8 h-8 text-sm",
65
+ lg: "w-10 h-10 text-base",
66
+ xl: "w-12 h-12 text-lg"
67
+ };
68
+ var statusIndicatorSizes = {
69
+ xs: "w-1.5 h-1.5",
70
+ sm: "w-2 h-2",
71
+ md: "w-2.5 h-2.5",
72
+ lg: "w-3 h-3",
73
+ xl: "w-3.5 h-3.5"
74
+ };
75
+ var variantClasses = {
76
+ primary: "bg-primary-500 text-white",
77
+ secondary: "bg-secondary-500 text-white",
78
+ neutral: "bg-gray-200 text-gray-600",
79
+ success: "bg-green-500 text-white",
80
+ warning: "bg-amber-500 text-white",
81
+ error: "bg-red-500 text-white"
82
+ };
83
+ var roleToVariant = {
84
+ user: "primary",
85
+ assistant: "secondary",
86
+ system: "neutral",
87
+ tool: "warning",
88
+ error: "error"
89
+ };
90
+ var roleSymbols = {
91
+ user: "U",
92
+ assistant: "A",
93
+ system: "S",
94
+ tool: "T",
95
+ error: "!"
96
+ };
97
+ var presenceColors = {
98
+ online: "bg-green-500",
99
+ offline: "bg-gray-400",
100
+ busy: "bg-amber-500"
101
+ };
102
+ var Avatar = React.forwardRef(
103
+ ({ size = "md", status = "idle", role, className, children, ...props }, ref) => {
104
+ const [imageStatus, setImageStatus] = React.useState("idle");
105
+ return /* @__PURE__ */ jsx(AvatarContext.Provider, { value: { size, role, imageStatus, setImageStatus }, children: /* @__PURE__ */ jsxs(
106
+ "div",
107
+ {
108
+ ref,
109
+ className: cn(
110
+ "relative inline-flex items-center justify-center rounded-full font-medium shrink-0",
111
+ "overflow-hidden",
112
+ sizeClasses[size],
113
+ className
114
+ ),
115
+ role: "img",
116
+ "aria-busy": status !== "idle",
117
+ ...props,
118
+ children: [
119
+ children,
120
+ status === "thinking" && /* @__PURE__ */ jsx(
121
+ "span",
122
+ {
123
+ className: "absolute inset-0 rounded-full animate-pulse bg-current opacity-20",
124
+ "aria-hidden": "true"
125
+ }
126
+ ),
127
+ status === "planning" && /* @__PURE__ */ jsx(
128
+ "span",
129
+ {
130
+ className: "absolute inset-0 rounded-full animate-breath bg-current opacity-15",
131
+ "aria-hidden": "true"
132
+ }
133
+ ),
134
+ status === "responding" && /* @__PURE__ */ jsx(
135
+ "span",
136
+ {
137
+ className: "absolute inset-0 rounded-full animate-ping bg-current opacity-20",
138
+ "aria-hidden": "true"
139
+ }
140
+ ),
141
+ status === "tool-calling" && /* @__PURE__ */ jsx(
142
+ "span",
143
+ {
144
+ className: "absolute inset-0 rounded-full border-2 border-transparent border-t-current animate-spin opacity-60",
145
+ "aria-hidden": "true"
146
+ }
147
+ )
148
+ ]
149
+ }
150
+ ) });
151
+ }
152
+ );
153
+ Avatar.displayName = "Avatar";
154
+ var AvatarImage = React.forwardRef(
155
+ ({ src, alt, onLoadingStatusChange, className, ...props }, ref) => {
156
+ const { setImageStatus } = useAvatarContext();
157
+ React.useEffect(() => {
158
+ if (!src) {
159
+ setImageStatus("idle");
160
+ onLoadingStatusChange?.("idle");
161
+ return;
162
+ }
163
+ setImageStatus("loading");
164
+ onLoadingStatusChange?.("loading");
165
+ const img = new Image();
166
+ img.onload = () => {
167
+ setImageStatus("loaded");
168
+ onLoadingStatusChange?.("loaded");
169
+ };
170
+ img.onerror = () => {
171
+ setImageStatus("error");
172
+ onLoadingStatusChange?.("error");
173
+ };
174
+ img.src = src;
175
+ return () => {
176
+ img.onload = null;
177
+ img.onerror = null;
178
+ };
179
+ }, [src, setImageStatus, onLoadingStatusChange]);
180
+ const { imageStatus } = useAvatarContext();
181
+ if (!src || imageStatus !== "loaded") {
182
+ return null;
183
+ }
184
+ return /* @__PURE__ */ jsx(
185
+ "img",
186
+ {
187
+ ref,
188
+ src,
189
+ alt,
190
+ className: cn("w-full h-full object-cover", className),
191
+ ...props
192
+ }
193
+ );
194
+ }
195
+ );
196
+ AvatarImage.displayName = "AvatarImage";
197
+ var AvatarFallback = React.forwardRef(
198
+ ({ variant, delayMs = 0, className, children, ...props }, ref) => {
199
+ const { role, imageStatus } = useAvatarContext();
200
+ const [canRender, setCanRender] = React.useState(delayMs === 0);
201
+ const resolvedVariant = variant || (role ? roleToVariant[role] : "neutral");
202
+ const fallbackContent = children || (role ? roleSymbols[role] : null);
203
+ React.useEffect(() => {
204
+ if (delayMs === 0) {
205
+ setCanRender(true);
206
+ return;
207
+ }
208
+ const timer = setTimeout(() => {
209
+ setCanRender(true);
210
+ }, delayMs);
211
+ return () => clearTimeout(timer);
212
+ }, [delayMs]);
213
+ if (imageStatus === "loaded" || !canRender) {
214
+ return null;
215
+ }
216
+ return /* @__PURE__ */ jsx(
217
+ "div",
218
+ {
219
+ ref,
220
+ className: cn(
221
+ "w-full h-full flex items-center justify-center",
222
+ variantClasses[resolvedVariant],
223
+ className
224
+ ),
225
+ ...props,
226
+ children: fallbackContent
227
+ }
228
+ );
229
+ }
230
+ );
231
+ AvatarFallback.displayName = "AvatarFallback";
232
+ var AvatarStatusIndicator = React.forwardRef(
233
+ ({ status, className, ...props }, ref) => {
234
+ const { size } = useAvatarContext();
235
+ return /* @__PURE__ */ jsx(
236
+ "span",
237
+ {
238
+ ref,
239
+ className: cn(
240
+ "absolute bottom-0 right-0 rounded-full border-2 border-white",
241
+ presenceColors[status],
242
+ statusIndicatorSizes[size],
243
+ className
244
+ ),
245
+ "aria-label": status,
246
+ ...props
247
+ }
248
+ );
249
+ }
250
+ );
251
+ AvatarStatusIndicator.displayName = "AvatarStatusIndicator";
252
+
253
+ // src/components/AgentAvatarGroup.tsx
254
+ import * as React2 from "react";
255
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
256
+ var overlapClasses = {
257
+ xs: "-ml-1.5",
258
+ sm: "-ml-2",
259
+ md: "-ml-3",
260
+ lg: "-ml-4",
261
+ xl: "-ml-5"
262
+ };
263
+ var AvatarGroup = React2.forwardRef(
264
+ ({ max = 4, size = "sm", direction = "left", className, children, ...props }, ref) => {
265
+ const childArray = React2.Children.toArray(children);
266
+ const visibleChildren = childArray.slice(0, max);
267
+ const overflowCount = childArray.length - max;
268
+ return /* @__PURE__ */ jsxs2(
269
+ "div",
270
+ {
271
+ ref,
272
+ className: cn(
273
+ "flex items-center",
274
+ direction === "right" && "flex-row-reverse",
275
+ className
276
+ ),
277
+ role: "group",
278
+ "aria-label": `${childArray.length} items`,
279
+ ...props,
280
+ children: [
281
+ visibleChildren.map((child, index) => {
282
+ const isFirst = index === 0;
283
+ const clonedChild = React2.isValidElement(child) ? React2.cloneElement(child, {
284
+ size,
285
+ className: cn(
286
+ child.props.className,
287
+ "border-2 border-white transition-transform hover:z-10 hover:scale-110"
288
+ )
289
+ }) : child;
290
+ return /* @__PURE__ */ jsx2(
291
+ "div",
292
+ {
293
+ className: cn(
294
+ "relative",
295
+ !isFirst && (direction === "left" ? overlapClasses[size] : "-mr-2")
296
+ ),
297
+ style: { zIndex: visibleChildren.length - index },
298
+ children: clonedChild
299
+ },
300
+ index
301
+ );
302
+ }),
303
+ overflowCount > 0 && /* @__PURE__ */ jsx2(
304
+ Avatar,
305
+ {
306
+ size,
307
+ className: cn(
308
+ "border-2 border-white",
309
+ direction === "left" ? overlapClasses[size] : "-mr-2"
310
+ ),
311
+ style: { zIndex: 0 },
312
+ "aria-label": `${overflowCount} more`,
313
+ children: /* @__PURE__ */ jsxs2(AvatarFallback, { variant: "neutral", children: [
314
+ "+",
315
+ overflowCount
316
+ ] })
317
+ }
318
+ )
319
+ ]
320
+ }
321
+ );
322
+ }
323
+ );
324
+ AvatarGroup.displayName = "AvatarGroup";
325
+
326
+ // src/components/AgentAvatar.tsx
327
+ import * as React3 from "react";
328
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
329
+ var sizeClasses2 = {
330
+ sm: "w-8 h-8 text-xs",
331
+ md: "w-10 h-10 text-sm",
332
+ lg: "w-12 h-12 text-base"
333
+ };
334
+ var variantClasses2 = {
335
+ primary: "bg-primary-100 text-primary-700",
336
+ secondary: "bg-secondary-100 text-secondary-700",
337
+ success: "bg-green-100 text-green-700",
338
+ warning: "bg-amber-100 text-amber-700",
339
+ error: "bg-red-100 text-red-700",
340
+ info: "bg-blue-100 text-blue-700",
341
+ neutral: "bg-gray-100 text-gray-700"
342
+ };
343
+ var statusColors = {
344
+ online: "bg-green-500",
345
+ offline: "bg-gray-400",
346
+ busy: "bg-amber-500"
347
+ };
348
+ var statusSizes = {
349
+ sm: "w-2.5 h-2.5",
350
+ md: "w-3 h-3",
351
+ lg: "w-3.5 h-3.5"
352
+ };
353
+ function useImageLoadingStatus(src, onStatusChange) {
354
+ const [status, setStatus] = React3.useState("idle");
355
+ React3.useEffect(() => {
356
+ if (!src) {
357
+ setStatus("idle");
358
+ onStatusChange?.("idle");
359
+ return;
360
+ }
361
+ setStatus("loading");
362
+ onStatusChange?.("loading");
363
+ const img = new Image();
364
+ img.onload = () => {
365
+ setStatus("loaded");
366
+ onStatusChange?.("loaded");
367
+ };
368
+ img.onerror = () => {
369
+ setStatus("error");
370
+ onStatusChange?.("error");
371
+ };
372
+ img.src = src;
373
+ return () => {
374
+ img.onload = null;
375
+ img.onerror = null;
376
+ };
377
+ }, [src, onStatusChange]);
378
+ return status;
379
+ }
380
+ var AgentAvatar = React3.forwardRef(
381
+ ({
382
+ src,
383
+ name,
384
+ icon,
385
+ variant = "neutral",
386
+ status,
387
+ size = "md",
388
+ onLoadingStatusChange,
389
+ fallbackDelayMs = 0,
390
+ className
391
+ }, ref) => {
392
+ const imageStatus = useImageLoadingStatus(src, onLoadingStatusChange);
393
+ const [showFallback, setShowFallback] = React3.useState(!src);
394
+ React3.useEffect(() => {
395
+ if (!src) {
396
+ setShowFallback(true);
397
+ return;
398
+ }
399
+ if (imageStatus === "loaded") {
400
+ setShowFallback(false);
401
+ return;
402
+ }
403
+ if (imageStatus === "error") {
404
+ setShowFallback(true);
405
+ return;
406
+ }
407
+ if (fallbackDelayMs > 0) {
408
+ setShowFallback(false);
409
+ const timer = setTimeout(() => {
410
+ if (imageStatus === "loading") {
411
+ setShowFallback(true);
412
+ }
413
+ }, fallbackDelayMs);
414
+ return () => clearTimeout(timer);
415
+ } else {
416
+ setShowFallback(true);
417
+ }
418
+ }, [src, imageStatus, fallbackDelayMs]);
419
+ const initials = name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2);
420
+ const showImage = src && imageStatus === "loaded";
421
+ return /* @__PURE__ */ jsxs3("div", { ref, className: cn("relative inline-block shrink-0", className), children: [
422
+ src && /* @__PURE__ */ jsx3(
423
+ "img",
424
+ {
425
+ src,
426
+ alt: name,
427
+ className: cn(
428
+ "rounded-full object-cover",
429
+ sizeClasses2[size],
430
+ showImage ? "opacity-100" : "opacity-0 absolute inset-0"
431
+ )
432
+ }
433
+ ),
434
+ (!src || showFallback) && !showImage && /* @__PURE__ */ jsx3(
435
+ "div",
436
+ {
437
+ className: cn(
438
+ "rounded-full flex items-center justify-center font-medium",
439
+ variantClasses2[variant],
440
+ sizeClasses2[size]
441
+ ),
442
+ role: "img",
443
+ "aria-label": name,
444
+ children: icon || initials
445
+ }
446
+ ),
447
+ status && /* @__PURE__ */ jsx3(
448
+ "span",
449
+ {
450
+ className: cn(
451
+ "absolute bottom-0 right-0 rounded-full border-2 border-white",
452
+ statusColors[status],
453
+ statusSizes[size]
454
+ ),
455
+ "aria-label": status
456
+ }
457
+ )
458
+ ] });
459
+ }
460
+ );
461
+ AgentAvatar.displayName = "AgentAvatar";
462
+
463
+ // src/components/MessageAvatar.tsx
464
+ import * as React4 from "react";
465
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
466
+ var roleSymbols2 = {
467
+ user: "U",
468
+ assistant: "A",
469
+ system: "S",
470
+ tool: "T",
471
+ error: "!"
472
+ };
473
+ var roleColors = {
474
+ user: "bg-primary-500 text-white",
475
+ assistant: "bg-secondary-500 text-white",
476
+ system: "bg-gray-200 text-gray-600",
477
+ tool: "bg-orange-500 text-white",
478
+ error: "bg-red-500 text-white"
479
+ };
480
+ var sizeClasses3 = {
481
+ sm: "w-6 h-6 text-xs",
482
+ md: "w-8 h-8 text-sm",
483
+ lg: "w-10 h-10 text-base"
484
+ };
485
+ var statusAnimations = {
486
+ idle: "",
487
+ thinking: "animate-pulse",
488
+ responding: "animate-breath",
489
+ "tool-calling": ""
490
+ };
491
+ var MessageAvatar = React4.forwardRef(
492
+ ({ role, status = "idle", src, name, icon, size = "md", className }, ref) => {
493
+ const symbol = roleSymbols2[role];
494
+ const displayText = name ? name.charAt(0).toUpperCase() : symbol;
495
+ const isToolCalling = status === "tool-calling";
496
+ return /* @__PURE__ */ jsxs4(
497
+ "div",
498
+ {
499
+ ref,
500
+ className: cn(
501
+ "relative inline-flex items-center justify-center rounded-full font-medium shrink-0",
502
+ roleColors[role],
503
+ sizeClasses3[size],
504
+ statusAnimations[status],
505
+ className
506
+ ),
507
+ role: "img",
508
+ "aria-label": name || role,
509
+ "aria-busy": status !== "idle",
510
+ children: [
511
+ src ? /* @__PURE__ */ jsx4(
512
+ "img",
513
+ {
514
+ src,
515
+ alt: name || role,
516
+ className: "w-full h-full rounded-full object-cover"
517
+ }
518
+ ) : icon ? /* @__PURE__ */ jsx4("span", { className: "flex items-center justify-center", children: icon }) : /* @__PURE__ */ jsx4("span", { children: displayText }),
519
+ isToolCalling && /* @__PURE__ */ jsx4(
520
+ "span",
521
+ {
522
+ className: cn(
523
+ "absolute inset-0 rounded-full border-2 border-transparent",
524
+ "border-t-white/80 animate-spin"
525
+ ),
526
+ "aria-hidden": "true"
527
+ }
528
+ ),
529
+ status === "responding" && /* @__PURE__ */ jsx4(
530
+ "span",
531
+ {
532
+ className: cn(
533
+ "absolute inset-0 rounded-full",
534
+ "animate-ping opacity-30",
535
+ roleColors[role]
536
+ ),
537
+ "aria-hidden": "true"
538
+ }
539
+ )
540
+ ]
541
+ }
542
+ );
543
+ }
544
+ );
545
+ MessageAvatar.displayName = "MessageAvatar";
546
+
547
+ // src/components/ChatBubble.tsx
548
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
549
+ function ChatBubble({
550
+ role,
551
+ avatar,
552
+ name,
553
+ children,
554
+ timestamp,
555
+ className
556
+ }) {
557
+ const isUser = role === "user";
558
+ const isSystem = role === "system";
559
+ if (isSystem) {
560
+ return /* @__PURE__ */ jsx5("div", { className: cn("flex justify-center my-4", className), children: /* @__PURE__ */ jsx5("div", { className: "text-sm text-gray-500 bg-gray-50 px-4 py-2 rounded-full", children }) });
561
+ }
562
+ const formattedTime = timestamp ? typeof timestamp === "string" ? timestamp : timestamp.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : null;
563
+ return /* @__PURE__ */ jsxs5(
564
+ "div",
565
+ {
566
+ className: cn(
567
+ "flex gap-3 my-4",
568
+ isUser ? "flex-row-reverse" : "flex-row",
569
+ className
570
+ ),
571
+ children: [
572
+ !isUser && /* @__PURE__ */ jsx5("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx5(
573
+ AgentAvatar,
574
+ {
575
+ src: avatar,
576
+ name: name || "AI",
577
+ size: "md"
578
+ }
579
+ ) }),
580
+ /* @__PURE__ */ jsxs5(
581
+ "div",
582
+ {
583
+ className: cn(
584
+ "flex flex-col max-w-[70%]",
585
+ isUser ? "items-end" : "items-start"
586
+ ),
587
+ children: [
588
+ name && !isUser && /* @__PURE__ */ jsx5("span", { className: "text-sm font-medium text-gray-900 mb-1", children: name }),
589
+ /* @__PURE__ */ jsx5(
590
+ "div",
591
+ {
592
+ className: cn(
593
+ "px-4 py-3 rounded-2xl",
594
+ isUser ? "bg-primary-500 text-white rounded-br-sm" : "bg-gray-100 text-gray-900 rounded-bl-sm"
595
+ ),
596
+ children
597
+ }
598
+ ),
599
+ formattedTime && /* @__PURE__ */ jsx5("span", { className: "text-xs text-gray-400 mt-1", children: formattedTime })
600
+ ]
601
+ }
602
+ )
603
+ ]
604
+ }
605
+ );
606
+ }
607
+
608
+ // src/components/ChatInput.tsx
609
+ import * as React5 from "react";
610
+ import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
611
+ var ChatInput = React5.forwardRef(
612
+ ({ className, onSubmit, ...props }, ref) => {
613
+ const handleSubmit = (e) => {
614
+ e.preventDefault();
615
+ onSubmit?.(e);
616
+ };
617
+ return /* @__PURE__ */ jsx6(
618
+ "form",
619
+ {
620
+ ref,
621
+ onSubmit: handleSubmit,
622
+ className: cn(
623
+ "w-full overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",
624
+ "focus-within:border-primary-500 focus-within:ring-1 focus-within:ring-primary-500",
625
+ "transition-shadow",
626
+ className
627
+ ),
628
+ ...props
629
+ }
630
+ );
631
+ }
632
+ );
633
+ ChatInput.displayName = "ChatInput";
634
+ var ChatInputTextarea = React5.forwardRef(
635
+ ({
636
+ className,
637
+ value,
638
+ onChange,
639
+ placeholder = "Type a message...",
640
+ minHeight = 48,
641
+ maxHeight = 200,
642
+ mentionTrigger = "@",
643
+ onMentionQuery,
644
+ mentionItems = [],
645
+ onMentionSelect,
646
+ onKeyDown,
647
+ ...props
648
+ }, ref) => {
649
+ const innerRef = React5.useRef(null);
650
+ React5.useImperativeHandle(ref, () => innerRef.current);
651
+ const [showMentions, setShowMentions] = React5.useState(false);
652
+ const [mentionQuery, setMentionQuery] = React5.useState("");
653
+ const [mentionIndex, setMentionIndex] = React5.useState(0);
654
+ const [mentionPosition, setMentionPosition] = React5.useState(null);
655
+ const mentionStartRef = React5.useRef(null);
656
+ const filteredMentions = React5.useMemo(() => {
657
+ if (onMentionQuery) {
658
+ return onMentionQuery(mentionQuery);
659
+ }
660
+ if (!mentionQuery) return mentionItems.slice(0, 8);
661
+ const query = mentionQuery.toLowerCase();
662
+ return mentionItems.filter(
663
+ (item) => item.name.toLowerCase().includes(query) || item.description?.toLowerCase().includes(query)
664
+ ).slice(0, 8);
665
+ }, [mentionQuery, mentionItems, onMentionQuery]);
666
+ React5.useEffect(() => {
667
+ const textarea = innerRef.current;
668
+ if (textarea) {
669
+ textarea.style.height = "auto";
670
+ const newHeight = Math.min(Math.max(textarea.scrollHeight, minHeight), maxHeight);
671
+ textarea.style.height = `${newHeight}px`;
672
+ }
673
+ }, [value, minHeight, maxHeight]);
674
+ const handleChange = (e) => {
675
+ const newValue = e.target.value;
676
+ const selectionStart = e.target.selectionStart;
677
+ onChange?.(newValue);
678
+ const textBeforeCursor = newValue.slice(0, selectionStart);
679
+ const lastTriggerIndex = textBeforeCursor.lastIndexOf(mentionTrigger);
680
+ if (lastTriggerIndex !== -1) {
681
+ const textAfterTrigger = textBeforeCursor.slice(lastTriggerIndex + 1);
682
+ if (!textAfterTrigger.includes(" ") && textAfterTrigger.length <= 20) {
683
+ mentionStartRef.current = lastTriggerIndex;
684
+ setMentionQuery(textAfterTrigger);
685
+ setShowMentions(true);
686
+ setMentionIndex(0);
687
+ if (innerRef.current) {
688
+ const rect = innerRef.current.getBoundingClientRect();
689
+ setMentionPosition({
690
+ top: rect.bottom + 4,
691
+ left: rect.left
692
+ });
693
+ }
694
+ return;
695
+ }
696
+ }
697
+ setShowMentions(false);
698
+ mentionStartRef.current = null;
699
+ };
700
+ const selectMention = React5.useCallback(
701
+ (item) => {
702
+ if (mentionStartRef.current === null || value === void 0) return;
703
+ const before = value.slice(0, mentionStartRef.current);
704
+ const after = value.slice(mentionStartRef.current + mentionQuery.length + 1);
705
+ const newValue = `${before}${mentionTrigger}${item.name} ${after}`;
706
+ onChange?.(newValue);
707
+ onMentionSelect?.(item);
708
+ setShowMentions(false);
709
+ mentionStartRef.current = null;
710
+ setTimeout(() => {
711
+ if (innerRef.current) {
712
+ const newCursorPos = before.length + item.name.length + 2;
713
+ innerRef.current.focus();
714
+ innerRef.current.setSelectionRange(newCursorPos, newCursorPos);
715
+ }
716
+ }, 0);
717
+ },
718
+ [value, mentionQuery, mentionTrigger, onChange, onMentionSelect]
719
+ );
720
+ const handleKeyDown = (e) => {
721
+ if (showMentions && filteredMentions.length > 0) {
722
+ if (e.key === "ArrowDown") {
723
+ e.preventDefault();
724
+ setMentionIndex((prev) => (prev + 1) % filteredMentions.length);
725
+ return;
726
+ }
727
+ if (e.key === "ArrowUp") {
728
+ e.preventDefault();
729
+ setMentionIndex((prev) => (prev - 1 + filteredMentions.length) % filteredMentions.length);
730
+ return;
731
+ }
732
+ if (e.key === "Enter" || e.key === "Tab") {
733
+ e.preventDefault();
734
+ selectMention(filteredMentions[mentionIndex]);
735
+ return;
736
+ }
737
+ if (e.key === "Escape") {
738
+ e.preventDefault();
739
+ setShowMentions(false);
740
+ return;
741
+ }
742
+ }
743
+ if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
744
+ e.preventDefault();
745
+ const form = e.currentTarget.form;
746
+ if (form) {
747
+ form.requestSubmit();
748
+ }
749
+ }
750
+ onKeyDown?.(e);
751
+ };
752
+ return /* @__PURE__ */ jsxs6(Fragment, { children: [
753
+ /* @__PURE__ */ jsx6(
754
+ "textarea",
755
+ {
756
+ ref: innerRef,
757
+ value,
758
+ onChange: handleChange,
759
+ onKeyDown: handleKeyDown,
760
+ placeholder,
761
+ rows: 1,
762
+ className: cn(
763
+ "w-full resize-none border-none bg-transparent p-4",
764
+ "text-sm text-gray-900 placeholder:text-gray-400",
765
+ "focus:outline-none focus:ring-0",
766
+ "disabled:cursor-not-allowed disabled:opacity-50",
767
+ className
768
+ ),
769
+ style: { minHeight, maxHeight },
770
+ ...props
771
+ }
772
+ ),
773
+ showMentions && filteredMentions.length > 0 && mentionPosition && /* @__PURE__ */ jsx6(
774
+ InternalMentionPopover,
775
+ {
776
+ items: filteredMentions,
777
+ selectedIndex: mentionIndex,
778
+ onSelect: selectMention,
779
+ style: {
780
+ position: "fixed",
781
+ top: mentionPosition.top,
782
+ left: mentionPosition.left
783
+ }
784
+ }
785
+ )
786
+ ] });
787
+ }
788
+ );
789
+ ChatInputTextarea.displayName = "ChatInputTextarea";
790
+ var ChatInputToolbar = React5.forwardRef(
791
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
792
+ "div",
793
+ {
794
+ ref,
795
+ className: cn("flex items-center justify-between border-t border-gray-100 p-2", className),
796
+ ...props
797
+ }
798
+ )
799
+ );
800
+ ChatInputToolbar.displayName = "ChatInputToolbar";
801
+ var ChatInputTools = React5.forwardRef(
802
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx6("div", { ref, className: cn("flex items-center gap-1", className), ...props })
803
+ );
804
+ ChatInputTools.displayName = "ChatInputTools";
805
+ var ChatInputButton = React5.forwardRef(
806
+ ({ className, variant = "ghost", type = "button", ...props }, ref) => /* @__PURE__ */ jsx6(
807
+ "button",
808
+ {
809
+ ref,
810
+ type,
811
+ className: cn(
812
+ "inline-flex items-center justify-center rounded-lg p-2 transition-colors",
813
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500",
814
+ "disabled:pointer-events-none disabled:opacity-50",
815
+ variant === "ghost" && "text-gray-500 hover:bg-gray-100 hover:text-gray-700",
816
+ variant === "default" && "bg-primary-500 text-white hover:bg-primary-600",
817
+ className
818
+ ),
819
+ ...props
820
+ }
821
+ )
822
+ );
823
+ ChatInputButton.displayName = "ChatInputButton";
824
+ var ChatInputSubmit = React5.forwardRef(
825
+ ({ className, status = "idle", onStop, disabled, type, ...props }, ref) => {
826
+ const isStreaming = status === "streaming";
827
+ const isSubmitting = status === "submitted";
828
+ const isDisabled = disabled || isSubmitting;
829
+ const handleClick = (e) => {
830
+ if (isStreaming && onStop) {
831
+ e.preventDefault();
832
+ onStop();
833
+ }
834
+ };
835
+ return /* @__PURE__ */ jsx6(
836
+ "button",
837
+ {
838
+ ref,
839
+ type: isStreaming ? "button" : "submit",
840
+ disabled: isDisabled,
841
+ onClick: handleClick,
842
+ className: cn(
843
+ "inline-flex items-center justify-center rounded-lg p-2 transition-colors",
844
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500",
845
+ "disabled:pointer-events-none disabled:opacity-50",
846
+ isStreaming ? "bg-red-500 text-white hover:bg-red-600" : "bg-primary-500 text-white hover:bg-primary-600",
847
+ className
848
+ ),
849
+ ...props,
850
+ children: isSubmitting ? /* @__PURE__ */ jsx6(LoaderIcon, { className: "h-5 w-5 animate-spin" }) : isStreaming ? /* @__PURE__ */ jsx6(StopIcon, { className: "h-5 w-5" }) : /* @__PURE__ */ jsx6(SendIcon, { className: "h-5 w-5" })
851
+ }
852
+ );
853
+ }
854
+ );
855
+ ChatInputSubmit.displayName = "ChatInputSubmit";
856
+ function InternalMentionPopover({
857
+ items,
858
+ selectedIndex,
859
+ onSelect,
860
+ style,
861
+ className
862
+ }) {
863
+ return /* @__PURE__ */ jsx6(
864
+ "div",
865
+ {
866
+ className: cn(
867
+ "z-50 w-72 max-h-64 overflow-y-auto",
868
+ "bg-white rounded-xl shadow-lg border border-gray-200",
869
+ "py-1",
870
+ className
871
+ ),
872
+ style,
873
+ children: items.map((item, index) => /* @__PURE__ */ jsxs6(
874
+ "button",
875
+ {
876
+ type: "button",
877
+ onClick: () => onSelect(item),
878
+ className: cn(
879
+ "w-full flex items-center gap-3 px-3 py-2.5 text-left",
880
+ "transition-colors",
881
+ index === selectedIndex ? "bg-primary-50 text-primary-900" : "hover:bg-gray-50"
882
+ ),
883
+ children: [
884
+ item.avatar ? /* @__PURE__ */ jsx6(
885
+ "img",
886
+ {
887
+ src: item.avatar,
888
+ alt: item.name,
889
+ className: "h-9 w-9 rounded-full object-cover"
890
+ }
891
+ ) : /* @__PURE__ */ jsx6("div", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-primary-100", children: /* @__PURE__ */ jsx6("span", { className: "text-sm font-medium text-primary-700", children: item.name.charAt(0).toUpperCase() }) }),
892
+ /* @__PURE__ */ jsxs6("div", { className: "min-w-0 flex-1", children: [
893
+ /* @__PURE__ */ jsx6("div", { className: "truncate text-sm font-medium text-gray-900", children: item.name }),
894
+ item.description && /* @__PURE__ */ jsx6("div", { className: "truncate text-xs text-gray-500", children: item.description })
895
+ ] })
896
+ ]
897
+ },
898
+ item.id
899
+ ))
900
+ }
901
+ );
902
+ }
903
+ function SendIcon({ className }) {
904
+ return /* @__PURE__ */ jsx6("svg", { className, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 19l9 2-9-18-9 18 9-2zm0 0v-8" }) });
905
+ }
906
+ function StopIcon({ className }) {
907
+ return /* @__PURE__ */ jsx6("svg", { className, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx6("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) });
908
+ }
909
+ function LoaderIcon({ className }) {
910
+ return /* @__PURE__ */ jsx6("svg", { className, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx6(
911
+ "path",
912
+ {
913
+ strokeLinecap: "round",
914
+ strokeLinejoin: "round",
915
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
916
+ }
917
+ ) });
918
+ }
919
+
920
+ // src/components/ChatMessage.tsx
921
+ import * as React7 from "react";
922
+
923
+ // src/components/StreamText.tsx
924
+ import * as React6 from "react";
925
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
926
+ function useCallbackRef(callback) {
927
+ const ref = React6.useRef(callback);
928
+ React6.useLayoutEffect(() => {
929
+ ref.current = callback;
930
+ });
931
+ return ref;
932
+ }
933
+ function useAsyncStream(stream, options = {}) {
934
+ const [text, setText] = React6.useState("");
935
+ const [isStreaming, setIsStreaming] = React6.useState(false);
936
+ const [error, setError] = React6.useState(null);
937
+ const onCompleteRef = useCallbackRef(options.onComplete);
938
+ const onErrorRef = useCallbackRef(options.onError);
939
+ const onUpdateRef = useCallbackRef(options.onUpdate);
940
+ const internalAbortRef = React6.useRef(null);
941
+ const abort = React6.useCallback(() => {
942
+ internalAbortRef.current?.abort();
943
+ options.abortController?.abort();
944
+ }, [options.abortController]);
945
+ React6.useEffect(() => {
946
+ if (!stream) {
947
+ setText("");
948
+ setIsStreaming(false);
949
+ setError(null);
950
+ return;
951
+ }
952
+ const internalAbort = new AbortController();
953
+ internalAbortRef.current = internalAbort;
954
+ const externalAbort = options.abortController;
955
+ const isAborted = () => internalAbort.signal.aborted || (externalAbort?.signal.aborted ?? false);
956
+ setIsStreaming(true);
957
+ setText("");
958
+ setError(null);
959
+ const consume = async () => {
960
+ try {
961
+ const iterable = isReadableStream(stream) ? readableStreamToAsyncIterable(stream) : stream;
962
+ let accumulated = "";
963
+ for await (const chunk of iterable) {
964
+ if (isAborted()) break;
965
+ accumulated += chunk;
966
+ setText(accumulated);
967
+ onUpdateRef.current?.(accumulated);
968
+ }
969
+ if (!isAborted()) {
970
+ setIsStreaming(false);
971
+ onCompleteRef.current?.();
972
+ }
973
+ } catch (err) {
974
+ if (!isAborted()) {
975
+ const streamError = err instanceof Error ? err : new Error(String(err));
976
+ setError(streamError);
977
+ setIsStreaming(false);
978
+ onErrorRef.current?.(streamError);
979
+ }
980
+ }
981
+ };
982
+ consume();
983
+ return () => {
984
+ internalAbort.abort();
985
+ };
986
+ }, [stream, options.abortController]);
987
+ return { text, isStreaming, error, abort };
988
+ }
989
+ function useTypingAnimation(text, options = {}) {
990
+ const { speed = 30 } = options;
991
+ const [displayText, setDisplayText] = React6.useState("");
992
+ const [isTyping, setIsTyping] = React6.useState(false);
993
+ const onCompleteRef = useCallbackRef(options.onComplete);
994
+ const onUpdateRef = useCallbackRef(options.onUpdate);
995
+ React6.useEffect(() => {
996
+ if (!text) {
997
+ setDisplayText("");
998
+ setIsTyping(false);
999
+ return;
1000
+ }
1001
+ setIsTyping(true);
1002
+ setDisplayText("");
1003
+ let index = 0;
1004
+ const timer = setInterval(() => {
1005
+ if (index < text.length) {
1006
+ const newText = text.slice(0, index + 1);
1007
+ setDisplayText(newText);
1008
+ onUpdateRef.current?.(newText);
1009
+ index++;
1010
+ } else {
1011
+ clearInterval(timer);
1012
+ setIsTyping(false);
1013
+ onCompleteRef.current?.();
1014
+ }
1015
+ }, speed);
1016
+ return () => clearInterval(timer);
1017
+ }, [text, speed]);
1018
+ return { displayText, isTyping };
1019
+ }
1020
+ function isReadableStream(value) {
1021
+ return typeof value === "object" && value !== null && "getReader" in value && typeof value.getReader === "function";
1022
+ }
1023
+ async function* readableStreamToAsyncIterable(stream) {
1024
+ const reader = stream.getReader();
1025
+ try {
1026
+ while (true) {
1027
+ const { done, value } = await reader.read();
1028
+ if (done) break;
1029
+ if (value) yield value;
1030
+ }
1031
+ } finally {
1032
+ reader.releaseLock();
1033
+ }
1034
+ }
1035
+ var StreamCursor = React6.forwardRef(({ char = "\u258B", className, ...props }, ref) => /* @__PURE__ */ jsx7(
1036
+ "span",
1037
+ {
1038
+ ref,
1039
+ className: cn("animate-pulse ml-0.5", className),
1040
+ "aria-hidden": "true",
1041
+ ...props,
1042
+ children: char
1043
+ }
1044
+ ));
1045
+ StreamCursor.displayName = "StreamCursor";
1046
+ var StreamText = React6.forwardRef(
1047
+ ({
1048
+ stream,
1049
+ text,
1050
+ cursor = true,
1051
+ cursorChar = "\u258B",
1052
+ speed = 30,
1053
+ onComplete,
1054
+ onError,
1055
+ onUpdate,
1056
+ abortController,
1057
+ children,
1058
+ className,
1059
+ ...props
1060
+ }, ref) => {
1061
+ const {
1062
+ text: streamedText,
1063
+ isStreaming,
1064
+ error
1065
+ } = useAsyncStream(stream, {
1066
+ onComplete: stream ? onComplete : void 0,
1067
+ onError,
1068
+ onUpdate: stream ? onUpdate : void 0,
1069
+ abortController
1070
+ });
1071
+ const { displayText: typedText, isTyping } = useTypingAnimation(
1072
+ stream ? void 0 : text,
1073
+ {
1074
+ speed,
1075
+ onComplete: stream ? void 0 : onComplete,
1076
+ onUpdate: stream ? void 0 : onUpdate
1077
+ }
1078
+ );
1079
+ const displayText = stream ? streamedText : typedText;
1080
+ const isActive = stream ? isStreaming : isTyping;
1081
+ const showCursor = cursor && isActive;
1082
+ if (children) {
1083
+ return /* @__PURE__ */ jsxs7("span", { ref, className, ...props, children: [
1084
+ children(displayText, isActive),
1085
+ showCursor && /* @__PURE__ */ jsx7(StreamCursor, { char: cursorChar })
1086
+ ] });
1087
+ }
1088
+ return /* @__PURE__ */ jsxs7(
1089
+ "span",
1090
+ {
1091
+ ref,
1092
+ className: cn("whitespace-pre-wrap", className),
1093
+ "aria-live": "polite",
1094
+ "aria-busy": isActive,
1095
+ ...props,
1096
+ children: [
1097
+ displayText,
1098
+ showCursor && /* @__PURE__ */ jsx7(StreamCursor, { char: cursorChar }),
1099
+ error && /* @__PURE__ */ jsxs7("span", { className: "text-red-500 ml-2", role: "alert", children: [
1100
+ "Error: ",
1101
+ error.message
1102
+ ] })
1103
+ ]
1104
+ }
1105
+ );
1106
+ }
1107
+ );
1108
+ StreamText.displayName = "StreamText";
1109
+
1110
+ // src/components/ChatMessage.tsx
1111
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1112
+ var ChatMessageContext = React7.createContext(null);
1113
+ function useChatMessageContext() {
1114
+ const context = React7.useContext(ChatMessageContext);
1115
+ if (!context) {
1116
+ throw new Error("ChatMessage components must be used within <ChatMessage>");
1117
+ }
1118
+ return context;
1119
+ }
1120
+ var roleToVariant2 = {
1121
+ user: "primary",
1122
+ assistant: "secondary",
1123
+ system: "neutral",
1124
+ tool: "warning",
1125
+ error: "error"
1126
+ };
1127
+ var statusToAnimation = {
1128
+ idle: "idle",
1129
+ thinking: "thinking",
1130
+ streaming: "responding",
1131
+ complete: "idle",
1132
+ error: "idle"
1133
+ };
1134
+ var ChatMessage = React7.forwardRef(
1135
+ ({ role, status = "complete", className, children, ...props }, ref) => {
1136
+ const isUser = role === "user";
1137
+ const isSystem = role === "system";
1138
+ const contextValue = {
1139
+ role,
1140
+ status,
1141
+ isUser
1142
+ };
1143
+ if (isSystem) {
1144
+ return /* @__PURE__ */ jsx8(ChatMessageContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx8(
1145
+ "div",
1146
+ {
1147
+ ref,
1148
+ className: cn("flex justify-center my-4", className),
1149
+ "data-role": role,
1150
+ "data-status": status,
1151
+ ...props,
1152
+ children
1153
+ }
1154
+ ) });
1155
+ }
1156
+ return /* @__PURE__ */ jsx8(ChatMessageContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx8(
1157
+ "div",
1158
+ {
1159
+ ref,
1160
+ className: cn(
1161
+ "flex gap-3 my-4",
1162
+ isUser ? "flex-row-reverse" : "flex-row",
1163
+ className
1164
+ ),
1165
+ "data-role": role,
1166
+ "data-status": status,
1167
+ ...props,
1168
+ children
1169
+ }
1170
+ ) });
1171
+ }
1172
+ );
1173
+ ChatMessage.displayName = "ChatMessage";
1174
+ var ChatMessageAvatar = React7.forwardRef(
1175
+ ({ src, name, animationStatus, className, ...props }, ref) => {
1176
+ const { role, status, isUser } = useChatMessageContext();
1177
+ if (isUser) return null;
1178
+ const animation = animationStatus ?? statusToAnimation[status];
1179
+ const variant = roleToVariant2[role];
1180
+ const fallbackChar = name?.charAt(0).toUpperCase() || role.charAt(0).toUpperCase();
1181
+ return /* @__PURE__ */ jsx8("div", { ref, className: cn("flex-shrink-0", className), ...props, children: /* @__PURE__ */ jsxs8(Avatar, { size: "md", status: animation, children: [
1182
+ src && /* @__PURE__ */ jsx8(AvatarImage, { src, alt: name || role }),
1183
+ /* @__PURE__ */ jsx8(AvatarFallback, { variant, children: fallbackChar })
1184
+ ] }) });
1185
+ }
1186
+ );
1187
+ ChatMessageAvatar.displayName = "ChatMessageAvatar";
1188
+ var contentStyles = {
1189
+ user: "bg-primary-500 text-white rounded-2xl rounded-br-sm",
1190
+ assistant: "bg-gray-100 text-gray-900 rounded-2xl rounded-bl-sm",
1191
+ system: "bg-gray-50 text-gray-500 rounded-full text-sm",
1192
+ tool: "bg-amber-50 text-amber-900 rounded-xl",
1193
+ error: "bg-red-50 text-red-900 rounded-xl"
1194
+ };
1195
+ var ChatMessageContent = React7.forwardRef(
1196
+ ({ name, className, children, ...props }, ref) => {
1197
+ const { role, status, isUser } = useChatMessageContext();
1198
+ return /* @__PURE__ */ jsxs8(
1199
+ "div",
1200
+ {
1201
+ ref,
1202
+ className: cn(
1203
+ "flex flex-col max-w-[70%]",
1204
+ isUser ? "items-end" : "items-start",
1205
+ className
1206
+ ),
1207
+ ...props,
1208
+ children: [
1209
+ name && !isUser && /* @__PURE__ */ jsx8("span", { className: "text-sm font-medium text-gray-900 mb-1", children: name }),
1210
+ /* @__PURE__ */ jsx8(
1211
+ "div",
1212
+ {
1213
+ className: cn("px-4 py-3", contentStyles[role]),
1214
+ "aria-busy": status === "thinking" || status === "streaming",
1215
+ "aria-live": status === "streaming" ? "polite" : void 0,
1216
+ children
1217
+ }
1218
+ )
1219
+ ]
1220
+ }
1221
+ );
1222
+ }
1223
+ );
1224
+ ChatMessageContent.displayName = "ChatMessageContent";
1225
+ var ChatMessageTimestamp = React7.forwardRef(
1226
+ ({ time, format, className, ...props }, ref) => {
1227
+ const { status } = useChatMessageContext();
1228
+ if (status === "thinking" || status === "streaming") {
1229
+ return null;
1230
+ }
1231
+ const date = typeof time === "string" ? new Date(time) : time;
1232
+ const formatted = format ? format(date) : date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
1233
+ return /* @__PURE__ */ jsx8(
1234
+ "span",
1235
+ {
1236
+ ref,
1237
+ className: cn("text-xs text-gray-400 mt-1", className),
1238
+ ...props,
1239
+ children: formatted
1240
+ }
1241
+ );
1242
+ }
1243
+ );
1244
+ ChatMessageTimestamp.displayName = "ChatMessageTimestamp";
1245
+ var ChatMessageThinking = React7.forwardRef(
1246
+ ({ label, className, ...props }, ref) => {
1247
+ return /* @__PURE__ */ jsxs8(
1248
+ "span",
1249
+ {
1250
+ ref,
1251
+ className: cn("inline-flex items-center gap-2 text-gray-500", className),
1252
+ ...props,
1253
+ children: [
1254
+ /* @__PURE__ */ jsx8("span", { className: "flex items-center gap-1", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx8(
1255
+ "span",
1256
+ {
1257
+ className: "w-1.5 h-1.5 bg-secondary-500 rounded-full animate-bounce",
1258
+ style: { animationDelay: `${i * 150}ms`, animationDuration: "600ms" }
1259
+ },
1260
+ i
1261
+ )) }),
1262
+ label && /* @__PURE__ */ jsx8("span", { className: "text-sm", children: label })
1263
+ ]
1264
+ }
1265
+ );
1266
+ }
1267
+ );
1268
+ ChatMessageThinking.displayName = "ChatMessageThinking";
1269
+ var ChatMessageError = React7.forwardRef(
1270
+ ({ message = "Something went wrong", onRetry, className, ...props }, ref) => {
1271
+ return /* @__PURE__ */ jsxs8(
1272
+ "span",
1273
+ {
1274
+ ref,
1275
+ className: cn("flex items-center gap-2 text-red-600", className),
1276
+ role: "alert",
1277
+ ...props,
1278
+ children: [
1279
+ /* @__PURE__ */ jsx8("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx8(
1280
+ "path",
1281
+ {
1282
+ strokeLinecap: "round",
1283
+ strokeLinejoin: "round",
1284
+ strokeWidth: 2,
1285
+ d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
1286
+ }
1287
+ ) }),
1288
+ /* @__PURE__ */ jsx8("span", { children: message }),
1289
+ onRetry && /* @__PURE__ */ jsx8(
1290
+ "button",
1291
+ {
1292
+ type: "button",
1293
+ onClick: onRetry,
1294
+ className: "text-sm underline hover:no-underline",
1295
+ children: "Retry"
1296
+ }
1297
+ )
1298
+ ]
1299
+ }
1300
+ );
1301
+ }
1302
+ );
1303
+ ChatMessageError.displayName = "ChatMessageError";
1304
+ var ChatMessageSimple = React7.forwardRef(
1305
+ ({
1306
+ role,
1307
+ status = "complete",
1308
+ content,
1309
+ stream,
1310
+ avatar,
1311
+ name,
1312
+ timestamp,
1313
+ thinkingLabel,
1314
+ errorMessage,
1315
+ onComplete,
1316
+ onError,
1317
+ onRetry,
1318
+ className,
1319
+ ...props
1320
+ }, ref) => {
1321
+ const renderContent = () => {
1322
+ switch (status) {
1323
+ case "thinking":
1324
+ return /* @__PURE__ */ jsx8(ChatMessageThinking, { label: thinkingLabel });
1325
+ case "streaming":
1326
+ return /* @__PURE__ */ jsx8(
1327
+ StreamText,
1328
+ {
1329
+ stream,
1330
+ cursor: true,
1331
+ onComplete,
1332
+ onError
1333
+ }
1334
+ );
1335
+ case "error":
1336
+ return /* @__PURE__ */ jsx8(ChatMessageError, { message: errorMessage, onRetry });
1337
+ case "idle":
1338
+ case "complete":
1339
+ default:
1340
+ return /* @__PURE__ */ jsx8("span", { className: "whitespace-pre-wrap", children: content });
1341
+ }
1342
+ };
1343
+ return /* @__PURE__ */ jsxs8(ChatMessage, { ref, role, status, className, ...props, children: [
1344
+ /* @__PURE__ */ jsx8(ChatMessageAvatar, { src: avatar, name }),
1345
+ /* @__PURE__ */ jsxs8(ChatMessageContent, { name, children: [
1346
+ renderContent(),
1347
+ timestamp && status === "complete" && /* @__PURE__ */ jsx8(ChatMessageTimestamp, { time: timestamp })
1348
+ ] })
1349
+ ] });
1350
+ }
1351
+ );
1352
+ ChatMessageSimple.displayName = "ChatMessageSimple";
1353
+
1354
+ // src/components/MentionPopover.tsx
1355
+ import * as React8 from "react";
1356
+ import {
1357
+ useFloating,
1358
+ autoUpdate,
1359
+ offset,
1360
+ flip,
1361
+ shift,
1362
+ size as floatingSize
1363
+ } from "@floating-ui/react";
1364
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1365
+ var MentionContext = React8.createContext(null);
1366
+ function useMentionContext() {
1367
+ const context = React8.useContext(MentionContext);
1368
+ if (!context) {
1369
+ throw new Error("MentionItem must be used within MentionPopover");
1370
+ }
1371
+ return context;
1372
+ }
1373
+ function useMentionPopover(anchorRef, virtualAnchor, options = {}) {
1374
+ const { placement = "bottom-start", offset: offsetValue = 4 } = options;
1375
+ const floating = useFloating({
1376
+ placement,
1377
+ middleware: [
1378
+ offset(offsetValue),
1379
+ flip({ fallbackPlacements: ["top-start", "bottom-end", "top-end"] }),
1380
+ shift({ padding: 8 }),
1381
+ floatingSize({
1382
+ apply({ availableHeight, elements }) {
1383
+ Object.assign(elements.floating.style, {
1384
+ maxHeight: `${Math.min(availableHeight, 320)}px`
1385
+ });
1386
+ }
1387
+ })
1388
+ ],
1389
+ whileElementsMounted: autoUpdate
1390
+ });
1391
+ React8.useLayoutEffect(() => {
1392
+ if (virtualAnchor) {
1393
+ floating.refs.setReference(virtualAnchor);
1394
+ } else if (anchorRef?.current) {
1395
+ floating.refs.setReference(anchorRef.current);
1396
+ }
1397
+ }, [virtualAnchor, anchorRef, floating.refs]);
1398
+ return {
1399
+ floating,
1400
+ isPositioned: floating.isPositioned
1401
+ };
1402
+ }
1403
+ var MentionItem = React8.forwardRef(
1404
+ ({ agent, highlighted = false, onSelect, className, children, ...props }, ref) => {
1405
+ const context = React8.useContext(MentionContext);
1406
+ const handleSelect = onSelect || context?.onSelect;
1407
+ return /* @__PURE__ */ jsx9(
1408
+ "div",
1409
+ {
1410
+ ref,
1411
+ role: "option",
1412
+ "aria-selected": highlighted,
1413
+ className: cn(
1414
+ "flex items-center gap-3 px-3 py-2 cursor-pointer transition-colors",
1415
+ highlighted ? "bg-primary-50 text-primary-900" : "hover:bg-gray-50",
1416
+ className
1417
+ ),
1418
+ onClick: () => handleSelect?.(agent),
1419
+ onKeyDown: (e) => {
1420
+ if (e.key === "Enter" || e.key === " ") {
1421
+ e.preventDefault();
1422
+ handleSelect?.(agent);
1423
+ }
1424
+ },
1425
+ tabIndex: 0,
1426
+ ...props,
1427
+ children: children || /* @__PURE__ */ jsxs9(Fragment2, { children: [
1428
+ /* @__PURE__ */ jsxs9(Avatar, { size: "sm", children: [
1429
+ agent.avatar && /* @__PURE__ */ jsx9(AvatarImage, { src: agent.avatar, alt: agent.name }),
1430
+ /* @__PURE__ */ jsx9(AvatarFallback, { variant: "secondary", children: agent.name.charAt(0).toUpperCase() })
1431
+ ] }),
1432
+ /* @__PURE__ */ jsxs9("div", { className: "flex-1 min-w-0", children: [
1433
+ /* @__PURE__ */ jsx9("div", { className: "font-medium text-sm truncate", children: agent.name }),
1434
+ agent.description && /* @__PURE__ */ jsx9("div", { className: "text-xs text-gray-500 truncate", children: agent.description })
1435
+ ] }),
1436
+ agent.status && /* @__PURE__ */ jsx9(
1437
+ "span",
1438
+ {
1439
+ className: cn(
1440
+ "w-2 h-2 rounded-full",
1441
+ agent.status === "online" && "bg-green-500",
1442
+ agent.status === "busy" && "bg-amber-500",
1443
+ agent.status === "offline" && "bg-gray-300"
1444
+ )
1445
+ }
1446
+ )
1447
+ ] })
1448
+ }
1449
+ );
1450
+ }
1451
+ );
1452
+ MentionItem.displayName = "MentionItem";
1453
+ var MentionPopover = React8.forwardRef(
1454
+ ({
1455
+ open,
1456
+ anchorRef,
1457
+ virtualAnchor,
1458
+ query = "",
1459
+ agents,
1460
+ onSelect,
1461
+ onClose,
1462
+ maxItems = 5,
1463
+ placement = "bottom-start",
1464
+ filter,
1465
+ emptyMessage = "No agents found",
1466
+ children,
1467
+ className
1468
+ }, ref) => {
1469
+ const [highlightedIndex, setHighlightedIndex] = React8.useState(0);
1470
+ const { floating, isPositioned } = useMentionPopover(anchorRef, virtualAnchor, {
1471
+ placement
1472
+ });
1473
+ const defaultFilter = React8.useCallback(
1474
+ (agent, q) => {
1475
+ const searchLower = q.toLowerCase();
1476
+ return agent.name.toLowerCase().includes(searchLower) || agent.description?.toLowerCase().includes(searchLower) || false;
1477
+ },
1478
+ []
1479
+ );
1480
+ const filteredAgents = React8.useMemo(() => {
1481
+ const filterFn = filter || defaultFilter;
1482
+ return agents.filter((agent) => filterFn(agent, query)).slice(0, maxItems);
1483
+ }, [agents, query, maxItems, filter, defaultFilter]);
1484
+ React8.useEffect(() => {
1485
+ setHighlightedIndex(0);
1486
+ }, [filteredAgents.length]);
1487
+ React8.useEffect(() => {
1488
+ if (!open) return;
1489
+ const handleKeyDown = (e) => {
1490
+ switch (e.key) {
1491
+ case "ArrowDown":
1492
+ e.preventDefault();
1493
+ setHighlightedIndex(
1494
+ (prev) => prev < filteredAgents.length - 1 ? prev + 1 : 0
1495
+ );
1496
+ break;
1497
+ case "ArrowUp":
1498
+ e.preventDefault();
1499
+ setHighlightedIndex(
1500
+ (prev) => prev > 0 ? prev - 1 : filteredAgents.length - 1
1501
+ );
1502
+ break;
1503
+ case "Enter":
1504
+ e.preventDefault();
1505
+ if (filteredAgents[highlightedIndex]) {
1506
+ onSelect?.(filteredAgents[highlightedIndex]);
1507
+ }
1508
+ break;
1509
+ case "Escape":
1510
+ e.preventDefault();
1511
+ onClose?.();
1512
+ break;
1513
+ }
1514
+ };
1515
+ document.addEventListener("keydown", handleKeyDown);
1516
+ return () => document.removeEventListener("keydown", handleKeyDown);
1517
+ }, [open, filteredAgents, highlightedIndex, onSelect, onClose]);
1518
+ if (!open) return null;
1519
+ const contextValue = {
1520
+ query,
1521
+ highlightedIndex,
1522
+ setHighlightedIndex,
1523
+ onSelect: onSelect || (() => {
1524
+ })
1525
+ };
1526
+ return /* @__PURE__ */ jsx9(MentionContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs9(
1527
+ "div",
1528
+ {
1529
+ ref: (node) => {
1530
+ floating.refs.setFloating(node);
1531
+ if (typeof ref === "function") ref(node);
1532
+ else if (ref) ref.current = node;
1533
+ },
1534
+ role: "listbox",
1535
+ "aria-label": "Select an agent to mention",
1536
+ className: cn(
1537
+ "z-50 min-w-[200px] max-w-[300px]",
1538
+ "bg-white rounded-lg shadow-lg border border-gray-200",
1539
+ "overflow-hidden overflow-y-auto",
1540
+ !isPositioned && "invisible",
1541
+ className
1542
+ ),
1543
+ style: {
1544
+ position: floating.strategy,
1545
+ top: floating.y ?? 0,
1546
+ left: floating.x ?? 0
1547
+ },
1548
+ children: [
1549
+ /* @__PURE__ */ jsx9("div", { className: "py-1", children: children ? children(filteredAgents) : filteredAgents.length > 0 ? filteredAgents.map((agent, index) => /* @__PURE__ */ jsx9(
1550
+ MentionItem,
1551
+ {
1552
+ agent,
1553
+ highlighted: index === highlightedIndex,
1554
+ onSelect
1555
+ },
1556
+ agent.id
1557
+ )) : /* @__PURE__ */ jsx9("div", { className: "px-3 py-4 text-sm text-gray-500 text-center", children: emptyMessage }) }),
1558
+ /* @__PURE__ */ jsx9("div", { className: "border-t border-gray-100 px-3 py-1.5 bg-gray-50", children: /* @__PURE__ */ jsx9("span", { className: "text-xs text-gray-400", children: "\u2191\u2193 to navigate \xB7 Enter to select \xB7 Esc to close" }) })
1559
+ ]
1560
+ }
1561
+ ) });
1562
+ }
1563
+ );
1564
+ MentionPopover.displayName = "MentionPopover";
1565
+
1566
+ // src/components/ThinkingIndicator.tsx
1567
+ import * as React9 from "react";
1568
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1569
+ var sizeClasses4 = {
1570
+ sm: { dot: "w-1 h-1", gap: "gap-0.5", text: "text-xs" },
1571
+ md: { dot: "w-1.5 h-1.5", gap: "gap-1", text: "text-sm" },
1572
+ lg: { dot: "w-2 h-2", gap: "gap-1.5", text: "text-base" }
1573
+ };
1574
+ var colorClasses = {
1575
+ primary: "bg-primary-500",
1576
+ secondary: "bg-secondary-500",
1577
+ neutral: "bg-gray-400"
1578
+ };
1579
+ function DotsAnimation({
1580
+ size,
1581
+ color
1582
+ }) {
1583
+ const { dot, gap } = sizeClasses4[size];
1584
+ return /* @__PURE__ */ jsx10("span", { className: cn("inline-flex items-center", gap), children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx10(
1585
+ "span",
1586
+ {
1587
+ className: cn(dot, colorClasses[color], "rounded-full animate-pulse"),
1588
+ style: { animationDelay: `${i * 150}ms` }
1589
+ },
1590
+ i
1591
+ )) });
1592
+ }
1593
+ function BounceAnimation({
1594
+ size,
1595
+ color
1596
+ }) {
1597
+ const { dot, gap } = sizeClasses4[size];
1598
+ return /* @__PURE__ */ jsx10("span", { className: cn("inline-flex items-end", gap), style: { height: "1em" }, children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx10(
1599
+ "span",
1600
+ {
1601
+ className: cn(dot, colorClasses[color], "rounded-full animate-bounce"),
1602
+ style: { animationDelay: `${i * 150}ms`, animationDuration: "600ms" }
1603
+ },
1604
+ i
1605
+ )) });
1606
+ }
1607
+ function WaveAnimation({
1608
+ size,
1609
+ color
1610
+ }) {
1611
+ const { gap } = sizeClasses4[size];
1612
+ const barWidth = size === "sm" ? "w-0.5" : size === "md" ? "w-1" : "w-1.5";
1613
+ return /* @__PURE__ */ jsx10("span", { className: cn("inline-flex items-center", gap), style: { height: "1em" }, children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ jsx10(
1614
+ "span",
1615
+ {
1616
+ className: cn(barWidth, colorClasses[color], "rounded-full animate-wave"),
1617
+ style: {
1618
+ animationDelay: `${i * 100}ms`,
1619
+ height: "0.5em"
1620
+ }
1621
+ },
1622
+ i
1623
+ )) });
1624
+ }
1625
+ function PulseAnimation({
1626
+ size,
1627
+ color
1628
+ }) {
1629
+ const ringSize = size === "sm" ? "w-3 h-3" : size === "md" ? "w-4 h-4" : "w-5 h-5";
1630
+ return /* @__PURE__ */ jsxs10("span", { className: "relative inline-flex", children: [
1631
+ /* @__PURE__ */ jsx10("span", { className: cn(ringSize, colorClasses[color], "rounded-full animate-ping opacity-75") }),
1632
+ /* @__PURE__ */ jsx10(
1633
+ "span",
1634
+ {
1635
+ className: cn(
1636
+ ringSize,
1637
+ colorClasses[color],
1638
+ "rounded-full absolute inset-0"
1639
+ )
1640
+ }
1641
+ )
1642
+ ] });
1643
+ }
1644
+ function SpinnerAnimation({
1645
+ size,
1646
+ color
1647
+ }) {
1648
+ const spinnerSize = size === "sm" ? "w-3 h-3" : size === "md" ? "w-4 h-4" : "w-5 h-5";
1649
+ const borderColor = color === "primary" ? "border-primary-500" : color === "secondary" ? "border-secondary-500" : "border-gray-400";
1650
+ return /* @__PURE__ */ jsx10(
1651
+ "span",
1652
+ {
1653
+ className: cn(
1654
+ spinnerSize,
1655
+ "inline-block rounded-full border-2 border-transparent animate-spin",
1656
+ `border-t-2 ${borderColor}`
1657
+ )
1658
+ }
1659
+ );
1660
+ }
1661
+ var ThinkingIndicator = React9.forwardRef(
1662
+ ({
1663
+ label,
1664
+ agents,
1665
+ variant = "bounce",
1666
+ size = "md",
1667
+ inline = false,
1668
+ color = "secondary",
1669
+ className,
1670
+ ...props
1671
+ }, ref) => {
1672
+ const { text } = sizeClasses4[size];
1673
+ const renderAnimation = () => {
1674
+ switch (variant) {
1675
+ case "dots":
1676
+ return /* @__PURE__ */ jsx10(DotsAnimation, { size, color });
1677
+ case "pulse":
1678
+ return /* @__PURE__ */ jsx10(PulseAnimation, { size, color });
1679
+ case "wave":
1680
+ return /* @__PURE__ */ jsx10(WaveAnimation, { size, color });
1681
+ case "spinner":
1682
+ return /* @__PURE__ */ jsx10(SpinnerAnimation, { size, color });
1683
+ case "bounce":
1684
+ default:
1685
+ return /* @__PURE__ */ jsx10(BounceAnimation, { size, color });
1686
+ }
1687
+ };
1688
+ const labelText = agents && agents.length > 0 ? `${agents.join(", ")} ${agents.length > 1 ? "are" : "is"} thinking...` : label || "Thinking...";
1689
+ if (inline) {
1690
+ return /* @__PURE__ */ jsx10(
1691
+ "span",
1692
+ {
1693
+ ref,
1694
+ className: cn("inline-flex items-center gap-2", className),
1695
+ role: "status",
1696
+ "aria-live": "polite",
1697
+ "aria-label": labelText,
1698
+ ...props,
1699
+ children: renderAnimation()
1700
+ }
1701
+ );
1702
+ }
1703
+ return /* @__PURE__ */ jsxs10(
1704
+ "div",
1705
+ {
1706
+ ref,
1707
+ className: cn("flex items-center gap-3 my-4", className),
1708
+ role: "status",
1709
+ "aria-live": "polite",
1710
+ "aria-label": labelText,
1711
+ ...props,
1712
+ children: [
1713
+ renderAnimation(),
1714
+ /* @__PURE__ */ jsx10("span", { className: cn("text-gray-500", text), children: labelText })
1715
+ ]
1716
+ }
1717
+ );
1718
+ }
1719
+ );
1720
+ ThinkingIndicator.displayName = "ThinkingIndicator";
1721
+
1722
+ // src/components/ToolResult.tsx
1723
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1724
+ var statusIcons = {
1725
+ pending: "\u23F3",
1726
+ streaming: "\u{1F4E1}",
1727
+ ready: "\u{1F4CB}",
1728
+ running: "\u26A1",
1729
+ "approval-required": "\u{1F510}",
1730
+ approved: "\u2705",
1731
+ denied: "\u{1F6AB}",
1732
+ success: "\u2713",
1733
+ error: "\u2717"
1734
+ };
1735
+ var statusColors2 = {
1736
+ pending: "text-gray-500 bg-gray-50 border-gray-200",
1737
+ streaming: "text-blue-600 bg-blue-50 border-blue-200",
1738
+ ready: "text-indigo-600 bg-indigo-50 border-indigo-200",
1739
+ running: "text-amber-600 bg-amber-50 border-amber-200",
1740
+ "approval-required": "text-purple-600 bg-purple-50 border-purple-200",
1741
+ approved: "text-green-600 bg-green-50 border-green-200",
1742
+ denied: "text-gray-600 bg-gray-50 border-gray-200",
1743
+ success: "text-green-600 bg-green-50 border-green-200",
1744
+ error: "text-red-600 bg-red-50 border-red-200"
1745
+ };
1746
+ var statusLabels = {
1747
+ pending: "Pending",
1748
+ streaming: "Receiving parameters...",
1749
+ ready: "Ready to execute",
1750
+ running: "Running...",
1751
+ "approval-required": "Waiting for approval",
1752
+ approved: "Approved",
1753
+ denied: "Denied",
1754
+ success: "Completed",
1755
+ error: "Failed"
1756
+ };
1757
+ function ToolResult({
1758
+ tool,
1759
+ status,
1760
+ children,
1761
+ collapsed = false,
1762
+ onToggle,
1763
+ error,
1764
+ onApprove,
1765
+ onDeny,
1766
+ className
1767
+ }) {
1768
+ const showApprovalButtons = status === "approval-required" && (onApprove || onDeny);
1769
+ const isAnimating = ["streaming", "running"].includes(status);
1770
+ return /* @__PURE__ */ jsxs11(
1771
+ "div",
1772
+ {
1773
+ className: cn(
1774
+ "my-4 rounded-lg border overflow-hidden",
1775
+ statusColors2[status],
1776
+ className
1777
+ ),
1778
+ children: [
1779
+ /* @__PURE__ */ jsxs11(
1780
+ "div",
1781
+ {
1782
+ className: cn(
1783
+ "flex items-center gap-2 px-4 py-2",
1784
+ onToggle && "cursor-pointer hover:bg-black/5"
1785
+ ),
1786
+ onClick: onToggle,
1787
+ children: [
1788
+ /* @__PURE__ */ jsx11("span", { className: "text-lg", children: statusIcons[status] }),
1789
+ /* @__PURE__ */ jsx11("span", { className: "font-mono text-sm font-medium", children: tool }),
1790
+ /* @__PURE__ */ jsx11("span", { className: cn("text-xs", isAnimating && "animate-pulse"), children: statusLabels[status] }),
1791
+ onToggle && /* @__PURE__ */ jsx11("span", { className: "ml-auto text-xs", children: collapsed ? "\u25B6" : "\u25BC" })
1792
+ ]
1793
+ }
1794
+ ),
1795
+ showApprovalButtons && /* @__PURE__ */ jsxs11("div", { className: "flex gap-2 px-4 py-2 bg-white border-t border-inherit", children: [
1796
+ onApprove && /* @__PURE__ */ jsx11(
1797
+ "button",
1798
+ {
1799
+ onClick: onApprove,
1800
+ className: "px-3 py-1.5 text-sm font-medium text-white bg-green-600 rounded-md hover:bg-green-700 transition-colors",
1801
+ children: "Approve"
1802
+ }
1803
+ ),
1804
+ onDeny && /* @__PURE__ */ jsx11(
1805
+ "button",
1806
+ {
1807
+ onClick: () => onDeny(),
1808
+ className: "px-3 py-1.5 text-sm font-medium text-gray-700 bg-gray-200 rounded-md hover:bg-gray-300 transition-colors",
1809
+ children: "Deny"
1810
+ }
1811
+ )
1812
+ ] }),
1813
+ !collapsed && (children || error) && /* @__PURE__ */ jsx11("div", { className: "px-4 py-3 bg-white border-t border-inherit", children: error ? /* @__PURE__ */ jsx11("div", { className: "text-sm text-red-600 font-mono", children: error }) : /* @__PURE__ */ jsx11("div", { className: "text-sm", children }) })
1814
+ ]
1815
+ }
1816
+ );
1817
+ }
1818
+
1819
+ // src/components/MessageList.tsx
1820
+ import * as React10 from "react";
1821
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1822
+ function MessageList({
1823
+ messages,
1824
+ renderMessage,
1825
+ autoScroll = true,
1826
+ throttleMs,
1827
+ className
1828
+ }) {
1829
+ const containerRef = React10.useRef(null);
1830
+ const bottomRef = React10.useRef(null);
1831
+ const scrollToBottom = React10.useCallback(() => {
1832
+ if (bottomRef.current) {
1833
+ bottomRef.current.scrollIntoView({ behavior: "smooth" });
1834
+ }
1835
+ }, []);
1836
+ const throttledScroll = React10.useMemo(
1837
+ () => throttleMs && throttleMs > 0 ? throttle(scrollToBottom, throttleMs) : scrollToBottom,
1838
+ [scrollToBottom, throttleMs]
1839
+ );
1840
+ React10.useEffect(() => {
1841
+ if (autoScroll) {
1842
+ throttledScroll();
1843
+ }
1844
+ }, [messages, autoScroll, throttledScroll]);
1845
+ return /* @__PURE__ */ jsxs12(
1846
+ "div",
1847
+ {
1848
+ ref: containerRef,
1849
+ className: cn(
1850
+ "flex flex-col overflow-y-auto p-4",
1851
+ className
1852
+ ),
1853
+ children: [
1854
+ messages.map(
1855
+ (message) => renderMessage ? /* @__PURE__ */ jsx12(React10.Fragment, { children: renderMessage(message) }, message.id) : /* @__PURE__ */ jsx12(DefaultMessage, { message }, message.id)
1856
+ ),
1857
+ /* @__PURE__ */ jsx12("div", { ref: bottomRef })
1858
+ ]
1859
+ }
1860
+ );
1861
+ }
1862
+ function DefaultMessage({ message }) {
1863
+ return /* @__PURE__ */ jsxs12(ChatMessage, { role: message.role, children: [
1864
+ /* @__PURE__ */ jsx12(ChatMessageAvatar, { src: message.avatar, name: message.name }),
1865
+ /* @__PURE__ */ jsxs12(ChatMessageContent, { name: message.name, children: [
1866
+ message.content,
1867
+ message.timestamp && /* @__PURE__ */ jsx12(ChatMessageTimestamp, { time: message.timestamp })
1868
+ ] })
1869
+ ] });
1870
+ }
1871
+
1872
+ // src/components/SourceBlock.tsx
1873
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1874
+ function SourceBlock({
1875
+ source,
1876
+ showExcerpt = true,
1877
+ className
1878
+ }) {
1879
+ const isUrl = source.sourceType === "url";
1880
+ const Icon = isUrl ? LinkIcon : DocumentIcon;
1881
+ return /* @__PURE__ */ jsx13(
1882
+ "div",
1883
+ {
1884
+ className: cn(
1885
+ "my-2 rounded-lg border border-gray-200 bg-gray-50 overflow-hidden",
1886
+ className
1887
+ ),
1888
+ children: /* @__PURE__ */ jsxs13("div", { className: "flex items-start gap-3 p-3", children: [
1889
+ /* @__PURE__ */ jsx13("div", { className: "flex-shrink-0 w-8 h-8 rounded-md bg-white border border-gray-200 flex items-center justify-center", children: /* @__PURE__ */ jsx13(Icon, { className: "w-4 h-4 text-gray-500" }) }),
1890
+ /* @__PURE__ */ jsxs13("div", { className: "flex-1 min-w-0", children: [
1891
+ isUrl && source.url ? /* @__PURE__ */ jsx13(
1892
+ "a",
1893
+ {
1894
+ href: source.url,
1895
+ target: "_blank",
1896
+ rel: "noopener noreferrer",
1897
+ className: "text-sm font-medium text-blue-600 hover:text-blue-800 hover:underline truncate block",
1898
+ children: source.title
1899
+ }
1900
+ ) : /* @__PURE__ */ jsx13("span", { className: "text-sm font-medium text-gray-900 truncate block", children: source.title }),
1901
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2 mt-0.5 text-xs text-gray-500", children: [
1902
+ source.filename && /* @__PURE__ */ jsx13("span", { className: "truncate", children: source.filename }),
1903
+ source.mediaType && /* @__PURE__ */ jsx13("span", { className: "px-1.5 py-0.5 bg-gray-200 rounded text-gray-600", children: formatMediaType(source.mediaType) }),
1904
+ isUrl && source.url && /* @__PURE__ */ jsx13("span", { className: "truncate", children: new URL(source.url).hostname })
1905
+ ] }),
1906
+ showExcerpt && source.excerpt && /* @__PURE__ */ jsx13("p", { className: "mt-2 text-xs text-gray-600 line-clamp-2", children: source.excerpt })
1907
+ ] })
1908
+ ] })
1909
+ }
1910
+ );
1911
+ }
1912
+ function SourceCitation({
1913
+ source,
1914
+ index,
1915
+ className
1916
+ }) {
1917
+ const isUrl = source.sourceType === "url";
1918
+ if (isUrl && source.url) {
1919
+ return /* @__PURE__ */ jsxs13(
1920
+ "a",
1921
+ {
1922
+ href: source.url,
1923
+ target: "_blank",
1924
+ rel: "noopener noreferrer",
1925
+ className: cn(
1926
+ "inline-flex items-center gap-1 px-1.5 py-0.5 text-xs rounded",
1927
+ "bg-blue-100 text-blue-700 hover:bg-blue-200 transition-colors",
1928
+ className
1929
+ ),
1930
+ children: [
1931
+ index !== void 0 && /* @__PURE__ */ jsxs13("span", { className: "font-medium", children: [
1932
+ "[",
1933
+ index + 1,
1934
+ "]"
1935
+ ] }),
1936
+ /* @__PURE__ */ jsx13("span", { className: "truncate max-w-[150px]", children: source.title })
1937
+ ]
1938
+ }
1939
+ );
1940
+ }
1941
+ return /* @__PURE__ */ jsxs13(
1942
+ "span",
1943
+ {
1944
+ className: cn(
1945
+ "inline-flex items-center gap-1 px-1.5 py-0.5 text-xs rounded",
1946
+ "bg-gray-100 text-gray-700",
1947
+ className
1948
+ ),
1949
+ children: [
1950
+ index !== void 0 && /* @__PURE__ */ jsxs13("span", { className: "font-medium", children: [
1951
+ "[",
1952
+ index + 1,
1953
+ "]"
1954
+ ] }),
1955
+ /* @__PURE__ */ jsx13("span", { className: "truncate max-w-[150px]", children: source.title })
1956
+ ]
1957
+ }
1958
+ );
1959
+ }
1960
+ function SourceList({
1961
+ sources,
1962
+ title = "Sources",
1963
+ className
1964
+ }) {
1965
+ if (sources.length === 0) return null;
1966
+ return /* @__PURE__ */ jsxs13("div", { className: cn("mt-4 pt-4 border-t border-gray-200", className), children: [
1967
+ /* @__PURE__ */ jsx13("h4", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider mb-2", children: title }),
1968
+ /* @__PURE__ */ jsx13("div", { className: "space-y-2", children: sources.map((source, index) => /* @__PURE__ */ jsx13(SourceBlock, { source, showExcerpt: false }, source.sourceId)) })
1969
+ ] });
1970
+ }
1971
+ function formatMediaType(mediaType) {
1972
+ const typeMap = {
1973
+ "application/pdf": "PDF",
1974
+ "application/msword": "DOC",
1975
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "DOCX",
1976
+ "text/plain": "TXT",
1977
+ "text/markdown": "MD",
1978
+ "text/html": "HTML",
1979
+ "application/json": "JSON"
1980
+ };
1981
+ return typeMap[mediaType] || mediaType.split("/").pop()?.toUpperCase() || "FILE";
1982
+ }
1983
+ function LinkIcon({ className }) {
1984
+ return /* @__PURE__ */ jsx13(
1985
+ "svg",
1986
+ {
1987
+ className,
1988
+ fill: "none",
1989
+ viewBox: "0 0 24 24",
1990
+ stroke: "currentColor",
1991
+ strokeWidth: 2,
1992
+ children: /* @__PURE__ */ jsx13(
1993
+ "path",
1994
+ {
1995
+ strokeLinecap: "round",
1996
+ strokeLinejoin: "round",
1997
+ d: "M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"
1998
+ }
1999
+ )
2000
+ }
2001
+ );
2002
+ }
2003
+ function DocumentIcon({ className }) {
2004
+ return /* @__PURE__ */ jsx13(
2005
+ "svg",
2006
+ {
2007
+ className,
2008
+ fill: "none",
2009
+ viewBox: "0 0 24 24",
2010
+ stroke: "currentColor",
2011
+ strokeWidth: 2,
2012
+ children: /* @__PURE__ */ jsx13(
2013
+ "path",
2014
+ {
2015
+ strokeLinecap: "round",
2016
+ strokeLinejoin: "round",
2017
+ d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
2018
+ }
2019
+ )
2020
+ }
2021
+ );
2022
+ }
2023
+
2024
+ // src/components/ChatWindow.tsx
2025
+ import * as React11 from "react";
2026
+ import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2027
+ var ChatWindowContext = React11.createContext(null);
2028
+ function useChatWindowContext() {
2029
+ const context = React11.useContext(ChatWindowContext);
2030
+ if (!context) {
2031
+ throw new Error("ChatWindow components must be used within <ChatWindow>");
2032
+ }
2033
+ return context;
2034
+ }
2035
+ var ChatWindow = React11.forwardRef(
2036
+ ({ agent = null, status = "idle", className, children, ...props }, ref) => {
2037
+ const contextValue = { status, agent };
2038
+ return /* @__PURE__ */ jsx14(ChatWindowContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx14(
2039
+ "div",
2040
+ {
2041
+ ref,
2042
+ className: cn("flex flex-col h-full bg-white", className),
2043
+ "data-status": status,
2044
+ ...props,
2045
+ children
2046
+ }
2047
+ ) });
2048
+ }
2049
+ );
2050
+ ChatWindow.displayName = "ChatWindow";
2051
+ var ChatWindowHeader = React11.forwardRef(
2052
+ ({ agent: agentProp, className, children, ...props }, ref) => {
2053
+ const { agent: contextAgent } = useChatWindowContext();
2054
+ const agent = agentProp ?? contextAgent;
2055
+ return /* @__PURE__ */ jsx14(
2056
+ "div",
2057
+ {
2058
+ ref,
2059
+ className: cn(
2060
+ "flex items-center gap-3 px-4 py-3 border-b border-gray-200",
2061
+ "flex-shrink-0",
2062
+ className
2063
+ ),
2064
+ ...props,
2065
+ children: children ?? /* @__PURE__ */ jsx14(Fragment4, { children: agent && /* @__PURE__ */ jsxs14(Fragment4, { children: [
2066
+ /* @__PURE__ */ jsx14(ChatWindowHeaderAvatar, { agent }),
2067
+ /* @__PURE__ */ jsx14(ChatWindowHeaderInfo, { agent })
2068
+ ] }) })
2069
+ }
2070
+ );
2071
+ }
2072
+ );
2073
+ ChatWindowHeader.displayName = "ChatWindowHeader";
2074
+ var ChatWindowHeaderAvatar = React11.forwardRef(
2075
+ ({ agent, className, ...props }, ref) => /* @__PURE__ */ jsx14("div", { ref, className: cn("flex-shrink-0", className), ...props, children: /* @__PURE__ */ jsxs14(Avatar, { size: "md", status: agent.status === "online" ? "idle" : void 0, children: [
2076
+ agent.avatar && /* @__PURE__ */ jsx14(AvatarImage, { src: agent.avatar, alt: agent.name }),
2077
+ /* @__PURE__ */ jsx14(AvatarFallback, { variant: "secondary", children: agent.name.charAt(0).toUpperCase() })
2078
+ ] }) })
2079
+ );
2080
+ ChatWindowHeaderAvatar.displayName = "ChatWindowHeaderAvatar";
2081
+ var statusTextMap = {
2082
+ online: "\u5728\u7EBF",
2083
+ offline: "\u79BB\u7EBF",
2084
+ busy: "\u5FD9\u788C"
2085
+ };
2086
+ var ChatWindowHeaderInfo = React11.forwardRef(
2087
+ ({ agent, className, ...props }, ref) => /* @__PURE__ */ jsxs14("div", { ref, className: cn("flex-1 min-w-0", className), ...props, children: [
2088
+ /* @__PURE__ */ jsx14("h2", { className: "font-semibold text-gray-900 truncate", children: agent.name }),
2089
+ agent.description && /* @__PURE__ */ jsx14("p", { className: "text-sm text-gray-500 truncate", children: agent.description }),
2090
+ agent.status && !agent.description && /* @__PURE__ */ jsx14(
2091
+ "span",
2092
+ {
2093
+ className: cn(
2094
+ "text-xs",
2095
+ agent.status === "online" && "text-green-500",
2096
+ agent.status === "offline" && "text-gray-400",
2097
+ agent.status === "busy" && "text-orange-500"
2098
+ ),
2099
+ children: statusTextMap[agent.status]
2100
+ }
2101
+ )
2102
+ ] })
2103
+ );
2104
+ ChatWindowHeaderInfo.displayName = "ChatWindowHeaderInfo";
2105
+ var ChatWindowHeaderActions = React11.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx14("div", { ref, className: cn("flex items-center gap-1", className), ...props, children }));
2106
+ ChatWindowHeaderActions.displayName = "ChatWindowHeaderActions";
2107
+ var ChatWindowMessages = React11.forwardRef(
2108
+ ({ autoScroll = true, throttleMs = 50, className, children, ...props }, ref) => {
2109
+ const bottomRef = React11.useRef(null);
2110
+ React11.useEffect(() => {
2111
+ if (autoScroll && bottomRef.current) {
2112
+ bottomRef.current.scrollIntoView({ behavior: "smooth" });
2113
+ }
2114
+ }, [children, autoScroll]);
2115
+ return /* @__PURE__ */ jsxs14(
2116
+ "div",
2117
+ {
2118
+ ref,
2119
+ className: cn("flex-1 overflow-y-auto p-4 space-y-4", className),
2120
+ ...props,
2121
+ children: [
2122
+ children,
2123
+ /* @__PURE__ */ jsx14("div", { ref: bottomRef })
2124
+ ]
2125
+ }
2126
+ );
2127
+ }
2128
+ );
2129
+ ChatWindowMessages.displayName = "ChatWindowMessages";
2130
+ var ChatWindowInput = React11.forwardRef(
2131
+ ({ onSend, value, onValueChange, disabled, placeholder, className, children, ...props }, ref) => {
2132
+ const { status } = useChatWindowContext();
2133
+ const [internalValue, setInternalValue] = React11.useState("");
2134
+ const inputValue = value ?? internalValue;
2135
+ const setInputValue = onValueChange ?? setInternalValue;
2136
+ const isDisabled = disabled || status === "streaming";
2137
+ const handleSubmit = () => {
2138
+ if (inputValue.trim() && onSend) {
2139
+ onSend(inputValue.trim());
2140
+ setInternalValue("");
2141
+ if (onValueChange) {
2142
+ onValueChange("");
2143
+ }
2144
+ }
2145
+ };
2146
+ if (children) {
2147
+ return /* @__PURE__ */ jsx14(
2148
+ "div",
2149
+ {
2150
+ ref,
2151
+ className: cn("flex-shrink-0 border-t border-gray-200 p-4", className),
2152
+ ...props,
2153
+ children
2154
+ }
2155
+ );
2156
+ }
2157
+ return /* @__PURE__ */ jsx14(
2158
+ "div",
2159
+ {
2160
+ ref,
2161
+ className: cn("flex-shrink-0 border-t border-gray-200 p-4", className),
2162
+ ...props,
2163
+ children: /* @__PURE__ */ jsxs14(ChatInput, { onSubmit: handleSubmit, children: [
2164
+ /* @__PURE__ */ jsx14(
2165
+ ChatInputTextarea,
2166
+ {
2167
+ value: inputValue,
2168
+ onChange: setInputValue,
2169
+ placeholder: placeholder ?? "\u8F93\u5165\u6D88\u606F...",
2170
+ disabled: isDisabled
2171
+ }
2172
+ ),
2173
+ /* @__PURE__ */ jsxs14(ChatInputToolbar, { children: [
2174
+ /* @__PURE__ */ jsx14(ChatInputTools, {}),
2175
+ /* @__PURE__ */ jsx14(
2176
+ ChatInputSubmit,
2177
+ {
2178
+ status: status === "streaming" ? "streaming" : "idle",
2179
+ disabled: !inputValue.trim()
2180
+ }
2181
+ )
2182
+ ] })
2183
+ ] })
2184
+ }
2185
+ );
2186
+ }
2187
+ );
2188
+ ChatWindowInput.displayName = "ChatWindowInput";
2189
+ var ChatWindowEmpty = React11.forwardRef(
2190
+ ({ icon, title = "\u5F00\u59CB\u65B0\u5BF9\u8BDD", description = "\u5411 AI \u52A9\u624B\u63D0\u95EE\u4EFB\u4F55\u95EE\u9898", className, children, ...props }, ref) => /* @__PURE__ */ jsxs14(
2191
+ "div",
2192
+ {
2193
+ ref,
2194
+ className: cn(
2195
+ "flex-1 flex flex-col items-center justify-center p-8 text-center",
2196
+ className
2197
+ ),
2198
+ ...props,
2199
+ children: [
2200
+ icon ?? /* @__PURE__ */ jsx14("div", { className: "w-16 h-16 rounded-full bg-gray-100 flex items-center justify-center mb-4", children: /* @__PURE__ */ jsx14(
2201
+ "svg",
2202
+ {
2203
+ className: "w-8 h-8 text-gray-400",
2204
+ fill: "none",
2205
+ viewBox: "0 0 24 24",
2206
+ stroke: "currentColor",
2207
+ children: /* @__PURE__ */ jsx14(
2208
+ "path",
2209
+ {
2210
+ strokeLinecap: "round",
2211
+ strokeLinejoin: "round",
2212
+ strokeWidth: 1.5,
2213
+ d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
2214
+ }
2215
+ )
2216
+ }
2217
+ ) }),
2218
+ /* @__PURE__ */ jsx14("h3", { className: "text-lg font-medium text-gray-900", children: title }),
2219
+ description && /* @__PURE__ */ jsx14("p", { className: "text-sm text-gray-500 mt-1", children: description }),
2220
+ children
2221
+ ]
2222
+ }
2223
+ )
2224
+ );
2225
+ ChatWindowEmpty.displayName = "ChatWindowEmpty";
2226
+
2227
+ // src/components/ChatList.tsx
2228
+ import * as React12 from "react";
2229
+ import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
2230
+ var ChatListContext = React12.createContext(null);
2231
+ function useChatListContext() {
2232
+ const context = React12.useContext(ChatListContext);
2233
+ if (!context) {
2234
+ throw new Error("ChatList components must be used within <ChatList>");
2235
+ }
2236
+ return context;
2237
+ }
2238
+ var ChatList = React12.forwardRef(
2239
+ ({ activeId = null, onSelect, className, children, ...props }, ref) => {
2240
+ const contextValue = {
2241
+ activeId,
2242
+ onSelect: onSelect ?? (() => {
2243
+ })
2244
+ };
2245
+ return /* @__PURE__ */ jsx15(ChatListContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx15(
2246
+ "div",
2247
+ {
2248
+ ref,
2249
+ className: cn(
2250
+ "flex flex-col h-full bg-white border-r border-gray-200",
2251
+ className
2252
+ ),
2253
+ ...props,
2254
+ children
2255
+ }
2256
+ ) });
2257
+ }
2258
+ );
2259
+ ChatList.displayName = "ChatList";
2260
+ var ChatListHeader = React12.forwardRef(
2261
+ ({ title = "\u4F1A\u8BDD", className, children, ...props }, ref) => /* @__PURE__ */ jsx15(
2262
+ "div",
2263
+ {
2264
+ ref,
2265
+ className: cn(
2266
+ "flex items-center justify-between px-4 py-3 border-b border-gray-200",
2267
+ "flex-shrink-0",
2268
+ className
2269
+ ),
2270
+ ...props,
2271
+ children: children ?? /* @__PURE__ */ jsx15("h2", { className: "font-semibold text-gray-900", children: title })
2272
+ }
2273
+ )
2274
+ );
2275
+ ChatListHeader.displayName = "ChatListHeader";
2276
+ var ChatListSearch = React12.forwardRef(
2277
+ ({ placeholder = "\u641C\u7D22\u4F1A\u8BDD...", value, onChange, className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { className: cn("p-3", className), children: /* @__PURE__ */ jsxs15("div", { className: "relative", children: [
2278
+ /* @__PURE__ */ jsx15(
2279
+ "svg",
2280
+ {
2281
+ className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400",
2282
+ fill: "none",
2283
+ viewBox: "0 0 24 24",
2284
+ stroke: "currentColor",
2285
+ children: /* @__PURE__ */ jsx15(
2286
+ "path",
2287
+ {
2288
+ strokeLinecap: "round",
2289
+ strokeLinejoin: "round",
2290
+ strokeWidth: 2,
2291
+ d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
2292
+ }
2293
+ )
2294
+ }
2295
+ ),
2296
+ /* @__PURE__ */ jsx15(
2297
+ "input",
2298
+ {
2299
+ ref,
2300
+ type: "text",
2301
+ value,
2302
+ onChange: (e) => onChange?.(e.target.value),
2303
+ placeholder,
2304
+ className: cn(
2305
+ "w-full pl-9 pr-3 py-2 text-sm rounded-lg",
2306
+ "border border-gray-200 bg-gray-50",
2307
+ "focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",
2308
+ "placeholder:text-gray-400"
2309
+ ),
2310
+ ...props
2311
+ }
2312
+ )
2313
+ ] }) })
2314
+ );
2315
+ ChatListSearch.displayName = "ChatListSearch";
2316
+ var ChatListGroup = React12.forwardRef(
2317
+ ({ label, className, children, ...props }, ref) => /* @__PURE__ */ jsxs15("div", { ref, className: cn("py-1", className), ...props, children: [
2318
+ label && /* @__PURE__ */ jsx15("div", { className: "px-4 py-2 text-xs font-medium text-gray-500 uppercase tracking-wider", children: label }),
2319
+ children
2320
+ ] })
2321
+ );
2322
+ ChatListGroup.displayName = "ChatListGroup";
2323
+ var ChatListItem = React12.forwardRef(
2324
+ ({ conversation, className, children, ...props }, ref) => {
2325
+ const { activeId, onSelect } = useChatListContext();
2326
+ const isActive = activeId === conversation.id;
2327
+ return /* @__PURE__ */ jsx15(
2328
+ "div",
2329
+ {
2330
+ ref,
2331
+ role: "button",
2332
+ tabIndex: 0,
2333
+ onClick: () => onSelect(conversation.id),
2334
+ onKeyDown: (e) => e.key === "Enter" && onSelect(conversation.id),
2335
+ className: cn(
2336
+ "flex items-center gap-3 px-4 py-3 cursor-pointer transition-colors",
2337
+ "hover:bg-gray-50",
2338
+ isActive && "bg-blue-50 hover:bg-blue-50",
2339
+ className
2340
+ ),
2341
+ "data-active": isActive,
2342
+ ...props,
2343
+ children: children ?? /* @__PURE__ */ jsxs15(Fragment5, { children: [
2344
+ /* @__PURE__ */ jsx15(ChatListItemAvatar, { agent: conversation.agent }),
2345
+ /* @__PURE__ */ jsx15(ChatListItemContent, { conversation }),
2346
+ conversation.unreadCount && conversation.unreadCount > 0 && /* @__PURE__ */ jsx15(ChatListItemBadge, { count: conversation.unreadCount })
2347
+ ] })
2348
+ }
2349
+ );
2350
+ }
2351
+ );
2352
+ ChatListItem.displayName = "ChatListItem";
2353
+ var ChatListItemAvatar = React12.forwardRef(
2354
+ ({ agent, className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("flex-shrink-0", className), ...props, children: /* @__PURE__ */ jsxs15(Avatar, { size: "sm", children: [
2355
+ agent?.avatar && /* @__PURE__ */ jsx15(AvatarImage, { src: agent.avatar, alt: agent.name }),
2356
+ /* @__PURE__ */ jsx15(AvatarFallback, { variant: "secondary", children: agent?.name?.charAt(0).toUpperCase() || "A" })
2357
+ ] }) })
2358
+ );
2359
+ ChatListItemAvatar.displayName = "ChatListItemAvatar";
2360
+ var ChatListItemContent = React12.forwardRef(
2361
+ ({ conversation, className, ...props }, ref) => /* @__PURE__ */ jsxs15("div", { ref, className: cn("flex-1 min-w-0", className), ...props, children: [
2362
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between", children: [
2363
+ /* @__PURE__ */ jsx15("span", { className: "font-medium text-gray-900 truncate", children: conversation.title }),
2364
+ conversation.lastActiveAt && /* @__PURE__ */ jsx15("span", { className: "text-xs text-gray-400 flex-shrink-0 ml-2", children: formatRelativeTime(conversation.lastActiveAt) })
2365
+ ] }),
2366
+ conversation.lastMessage && /* @__PURE__ */ jsx15("p", { className: "text-sm text-gray-500 truncate", children: conversation.lastMessage })
2367
+ ] })
2368
+ );
2369
+ ChatListItemContent.displayName = "ChatListItemContent";
2370
+ var ChatListItemBadge = React12.forwardRef(
2371
+ ({ count, className, ...props }, ref) => /* @__PURE__ */ jsx15(
2372
+ "span",
2373
+ {
2374
+ ref,
2375
+ className: cn(
2376
+ "flex-shrink-0 min-w-[20px] h-5 px-1.5 rounded-full",
2377
+ "bg-blue-500 text-white text-xs font-medium",
2378
+ "flex items-center justify-center",
2379
+ className
2380
+ ),
2381
+ ...props,
2382
+ children: count > 99 ? "99+" : count
2383
+ }
2384
+ )
2385
+ );
2386
+ ChatListItemBadge.displayName = "ChatListItemBadge";
2387
+ var ChatListEmpty = React12.forwardRef(
2388
+ ({ icon, title = "\u6682\u65E0\u4F1A\u8BDD", description, className, children, ...props }, ref) => /* @__PURE__ */ jsxs15(
2389
+ "div",
2390
+ {
2391
+ ref,
2392
+ className: cn("flex flex-col items-center justify-center py-12 px-4", className),
2393
+ ...props,
2394
+ children: [
2395
+ icon ?? /* @__PURE__ */ jsx15(
2396
+ "svg",
2397
+ {
2398
+ className: "w-12 h-12 text-gray-300 mb-4",
2399
+ fill: "none",
2400
+ viewBox: "0 0 24 24",
2401
+ stroke: "currentColor",
2402
+ children: /* @__PURE__ */ jsx15(
2403
+ "path",
2404
+ {
2405
+ strokeLinecap: "round",
2406
+ strokeLinejoin: "round",
2407
+ strokeWidth: 1.5,
2408
+ d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
2409
+ }
2410
+ )
2411
+ }
2412
+ ),
2413
+ /* @__PURE__ */ jsx15("p", { className: "text-gray-900 font-medium", children: title }),
2414
+ description && /* @__PURE__ */ jsx15("p", { className: "text-sm text-gray-500 mt-1", children: description }),
2415
+ children
2416
+ ]
2417
+ }
2418
+ )
2419
+ );
2420
+ ChatListEmpty.displayName = "ChatListEmpty";
2421
+
2422
+ // src/components/AgentChat.tsx
2423
+ import * as React13 from "react";
2424
+ import {
2425
+ isTextBlock,
2426
+ isThinkingBlock,
2427
+ isToolBlock,
2428
+ isSourceBlock,
2429
+ isErrorBlock
2430
+ } from "@uix-ai/core";
2431
+ import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
2432
+ function DefaultBlockRenderer({
2433
+ block
2434
+ }) {
2435
+ if (isTextBlock(block)) {
2436
+ const content = block.content;
2437
+ return /* @__PURE__ */ jsxs16("div", { className: "whitespace-pre-wrap break-words", children: [
2438
+ content.text,
2439
+ block.status === "streaming" && /* @__PURE__ */ jsx16("span", { className: "inline-block w-1.5 h-4 bg-gray-400 ml-0.5 animate-pulse align-text-bottom" })
2440
+ ] });
2441
+ }
2442
+ if (isThinkingBlock(block)) {
2443
+ const content = block.content;
2444
+ if (block.status === "streaming") {
2445
+ return /* @__PURE__ */ jsx16(ThinkingIndicator, { label: content.reasoning || void 0 });
2446
+ }
2447
+ if (content.reasoning) {
2448
+ return /* @__PURE__ */ jsxs16("details", { className: "text-sm text-gray-500", children: [
2449
+ /* @__PURE__ */ jsx16("summary", { className: "cursor-pointer hover:text-gray-700", children: "\u601D\u8003\u8FC7\u7A0B" }),
2450
+ /* @__PURE__ */ jsx16("div", { className: "mt-1 pl-3 border-l-2 border-gray-200 whitespace-pre-wrap", children: content.reasoning })
2451
+ ] });
2452
+ }
2453
+ return null;
2454
+ }
2455
+ if (isToolBlock(block)) {
2456
+ const content = block.content;
2457
+ return /* @__PURE__ */ jsx16(
2458
+ ToolResult,
2459
+ {
2460
+ tool: content.name,
2461
+ status: content.status,
2462
+ error: content.error,
2463
+ children: content.output != null && /* @__PURE__ */ jsx16("pre", { className: "text-xs overflow-x-auto", children: typeof content.output === "string" ? content.output : JSON.stringify(content.output, null, 2) })
2464
+ }
2465
+ );
2466
+ }
2467
+ if (isSourceBlock(block)) {
2468
+ const content = block.content;
2469
+ return /* @__PURE__ */ jsx16(SourceBlock, { source: content });
2470
+ }
2471
+ if (isErrorBlock(block)) {
2472
+ const content = block.content;
2473
+ return /* @__PURE__ */ jsx16("div", { className: "text-sm text-red-600 bg-red-50 rounded px-3 py-2", children: content.message });
2474
+ }
2475
+ return null;
2476
+ }
2477
+ function ConversationMessage({
2478
+ conversation,
2479
+ renderBlock,
2480
+ onToolApprove,
2481
+ onToolDeny
2482
+ }) {
2483
+ const sourceBlocks = [];
2484
+ const contentBlocks = [];
2485
+ for (const block of conversation.blocks) {
2486
+ if (isSourceBlock(block)) {
2487
+ sourceBlocks.push(block);
2488
+ } else {
2489
+ contentBlocks.push(block);
2490
+ }
2491
+ }
2492
+ return /* @__PURE__ */ jsxs16(ChatMessage, { role: conversation.role, status: conversation.status === "streaming" ? "streaming" : "complete", children: [
2493
+ /* @__PURE__ */ jsx16(
2494
+ ChatMessageAvatar,
2495
+ {
2496
+ name: conversation.role === "user" ? "\u7528\u6237" : "AI"
2497
+ }
2498
+ ),
2499
+ /* @__PURE__ */ jsxs16(ChatMessageContent, { children: [
2500
+ /* @__PURE__ */ jsxs16("div", { className: "space-y-2", children: [
2501
+ contentBlocks.map((block) => {
2502
+ if (renderBlock) {
2503
+ const custom = renderBlock(block, conversation);
2504
+ if (custom !== null) {
2505
+ return /* @__PURE__ */ jsx16(React13.Fragment, { children: custom }, block.id);
2506
+ }
2507
+ }
2508
+ return /* @__PURE__ */ jsx16(DefaultBlockRenderer, { block }, block.id);
2509
+ }),
2510
+ sourceBlocks.length > 0 && /* @__PURE__ */ jsx16(
2511
+ SourceList,
2512
+ {
2513
+ sources: sourceBlocks.map((block) => block.content)
2514
+ }
2515
+ )
2516
+ ] }),
2517
+ /* @__PURE__ */ jsx16(ChatMessageTimestamp, { time: new Date(conversation.timestamp) })
2518
+ ] })
2519
+ ] });
2520
+ }
2521
+ var AgentChat = React13.forwardRef(
2522
+ ({
2523
+ conversations,
2524
+ agent,
2525
+ status = "idle",
2526
+ onSend,
2527
+ onStop,
2528
+ onRetry,
2529
+ onToolApprove,
2530
+ onToolDeny,
2531
+ placeholder,
2532
+ emptyState,
2533
+ renderBlock,
2534
+ showHeader,
2535
+ autoScroll = true,
2536
+ className,
2537
+ ...props
2538
+ }, ref) => {
2539
+ const shouldShowHeader = showHeader ?? !!agent;
2540
+ return /* @__PURE__ */ jsxs16(
2541
+ ChatWindow,
2542
+ {
2543
+ ref,
2544
+ agent,
2545
+ status,
2546
+ className: cn("h-full", className),
2547
+ ...props,
2548
+ children: [
2549
+ shouldShowHeader && /* @__PURE__ */ jsx16(ChatWindowHeader, {}),
2550
+ conversations.length === 0 ? /* @__PURE__ */ jsx16(
2551
+ ChatWindowEmpty,
2552
+ {
2553
+ icon: emptyState?.icon,
2554
+ title: emptyState?.title,
2555
+ description: emptyState?.description
2556
+ }
2557
+ ) : /* @__PURE__ */ jsx16(ChatWindowMessages, { autoScroll, children: conversations.map((conversation) => /* @__PURE__ */ jsx16(
2558
+ ConversationMessage,
2559
+ {
2560
+ conversation,
2561
+ renderBlock,
2562
+ onToolApprove,
2563
+ onToolDeny
2564
+ },
2565
+ conversation.id
2566
+ )) }),
2567
+ /* @__PURE__ */ jsx16(
2568
+ ChatWindowInput,
2569
+ {
2570
+ onSend,
2571
+ placeholder
2572
+ }
2573
+ )
2574
+ ]
2575
+ }
2576
+ );
2577
+ }
2578
+ );
2579
+ AgentChat.displayName = "AgentChat";
2580
+ export {
2581
+ AgentAvatar,
2582
+ AgentChat,
2583
+ Avatar,
2584
+ AvatarFallback,
2585
+ AvatarGroup,
2586
+ AvatarImage,
2587
+ AvatarStatusIndicator,
2588
+ BounceAnimation,
2589
+ ChatBubble,
2590
+ ChatInput,
2591
+ ChatInputButton,
2592
+ ChatInputSubmit,
2593
+ ChatInputTextarea,
2594
+ ChatInputToolbar,
2595
+ ChatInputTools,
2596
+ ChatList,
2597
+ ChatListEmpty,
2598
+ ChatListGroup,
2599
+ ChatListHeader,
2600
+ ChatListItem,
2601
+ ChatListItemAvatar,
2602
+ ChatListItemBadge,
2603
+ ChatListItemContent,
2604
+ ChatListSearch,
2605
+ ChatMessage,
2606
+ ChatMessageAvatar,
2607
+ ChatMessageContent,
2608
+ ChatMessageError,
2609
+ ChatMessageSimple,
2610
+ ChatMessageThinking,
2611
+ ChatMessageTimestamp,
2612
+ ChatWindow,
2613
+ ChatWindowEmpty,
2614
+ ChatWindowHeader,
2615
+ ChatWindowHeaderActions,
2616
+ ChatWindowHeaderAvatar,
2617
+ ChatWindowHeaderInfo,
2618
+ ChatWindowInput,
2619
+ ChatWindowMessages,
2620
+ DotsAnimation,
2621
+ MentionItem,
2622
+ MentionPopover,
2623
+ MessageAvatar,
2624
+ MessageList,
2625
+ PulseAnimation,
2626
+ SourceBlock,
2627
+ SourceCitation,
2628
+ SourceList,
2629
+ SpinnerAnimation,
2630
+ StreamCursor,
2631
+ StreamText,
2632
+ ThinkingIndicator,
2633
+ ToolResult,
2634
+ WaveAnimation,
2635
+ sizeClasses as avatarSizeClasses,
2636
+ variantClasses as avatarVariantClasses,
2637
+ cn,
2638
+ formatRelativeTime,
2639
+ presenceColors,
2640
+ roleColors,
2641
+ roleSymbols,
2642
+ roleToVariant,
2643
+ statusAnimations,
2644
+ useAsyncStream,
2645
+ useChatListContext,
2646
+ useChatMessageContext,
2647
+ useChatWindowContext,
2648
+ useImageLoadingStatus,
2649
+ useMentionContext,
2650
+ useMentionPopover,
2651
+ useTypingAnimation
2652
+ };
2653
+ //# sourceMappingURL=index.js.map