@wallavi/widget 1.6.0 → 1.6.2
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.d.mts +20 -4
- package/dist/index.d.ts +20 -4
- package/dist/index.js +208 -44
- package/dist/index.mjs +209 -45
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -21,17 +21,31 @@ type PickerPart = {
|
|
|
21
21
|
}>;
|
|
22
22
|
selectedValue?: string;
|
|
23
23
|
};
|
|
24
|
+
type PlanStepState = {
|
|
25
|
+
index: number;
|
|
26
|
+
description: string;
|
|
27
|
+
status: "pending" | "executing" | "success" | "failed";
|
|
28
|
+
error?: string;
|
|
29
|
+
};
|
|
30
|
+
type PlanPart = {
|
|
31
|
+
type: "plan";
|
|
32
|
+
planId: string;
|
|
33
|
+
goal: string;
|
|
34
|
+
steps: PlanStepState[];
|
|
35
|
+
};
|
|
24
36
|
type MessagePart = {
|
|
25
37
|
type: "text";
|
|
26
38
|
text: string;
|
|
27
39
|
} | {
|
|
28
40
|
type: "reasoning";
|
|
29
41
|
text: string;
|
|
30
|
-
} | ToolPart | PickerPart;
|
|
42
|
+
} | ToolPart | PickerPart | PlanPart;
|
|
31
43
|
type Message = {
|
|
32
44
|
id: string;
|
|
33
45
|
role: "user" | "assistant";
|
|
34
46
|
parts: MessagePart[];
|
|
47
|
+
/** Files the user attached to this message — stored for display in the chat thread. */
|
|
48
|
+
attachments?: AttachmentPayload[];
|
|
35
49
|
};
|
|
36
50
|
interface PageContext {
|
|
37
51
|
/** Current URL or path (e.g. "/dashboard/agent/abc/agent-studio") */
|
|
@@ -101,9 +115,9 @@ interface ChatWidgetConfig {
|
|
|
101
115
|
*/
|
|
102
116
|
voiceAutoSend?: boolean;
|
|
103
117
|
/**
|
|
104
|
-
* Show the paperclip button
|
|
105
|
-
*
|
|
106
|
-
*
|
|
118
|
+
* Show the paperclip button and enable drag-and-drop / clipboard-paste for file attachments.
|
|
119
|
+
* Supported types: CSV, TXT, PDF (text extracted as context), JPG, PNG, WebP, GIF (stored in Vercel Blob).
|
|
120
|
+
* Default: false.
|
|
107
121
|
*/
|
|
108
122
|
enableAttachments?: boolean;
|
|
109
123
|
}
|
|
@@ -125,6 +139,8 @@ interface AttachmentPayload {
|
|
|
125
139
|
contentType: AttachmentContentType;
|
|
126
140
|
textContent?: string;
|
|
127
141
|
base64?: string;
|
|
142
|
+
/** Persistent public URL — set by upload.ts when Vercel Blob is configured. */
|
|
143
|
+
url?: string;
|
|
128
144
|
rowCount?: number;
|
|
129
145
|
truncated?: boolean;
|
|
130
146
|
sizeBytes: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -21,17 +21,31 @@ type PickerPart = {
|
|
|
21
21
|
}>;
|
|
22
22
|
selectedValue?: string;
|
|
23
23
|
};
|
|
24
|
+
type PlanStepState = {
|
|
25
|
+
index: number;
|
|
26
|
+
description: string;
|
|
27
|
+
status: "pending" | "executing" | "success" | "failed";
|
|
28
|
+
error?: string;
|
|
29
|
+
};
|
|
30
|
+
type PlanPart = {
|
|
31
|
+
type: "plan";
|
|
32
|
+
planId: string;
|
|
33
|
+
goal: string;
|
|
34
|
+
steps: PlanStepState[];
|
|
35
|
+
};
|
|
24
36
|
type MessagePart = {
|
|
25
37
|
type: "text";
|
|
26
38
|
text: string;
|
|
27
39
|
} | {
|
|
28
40
|
type: "reasoning";
|
|
29
41
|
text: string;
|
|
30
|
-
} | ToolPart | PickerPart;
|
|
42
|
+
} | ToolPart | PickerPart | PlanPart;
|
|
31
43
|
type Message = {
|
|
32
44
|
id: string;
|
|
33
45
|
role: "user" | "assistant";
|
|
34
46
|
parts: MessagePart[];
|
|
47
|
+
/** Files the user attached to this message — stored for display in the chat thread. */
|
|
48
|
+
attachments?: AttachmentPayload[];
|
|
35
49
|
};
|
|
36
50
|
interface PageContext {
|
|
37
51
|
/** Current URL or path (e.g. "/dashboard/agent/abc/agent-studio") */
|
|
@@ -101,9 +115,9 @@ interface ChatWidgetConfig {
|
|
|
101
115
|
*/
|
|
102
116
|
voiceAutoSend?: boolean;
|
|
103
117
|
/**
|
|
104
|
-
* Show the paperclip button
|
|
105
|
-
*
|
|
106
|
-
*
|
|
118
|
+
* Show the paperclip button and enable drag-and-drop / clipboard-paste for file attachments.
|
|
119
|
+
* Supported types: CSV, TXT, PDF (text extracted as context), JPG, PNG, WebP, GIF (stored in Vercel Blob).
|
|
120
|
+
* Default: false.
|
|
107
121
|
*/
|
|
108
122
|
enableAttachments?: boolean;
|
|
109
123
|
}
|
|
@@ -125,6 +139,8 @@ interface AttachmentPayload {
|
|
|
125
139
|
contentType: AttachmentContentType;
|
|
126
140
|
textContent?: string;
|
|
127
141
|
base64?: string;
|
|
142
|
+
/** Persistent public URL — set by upload.ts when Vercel Blob is configured. */
|
|
143
|
+
url?: string;
|
|
128
144
|
rowCount?: number;
|
|
129
145
|
truncated?: boolean;
|
|
130
146
|
sizeBytes: number;
|
package/dist/index.js
CHANGED
|
@@ -205,6 +205,30 @@ function useChat({
|
|
|
205
205
|
});
|
|
206
206
|
break;
|
|
207
207
|
}
|
|
208
|
+
case "plan-created": {
|
|
209
|
+
msg.parts.push({
|
|
210
|
+
type: "plan",
|
|
211
|
+
planId: proto.planId,
|
|
212
|
+
goal: proto.goal,
|
|
213
|
+
steps: proto.steps.map((s) => ({ ...s, status: "pending" }))
|
|
214
|
+
});
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
case "plan-step-update": {
|
|
218
|
+
const pIdx = msg.parts.findIndex(
|
|
219
|
+
(p) => p.type === "plan" && p.planId === proto.planId
|
|
220
|
+
);
|
|
221
|
+
if (pIdx !== -1) {
|
|
222
|
+
const prev2 = msg.parts[pIdx];
|
|
223
|
+
msg.parts[pIdx] = {
|
|
224
|
+
...prev2,
|
|
225
|
+
steps: prev2.steps.map(
|
|
226
|
+
(s) => s.index === proto.stepIndex ? { ...s, status: proto.status, ...proto.error ? { error: proto.error } : {} } : s
|
|
227
|
+
)
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
208
232
|
}
|
|
209
233
|
const copy = [...prev];
|
|
210
234
|
copy[idx] = msg;
|
|
@@ -254,17 +278,22 @@ function useChat({
|
|
|
254
278
|
const userInput = (text ?? input).trim();
|
|
255
279
|
if (!userInput || streaming) return;
|
|
256
280
|
setInput("");
|
|
281
|
+
const attachments = pendingAttachmentsRef.current.length > 0 ? [...pendingAttachmentsRef.current] : void 0;
|
|
282
|
+
pendingAttachmentsRef.current = [];
|
|
257
283
|
const userMsgId = newId();
|
|
258
284
|
setMessages((prev) => [
|
|
259
285
|
...prev,
|
|
260
|
-
{
|
|
286
|
+
{
|
|
287
|
+
id: userMsgId,
|
|
288
|
+
role: "user",
|
|
289
|
+
parts: [{ type: "text", text: userInput }],
|
|
290
|
+
...attachments ? { attachments } : {}
|
|
291
|
+
}
|
|
261
292
|
]);
|
|
262
293
|
setStreaming(true);
|
|
263
294
|
const assistantMsgId = newId();
|
|
264
295
|
streamingMsgIdRef.current = assistantMsgId;
|
|
265
296
|
setMessages((prev) => [...prev, { id: assistantMsgId, role: "assistant", parts: [] }]);
|
|
266
|
-
const attachments = pendingAttachmentsRef.current.length > 0 ? [...pendingAttachmentsRef.current] : void 0;
|
|
267
|
-
pendingAttachmentsRef.current = [];
|
|
268
297
|
try {
|
|
269
298
|
await fetchAndStream({ input: userInput, msgId: assistantMsgId, attachments });
|
|
270
299
|
} catch {
|
|
@@ -606,10 +635,166 @@ function ChatHeader({
|
|
|
606
635
|
}
|
|
607
636
|
);
|
|
608
637
|
}
|
|
638
|
+
var cn2 = (...inputs) => tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
639
|
+
function formatBytes(bytes) {
|
|
640
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
641
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
|
|
642
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
643
|
+
}
|
|
644
|
+
function ChipLeading({ a }) {
|
|
645
|
+
if (a.status === "uploading") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3 w-3 shrink-0 animate-spin" });
|
|
646
|
+
if (a.status === "error") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-3 w-3 shrink-0" });
|
|
647
|
+
const thumbSrc = a.payload?.url ?? a.payload?.base64;
|
|
648
|
+
if (a.mimeType.startsWith("image/") && thumbSrc) {
|
|
649
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
650
|
+
"img",
|
|
651
|
+
{
|
|
652
|
+
src: thumbSrc,
|
|
653
|
+
alt: a.name,
|
|
654
|
+
className: "h-6 w-6 rounded object-cover shrink-0 border border-border/40"
|
|
655
|
+
}
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
if (a.mimeType.startsWith("image/")) return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-6 rounded bg-muted-foreground/10 border border-border/30 shrink-0 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[8px] font-semibold text-muted-foreground/50", children: "IMG" }) });
|
|
659
|
+
if (a.mimeType === "application/pdf") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "h-3 w-3 shrink-0 text-red-400" });
|
|
660
|
+
if (a.mimeType.includes("csv") || a.mimeType.includes("spreadsheet") || a.name.endsWith(".csv")) {
|
|
661
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileSpreadsheet, { className: "h-3 w-3 shrink-0 text-emerald-500" });
|
|
662
|
+
}
|
|
663
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "h-3 w-3 shrink-0" });
|
|
664
|
+
}
|
|
665
|
+
function AttachmentChips({ attachments, onRemove }) {
|
|
666
|
+
if (attachments.length === 0) return null;
|
|
667
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5 px-1 pb-1.5", children: attachments.map((a) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
668
|
+
"div",
|
|
669
|
+
{
|
|
670
|
+
className: cn2(
|
|
671
|
+
"flex items-center gap-1.5 rounded-lg border px-2 py-1 text-[11px] max-w-[200px]",
|
|
672
|
+
a.status === "error" ? "border-red-200 bg-red-50 text-red-600 dark:border-red-800 dark:bg-red-950 dark:text-red-400" : "border-border bg-muted/60 text-muted-foreground"
|
|
673
|
+
),
|
|
674
|
+
children: [
|
|
675
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChipLeading, { a }),
|
|
676
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate leading-tight", children: a.status === "error" ? a.errorMessage ?? "Upload failed" : a.name }),
|
|
677
|
+
a.status === "ready" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground/50", children: formatBytes(a.sizeBytes) }),
|
|
678
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
679
|
+
"button",
|
|
680
|
+
{
|
|
681
|
+
type: "button",
|
|
682
|
+
onClick: () => onRemove(a.id),
|
|
683
|
+
className: "shrink-0 rounded-full p-0.5 hover:bg-foreground/10 transition-colors ml-0.5",
|
|
684
|
+
title: "Remove",
|
|
685
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-2.5 w-2.5" })
|
|
686
|
+
}
|
|
687
|
+
)
|
|
688
|
+
]
|
|
689
|
+
},
|
|
690
|
+
a.id
|
|
691
|
+
)) });
|
|
692
|
+
}
|
|
609
693
|
var Avatar2 = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
|
|
610
694
|
var AvatarImage2 = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
|
|
611
695
|
var AvatarFallback2 = ({ style, ...p }) => /* @__PURE__ */ jsxRuntime.jsx(AvatarPrimitive__namespace.Fallback, { style: { display: "flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", borderRadius: "9999px", ...style }, ...p });
|
|
612
696
|
var ReactMarkdown = ReactMarkdownLib__default.default;
|
|
697
|
+
function SentAttachments({ attachments, contrastColor }) {
|
|
698
|
+
const images = attachments.filter((a) => a.contentType === "image");
|
|
699
|
+
const files = attachments.filter((a) => a.contentType !== "image");
|
|
700
|
+
const isDark = contrastColor === "#ffffff" || contrastColor === "#fff";
|
|
701
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5 w-full", children: [
|
|
702
|
+
images.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex gap-1.5 flex-wrap", images.length === 1 && ""), children: images.map((img) => {
|
|
703
|
+
const src = img.url ?? img.base64;
|
|
704
|
+
return src ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
705
|
+
"img",
|
|
706
|
+
{
|
|
707
|
+
src,
|
|
708
|
+
alt: img.name,
|
|
709
|
+
className: cn(
|
|
710
|
+
"rounded-xl object-cover border",
|
|
711
|
+
images.length === 1 ? "w-full max-h-48" : "h-20 w-20",
|
|
712
|
+
isDark ? "border-white/20" : "border-black/10"
|
|
713
|
+
)
|
|
714
|
+
},
|
|
715
|
+
img.id
|
|
716
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
717
|
+
"div",
|
|
718
|
+
{
|
|
719
|
+
className: cn(
|
|
720
|
+
"h-20 w-20 rounded-xl flex items-center justify-center text-[10px] font-medium",
|
|
721
|
+
isDark ? "bg-white/10 text-white/60" : "bg-black/10 text-black/40"
|
|
722
|
+
),
|
|
723
|
+
children: "IMG"
|
|
724
|
+
},
|
|
725
|
+
img.id
|
|
726
|
+
);
|
|
727
|
+
}) }),
|
|
728
|
+
files.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: files.map((f) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
729
|
+
"div",
|
|
730
|
+
{
|
|
731
|
+
className: cn(
|
|
732
|
+
"flex items-center gap-1 rounded-lg px-2 py-1 text-[11px]",
|
|
733
|
+
isDark ? "bg-white/15 text-white/80" : "bg-black/10 text-black/60"
|
|
734
|
+
),
|
|
735
|
+
children: [
|
|
736
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate max-w-[120px]", children: f.name }),
|
|
737
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-60 shrink-0", children: formatBytes(f.sizeBytes) })
|
|
738
|
+
]
|
|
739
|
+
},
|
|
740
|
+
f.id
|
|
741
|
+
)) })
|
|
742
|
+
] });
|
|
743
|
+
}
|
|
744
|
+
function PlanStepIcon({ status }) {
|
|
745
|
+
if (status === "executing") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3 w-3 animate-spin text-primary" });
|
|
746
|
+
if (status === "success") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "h-3 w-3 text-emerald-500" });
|
|
747
|
+
if (status === "failed") return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-3 w-3 text-destructive" });
|
|
748
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 w-3 rounded-full border-2 border-muted-foreground/30" });
|
|
749
|
+
}
|
|
750
|
+
function PlanCard({ part }) {
|
|
751
|
+
const successCount = part.steps.filter((s) => s.status === "success").length;
|
|
752
|
+
const hasExecuting = part.steps.some((s) => s.status === "executing");
|
|
753
|
+
const allDone = successCount === part.steps.length && part.steps.length > 0;
|
|
754
|
+
const anyFailed = part.steps.some((s) => s.status === "failed");
|
|
755
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border bg-background overflow-hidden text-xs w-full shadow-sm", children: [
|
|
756
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-3 py-2 bg-muted/50 border-b", children: [
|
|
757
|
+
hasExecuting ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 shrink-0 animate-spin text-primary/80" }) : allDone ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "h-3.5 w-3.5 shrink-0 text-emerald-500" }) : anyFailed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-3.5 w-3.5 shrink-0 text-destructive" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Zap, { className: "h-3.5 w-3.5 shrink-0 text-primary/70" }),
|
|
758
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground/80 truncate flex-1 leading-snug", children: part.goal }),
|
|
759
|
+
part.steps.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 tabular-nums text-muted-foreground", children: [
|
|
760
|
+
successCount,
|
|
761
|
+
"/",
|
|
762
|
+
part.steps.length
|
|
763
|
+
] })
|
|
764
|
+
] }),
|
|
765
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-border/40", children: part.steps.map((step) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
766
|
+
"div",
|
|
767
|
+
{
|
|
768
|
+
className: cn(
|
|
769
|
+
"flex items-start gap-2.5 px-3 py-2 transition-colors duration-200",
|
|
770
|
+
step.status === "executing" && "bg-primary/5"
|
|
771
|
+
),
|
|
772
|
+
children: [
|
|
773
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(PlanStepIcon, { status: step.status }) }),
|
|
774
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
775
|
+
"span",
|
|
776
|
+
{
|
|
777
|
+
className: cn(
|
|
778
|
+
"leading-relaxed",
|
|
779
|
+
step.status === "pending" && "text-muted-foreground",
|
|
780
|
+
step.status === "executing" && "text-foreground font-medium",
|
|
781
|
+
step.status === "success" && "text-foreground/60 line-through decoration-foreground/20",
|
|
782
|
+
step.status === "failed" && "text-destructive"
|
|
783
|
+
),
|
|
784
|
+
children: [
|
|
785
|
+
step.index + 1,
|
|
786
|
+
". ",
|
|
787
|
+
step.description,
|
|
788
|
+
step.error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-destructive/70 text-[10px] mt-0.5 no-underline", children: step.error })
|
|
789
|
+
]
|
|
790
|
+
}
|
|
791
|
+
)
|
|
792
|
+
]
|
|
793
|
+
},
|
|
794
|
+
step.index
|
|
795
|
+
)) })
|
|
796
|
+
] });
|
|
797
|
+
}
|
|
613
798
|
function ThinkingDots() {
|
|
614
799
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
615
800
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
@@ -798,14 +983,18 @@ function MessageBubble({
|
|
|
798
983
|
const reasoningPart = message.parts.find((p) => p.type === "reasoning");
|
|
799
984
|
const toolParts = message.parts.filter((p) => p.type === "tool");
|
|
800
985
|
const pickerParts = message.parts.filter((p) => p.type === "picker");
|
|
986
|
+
const planParts = message.parts.filter((p) => p.type === "plan");
|
|
801
987
|
const contrastColor = getContrastColor(userColor);
|
|
802
988
|
if (isUser) {
|
|
803
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.
|
|
989
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
804
990
|
"div",
|
|
805
991
|
{
|
|
806
|
-
className: "max-w-[78%] rounded-2xl rounded-tr-sm px-4 py-2.5 text-sm leading-relaxed",
|
|
992
|
+
className: "max-w-[78%] rounded-2xl rounded-tr-sm px-4 py-2.5 text-sm leading-relaxed flex flex-col gap-2",
|
|
807
993
|
style: { backgroundColor: userColor, color: contrastColor },
|
|
808
|
-
children:
|
|
994
|
+
children: [
|
|
995
|
+
message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(SentAttachments, { attachments: message.attachments, contrastColor }),
|
|
996
|
+
textPart?.text && /* @__PURE__ */ jsxRuntime.jsx("span", { children: textPart.text })
|
|
997
|
+
]
|
|
809
998
|
}
|
|
810
999
|
) });
|
|
811
1000
|
}
|
|
@@ -815,6 +1004,7 @@ function MessageBubble({
|
|
|
815
1004
|
/* @__PURE__ */ jsxRuntime.jsx(Avatar2, { style: { width: 28, height: 28, marginTop: 2, border: "1px solid rgba(0,0,0,0.08)" }, children: profilePicture ? /* @__PURE__ */ jsxRuntime.jsx(AvatarImage2, { src: profilePicture, alt: agentName }) : /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback2, { style: { fontSize: 10, fontWeight: 600, backgroundColor: "var(--primary, #19191c)", color: "var(--primary-foreground, #fff)" }, children: agentName.slice(0, 2).toUpperCase() }) }),
|
|
816
1005
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5 min-w-0 max-w-[82%]", children: [
|
|
817
1006
|
showThinking && reasoningPart && /* @__PURE__ */ jsxRuntime.jsx(ReasoningBlock, { text: reasoningPart.text }),
|
|
1007
|
+
planParts.map((p) => /* @__PURE__ */ jsxRuntime.jsx(PlanCard, { part: p }, p.planId)),
|
|
818
1008
|
visibleToolParts.map((t) => /* @__PURE__ */ jsxRuntime.jsx(ToolCallBadge, { part: t }, t.toolCallId)),
|
|
819
1009
|
pickerParts.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
820
1010
|
PickerSelector,
|
|
@@ -910,44 +1100,6 @@ function ChatMessages({
|
|
|
910
1100
|
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: bottomRef })
|
|
911
1101
|
] });
|
|
912
1102
|
}
|
|
913
|
-
var cn2 = (...inputs) => tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
914
|
-
function formatBytes(bytes) {
|
|
915
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
916
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
|
|
917
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
918
|
-
}
|
|
919
|
-
function FileIcon({ mimeType }) {
|
|
920
|
-
if (mimeType.startsWith("image/")) return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ImageIcon, { className: "h-3 w-3 shrink-0" });
|
|
921
|
-
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "h-3 w-3 shrink-0" });
|
|
922
|
-
}
|
|
923
|
-
function AttachmentChips({ attachments, onRemove }) {
|
|
924
|
-
if (attachments.length === 0) return null;
|
|
925
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5 px-1 pb-1.5", children: attachments.map((a) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
926
|
-
"div",
|
|
927
|
-
{
|
|
928
|
-
className: cn2(
|
|
929
|
-
"flex items-center gap-1.5 rounded-lg border px-2 py-1 text-[11px] max-w-[180px]",
|
|
930
|
-
a.status === "error" ? "border-red-200 bg-red-50 text-red-600 dark:border-red-800 dark:bg-red-950 dark:text-red-400" : "border-border bg-muted/60 text-muted-foreground"
|
|
931
|
-
),
|
|
932
|
-
children: [
|
|
933
|
-
a.status === "uploading" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3 w-3 shrink-0 animate-spin" }) : a.status === "error" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-3 w-3 shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(FileIcon, { mimeType: a.mimeType }),
|
|
934
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate leading-tight", children: a.status === "error" ? a.errorMessage ?? "Upload failed" : a.name }),
|
|
935
|
-
a.status === "ready" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground/60", children: formatBytes(a.sizeBytes) }),
|
|
936
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
937
|
-
"button",
|
|
938
|
-
{
|
|
939
|
-
type: "button",
|
|
940
|
-
onClick: () => onRemove(a.id),
|
|
941
|
-
className: "shrink-0 rounded-full p-0.5 hover:bg-foreground/10 transition-colors",
|
|
942
|
-
title: "Remove attachment",
|
|
943
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-2.5 w-2.5" })
|
|
944
|
-
}
|
|
945
|
-
)
|
|
946
|
-
]
|
|
947
|
-
},
|
|
948
|
-
a.id
|
|
949
|
-
)) });
|
|
950
|
-
}
|
|
951
1103
|
var cn3 = (...inputs) => tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
952
1104
|
function ChatInput({
|
|
953
1105
|
input,
|
|
@@ -1034,6 +1186,18 @@ function ChatInput({
|
|
|
1034
1186
|
e.target.style.height = `${Math.min(e.target.scrollHeight, 128)}px`;
|
|
1035
1187
|
},
|
|
1036
1188
|
onKeyDown: handleKeyDown,
|
|
1189
|
+
onPaste: (e) => {
|
|
1190
|
+
if (!hasAttachments || !onAttach) return;
|
|
1191
|
+
const files = Array.from(e.clipboardData?.files ?? []).filter(
|
|
1192
|
+
(f) => f.type.startsWith("image/")
|
|
1193
|
+
);
|
|
1194
|
+
if (files.length > 0) {
|
|
1195
|
+
e.preventDefault();
|
|
1196
|
+
const dt = new DataTransfer();
|
|
1197
|
+
files.forEach((f) => dt.items.add(f));
|
|
1198
|
+
onAttach(dt.files);
|
|
1199
|
+
}
|
|
1200
|
+
},
|
|
1037
1201
|
disabled: streaming || voiceState === "recording" || voiceState === "transcribing",
|
|
1038
1202
|
autoFocus: true
|
|
1039
1203
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useRef, useEffect, useState, useCallback, useMemo } from 'react';
|
|
2
|
-
import { UploadCloud, X, RotateCcw, Loader2, Square, Mic, Paperclip, ArrowUp,
|
|
2
|
+
import { UploadCloud, X, RotateCcw, Loader2, Square, Mic, Paperclip, ArrowUp, Zap, ChevronDown, CheckCircle2, AlertCircle, Search, Check, FileText, FileSpreadsheet } from 'lucide-react';
|
|
3
3
|
import { clsx } from 'clsx';
|
|
4
4
|
import { twMerge } from 'tailwind-merge';
|
|
5
5
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
@@ -179,6 +179,30 @@ function useChat({
|
|
|
179
179
|
});
|
|
180
180
|
break;
|
|
181
181
|
}
|
|
182
|
+
case "plan-created": {
|
|
183
|
+
msg.parts.push({
|
|
184
|
+
type: "plan",
|
|
185
|
+
planId: proto.planId,
|
|
186
|
+
goal: proto.goal,
|
|
187
|
+
steps: proto.steps.map((s) => ({ ...s, status: "pending" }))
|
|
188
|
+
});
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
case "plan-step-update": {
|
|
192
|
+
const pIdx = msg.parts.findIndex(
|
|
193
|
+
(p) => p.type === "plan" && p.planId === proto.planId
|
|
194
|
+
);
|
|
195
|
+
if (pIdx !== -1) {
|
|
196
|
+
const prev2 = msg.parts[pIdx];
|
|
197
|
+
msg.parts[pIdx] = {
|
|
198
|
+
...prev2,
|
|
199
|
+
steps: prev2.steps.map(
|
|
200
|
+
(s) => s.index === proto.stepIndex ? { ...s, status: proto.status, ...proto.error ? { error: proto.error } : {} } : s
|
|
201
|
+
)
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
182
206
|
}
|
|
183
207
|
const copy = [...prev];
|
|
184
208
|
copy[idx] = msg;
|
|
@@ -228,17 +252,22 @@ function useChat({
|
|
|
228
252
|
const userInput = (text ?? input).trim();
|
|
229
253
|
if (!userInput || streaming) return;
|
|
230
254
|
setInput("");
|
|
255
|
+
const attachments = pendingAttachmentsRef.current.length > 0 ? [...pendingAttachmentsRef.current] : void 0;
|
|
256
|
+
pendingAttachmentsRef.current = [];
|
|
231
257
|
const userMsgId = newId();
|
|
232
258
|
setMessages((prev) => [
|
|
233
259
|
...prev,
|
|
234
|
-
{
|
|
260
|
+
{
|
|
261
|
+
id: userMsgId,
|
|
262
|
+
role: "user",
|
|
263
|
+
parts: [{ type: "text", text: userInput }],
|
|
264
|
+
...attachments ? { attachments } : {}
|
|
265
|
+
}
|
|
235
266
|
]);
|
|
236
267
|
setStreaming(true);
|
|
237
268
|
const assistantMsgId = newId();
|
|
238
269
|
streamingMsgIdRef.current = assistantMsgId;
|
|
239
270
|
setMessages((prev) => [...prev, { id: assistantMsgId, role: "assistant", parts: [] }]);
|
|
240
|
-
const attachments = pendingAttachmentsRef.current.length > 0 ? [...pendingAttachmentsRef.current] : void 0;
|
|
241
|
-
pendingAttachmentsRef.current = [];
|
|
242
271
|
try {
|
|
243
272
|
await fetchAndStream({ input: userInput, msgId: assistantMsgId, attachments });
|
|
244
273
|
} catch {
|
|
@@ -580,10 +609,166 @@ function ChatHeader({
|
|
|
580
609
|
}
|
|
581
610
|
);
|
|
582
611
|
}
|
|
612
|
+
var cn2 = (...inputs) => twMerge(clsx(inputs));
|
|
613
|
+
function formatBytes(bytes) {
|
|
614
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
615
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
|
|
616
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
617
|
+
}
|
|
618
|
+
function ChipLeading({ a }) {
|
|
619
|
+
if (a.status === "uploading") return /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 shrink-0 animate-spin" });
|
|
620
|
+
if (a.status === "error") return /* @__PURE__ */ jsx(AlertCircle, { className: "h-3 w-3 shrink-0" });
|
|
621
|
+
const thumbSrc = a.payload?.url ?? a.payload?.base64;
|
|
622
|
+
if (a.mimeType.startsWith("image/") && thumbSrc) {
|
|
623
|
+
return /* @__PURE__ */ jsx(
|
|
624
|
+
"img",
|
|
625
|
+
{
|
|
626
|
+
src: thumbSrc,
|
|
627
|
+
alt: a.name,
|
|
628
|
+
className: "h-6 w-6 rounded object-cover shrink-0 border border-border/40"
|
|
629
|
+
}
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
if (a.mimeType.startsWith("image/")) return /* @__PURE__ */ jsx("div", { className: "h-6 w-6 rounded bg-muted-foreground/10 border border-border/30 shrink-0 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-[8px] font-semibold text-muted-foreground/50", children: "IMG" }) });
|
|
633
|
+
if (a.mimeType === "application/pdf") return /* @__PURE__ */ jsx(FileText, { className: "h-3 w-3 shrink-0 text-red-400" });
|
|
634
|
+
if (a.mimeType.includes("csv") || a.mimeType.includes("spreadsheet") || a.name.endsWith(".csv")) {
|
|
635
|
+
return /* @__PURE__ */ jsx(FileSpreadsheet, { className: "h-3 w-3 shrink-0 text-emerald-500" });
|
|
636
|
+
}
|
|
637
|
+
return /* @__PURE__ */ jsx(FileText, { className: "h-3 w-3 shrink-0" });
|
|
638
|
+
}
|
|
639
|
+
function AttachmentChips({ attachments, onRemove }) {
|
|
640
|
+
if (attachments.length === 0) return null;
|
|
641
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5 px-1 pb-1.5", children: attachments.map((a) => /* @__PURE__ */ jsxs(
|
|
642
|
+
"div",
|
|
643
|
+
{
|
|
644
|
+
className: cn2(
|
|
645
|
+
"flex items-center gap-1.5 rounded-lg border px-2 py-1 text-[11px] max-w-[200px]",
|
|
646
|
+
a.status === "error" ? "border-red-200 bg-red-50 text-red-600 dark:border-red-800 dark:bg-red-950 dark:text-red-400" : "border-border bg-muted/60 text-muted-foreground"
|
|
647
|
+
),
|
|
648
|
+
children: [
|
|
649
|
+
/* @__PURE__ */ jsx(ChipLeading, { a }),
|
|
650
|
+
/* @__PURE__ */ jsx("span", { className: "truncate leading-tight", children: a.status === "error" ? a.errorMessage ?? "Upload failed" : a.name }),
|
|
651
|
+
a.status === "ready" && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground/50", children: formatBytes(a.sizeBytes) }),
|
|
652
|
+
/* @__PURE__ */ jsx(
|
|
653
|
+
"button",
|
|
654
|
+
{
|
|
655
|
+
type: "button",
|
|
656
|
+
onClick: () => onRemove(a.id),
|
|
657
|
+
className: "shrink-0 rounded-full p-0.5 hover:bg-foreground/10 transition-colors ml-0.5",
|
|
658
|
+
title: "Remove",
|
|
659
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-2.5 w-2.5" })
|
|
660
|
+
}
|
|
661
|
+
)
|
|
662
|
+
]
|
|
663
|
+
},
|
|
664
|
+
a.id
|
|
665
|
+
)) });
|
|
666
|
+
}
|
|
583
667
|
var Avatar2 = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Root, { style: { position: "relative", display: "flex", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", ...style }, ...p });
|
|
584
668
|
var AvatarImage2 = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Image, { style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", ...style }, ...p });
|
|
585
669
|
var AvatarFallback2 = ({ style, ...p }) => /* @__PURE__ */ jsx(AvatarPrimitive.Fallback, { style: { display: "flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", borderRadius: "9999px", ...style }, ...p });
|
|
586
670
|
var ReactMarkdown = ReactMarkdownLib;
|
|
671
|
+
function SentAttachments({ attachments, contrastColor }) {
|
|
672
|
+
const images = attachments.filter((a) => a.contentType === "image");
|
|
673
|
+
const files = attachments.filter((a) => a.contentType !== "image");
|
|
674
|
+
const isDark = contrastColor === "#ffffff" || contrastColor === "#fff";
|
|
675
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 w-full", children: [
|
|
676
|
+
images.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("flex gap-1.5 flex-wrap", images.length === 1 && ""), children: images.map((img) => {
|
|
677
|
+
const src = img.url ?? img.base64;
|
|
678
|
+
return src ? /* @__PURE__ */ jsx(
|
|
679
|
+
"img",
|
|
680
|
+
{
|
|
681
|
+
src,
|
|
682
|
+
alt: img.name,
|
|
683
|
+
className: cn(
|
|
684
|
+
"rounded-xl object-cover border",
|
|
685
|
+
images.length === 1 ? "w-full max-h-48" : "h-20 w-20",
|
|
686
|
+
isDark ? "border-white/20" : "border-black/10"
|
|
687
|
+
)
|
|
688
|
+
},
|
|
689
|
+
img.id
|
|
690
|
+
) : /* @__PURE__ */ jsx(
|
|
691
|
+
"div",
|
|
692
|
+
{
|
|
693
|
+
className: cn(
|
|
694
|
+
"h-20 w-20 rounded-xl flex items-center justify-center text-[10px] font-medium",
|
|
695
|
+
isDark ? "bg-white/10 text-white/60" : "bg-black/10 text-black/40"
|
|
696
|
+
),
|
|
697
|
+
children: "IMG"
|
|
698
|
+
},
|
|
699
|
+
img.id
|
|
700
|
+
);
|
|
701
|
+
}) }),
|
|
702
|
+
files.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: files.map((f) => /* @__PURE__ */ jsxs(
|
|
703
|
+
"div",
|
|
704
|
+
{
|
|
705
|
+
className: cn(
|
|
706
|
+
"flex items-center gap-1 rounded-lg px-2 py-1 text-[11px]",
|
|
707
|
+
isDark ? "bg-white/15 text-white/80" : "bg-black/10 text-black/60"
|
|
708
|
+
),
|
|
709
|
+
children: [
|
|
710
|
+
/* @__PURE__ */ jsx("span", { className: "truncate max-w-[120px]", children: f.name }),
|
|
711
|
+
/* @__PURE__ */ jsx("span", { className: "opacity-60 shrink-0", children: formatBytes(f.sizeBytes) })
|
|
712
|
+
]
|
|
713
|
+
},
|
|
714
|
+
f.id
|
|
715
|
+
)) })
|
|
716
|
+
] });
|
|
717
|
+
}
|
|
718
|
+
function PlanStepIcon({ status }) {
|
|
719
|
+
if (status === "executing") return /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin text-primary" });
|
|
720
|
+
if (status === "success") return /* @__PURE__ */ jsx(CheckCircle2, { className: "h-3 w-3 text-emerald-500" });
|
|
721
|
+
if (status === "failed") return /* @__PURE__ */ jsx(AlertCircle, { className: "h-3 w-3 text-destructive" });
|
|
722
|
+
return /* @__PURE__ */ jsx("div", { className: "h-3 w-3 rounded-full border-2 border-muted-foreground/30" });
|
|
723
|
+
}
|
|
724
|
+
function PlanCard({ part }) {
|
|
725
|
+
const successCount = part.steps.filter((s) => s.status === "success").length;
|
|
726
|
+
const hasExecuting = part.steps.some((s) => s.status === "executing");
|
|
727
|
+
const allDone = successCount === part.steps.length && part.steps.length > 0;
|
|
728
|
+
const anyFailed = part.steps.some((s) => s.status === "failed");
|
|
729
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-xl border bg-background overflow-hidden text-xs w-full shadow-sm", children: [
|
|
730
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 bg-muted/50 border-b", children: [
|
|
731
|
+
hasExecuting ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 shrink-0 animate-spin text-primary/80" }) : allDone ? /* @__PURE__ */ jsx(CheckCircle2, { className: "h-3.5 w-3.5 shrink-0 text-emerald-500" }) : anyFailed ? /* @__PURE__ */ jsx(AlertCircle, { className: "h-3.5 w-3.5 shrink-0 text-destructive" }) : /* @__PURE__ */ jsx(Zap, { className: "h-3.5 w-3.5 shrink-0 text-primary/70" }),
|
|
732
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground/80 truncate flex-1 leading-snug", children: part.goal }),
|
|
733
|
+
part.steps.length > 0 && /* @__PURE__ */ jsxs("span", { className: "shrink-0 tabular-nums text-muted-foreground", children: [
|
|
734
|
+
successCount,
|
|
735
|
+
"/",
|
|
736
|
+
part.steps.length
|
|
737
|
+
] })
|
|
738
|
+
] }),
|
|
739
|
+
/* @__PURE__ */ jsx("div", { className: "divide-y divide-border/40", children: part.steps.map((step) => /* @__PURE__ */ jsxs(
|
|
740
|
+
"div",
|
|
741
|
+
{
|
|
742
|
+
className: cn(
|
|
743
|
+
"flex items-start gap-2.5 px-3 py-2 transition-colors duration-200",
|
|
744
|
+
step.status === "executing" && "bg-primary/5"
|
|
745
|
+
),
|
|
746
|
+
children: [
|
|
747
|
+
/* @__PURE__ */ jsx("div", { className: "mt-0.5 shrink-0", children: /* @__PURE__ */ jsx(PlanStepIcon, { status: step.status }) }),
|
|
748
|
+
/* @__PURE__ */ jsxs(
|
|
749
|
+
"span",
|
|
750
|
+
{
|
|
751
|
+
className: cn(
|
|
752
|
+
"leading-relaxed",
|
|
753
|
+
step.status === "pending" && "text-muted-foreground",
|
|
754
|
+
step.status === "executing" && "text-foreground font-medium",
|
|
755
|
+
step.status === "success" && "text-foreground/60 line-through decoration-foreground/20",
|
|
756
|
+
step.status === "failed" && "text-destructive"
|
|
757
|
+
),
|
|
758
|
+
children: [
|
|
759
|
+
step.index + 1,
|
|
760
|
+
". ",
|
|
761
|
+
step.description,
|
|
762
|
+
step.error && /* @__PURE__ */ jsx("span", { className: "block text-destructive/70 text-[10px] mt-0.5 no-underline", children: step.error })
|
|
763
|
+
]
|
|
764
|
+
}
|
|
765
|
+
)
|
|
766
|
+
]
|
|
767
|
+
},
|
|
768
|
+
step.index
|
|
769
|
+
)) })
|
|
770
|
+
] });
|
|
771
|
+
}
|
|
587
772
|
function ThinkingDots() {
|
|
588
773
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
589
774
|
/* @__PURE__ */ jsx("style", { children: `
|
|
@@ -772,14 +957,18 @@ function MessageBubble({
|
|
|
772
957
|
const reasoningPart = message.parts.find((p) => p.type === "reasoning");
|
|
773
958
|
const toolParts = message.parts.filter((p) => p.type === "tool");
|
|
774
959
|
const pickerParts = message.parts.filter((p) => p.type === "picker");
|
|
960
|
+
const planParts = message.parts.filter((p) => p.type === "plan");
|
|
775
961
|
const contrastColor = getContrastColor(userColor);
|
|
776
962
|
if (isUser) {
|
|
777
|
-
return /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */
|
|
963
|
+
return /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs(
|
|
778
964
|
"div",
|
|
779
965
|
{
|
|
780
|
-
className: "max-w-[78%] rounded-2xl rounded-tr-sm px-4 py-2.5 text-sm leading-relaxed",
|
|
966
|
+
className: "max-w-[78%] rounded-2xl rounded-tr-sm px-4 py-2.5 text-sm leading-relaxed flex flex-col gap-2",
|
|
781
967
|
style: { backgroundColor: userColor, color: contrastColor },
|
|
782
|
-
children:
|
|
968
|
+
children: [
|
|
969
|
+
message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsx(SentAttachments, { attachments: message.attachments, contrastColor }),
|
|
970
|
+
textPart?.text && /* @__PURE__ */ jsx("span", { children: textPart.text })
|
|
971
|
+
]
|
|
783
972
|
}
|
|
784
973
|
) });
|
|
785
974
|
}
|
|
@@ -789,6 +978,7 @@ function MessageBubble({
|
|
|
789
978
|
/* @__PURE__ */ jsx(Avatar2, { style: { width: 28, height: 28, marginTop: 2, border: "1px solid rgba(0,0,0,0.08)" }, children: profilePicture ? /* @__PURE__ */ jsx(AvatarImage2, { src: profilePicture, alt: agentName }) : /* @__PURE__ */ jsx(AvatarFallback2, { style: { fontSize: 10, fontWeight: 600, backgroundColor: "var(--primary, #19191c)", color: "var(--primary-foreground, #fff)" }, children: agentName.slice(0, 2).toUpperCase() }) }),
|
|
790
979
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 min-w-0 max-w-[82%]", children: [
|
|
791
980
|
showThinking && reasoningPart && /* @__PURE__ */ jsx(ReasoningBlock, { text: reasoningPart.text }),
|
|
981
|
+
planParts.map((p) => /* @__PURE__ */ jsx(PlanCard, { part: p }, p.planId)),
|
|
792
982
|
visibleToolParts.map((t) => /* @__PURE__ */ jsx(ToolCallBadge, { part: t }, t.toolCallId)),
|
|
793
983
|
pickerParts.map((p) => /* @__PURE__ */ jsx(
|
|
794
984
|
PickerSelector,
|
|
@@ -884,44 +1074,6 @@ function ChatMessages({
|
|
|
884
1074
|
/* @__PURE__ */ jsx("div", { ref: bottomRef })
|
|
885
1075
|
] });
|
|
886
1076
|
}
|
|
887
|
-
var cn2 = (...inputs) => twMerge(clsx(inputs));
|
|
888
|
-
function formatBytes(bytes) {
|
|
889
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
890
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
|
|
891
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
892
|
-
}
|
|
893
|
-
function FileIcon({ mimeType }) {
|
|
894
|
-
if (mimeType.startsWith("image/")) return /* @__PURE__ */ jsx(ImageIcon, { className: "h-3 w-3 shrink-0" });
|
|
895
|
-
return /* @__PURE__ */ jsx(FileText, { className: "h-3 w-3 shrink-0" });
|
|
896
|
-
}
|
|
897
|
-
function AttachmentChips({ attachments, onRemove }) {
|
|
898
|
-
if (attachments.length === 0) return null;
|
|
899
|
-
return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5 px-1 pb-1.5", children: attachments.map((a) => /* @__PURE__ */ jsxs(
|
|
900
|
-
"div",
|
|
901
|
-
{
|
|
902
|
-
className: cn2(
|
|
903
|
-
"flex items-center gap-1.5 rounded-lg border px-2 py-1 text-[11px] max-w-[180px]",
|
|
904
|
-
a.status === "error" ? "border-red-200 bg-red-50 text-red-600 dark:border-red-800 dark:bg-red-950 dark:text-red-400" : "border-border bg-muted/60 text-muted-foreground"
|
|
905
|
-
),
|
|
906
|
-
children: [
|
|
907
|
-
a.status === "uploading" ? /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 shrink-0 animate-spin" }) : a.status === "error" ? /* @__PURE__ */ jsx(AlertCircle, { className: "h-3 w-3 shrink-0" }) : /* @__PURE__ */ jsx(FileIcon, { mimeType: a.mimeType }),
|
|
908
|
-
/* @__PURE__ */ jsx("span", { className: "truncate leading-tight", children: a.status === "error" ? a.errorMessage ?? "Upload failed" : a.name }),
|
|
909
|
-
a.status === "ready" && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground/60", children: formatBytes(a.sizeBytes) }),
|
|
910
|
-
/* @__PURE__ */ jsx(
|
|
911
|
-
"button",
|
|
912
|
-
{
|
|
913
|
-
type: "button",
|
|
914
|
-
onClick: () => onRemove(a.id),
|
|
915
|
-
className: "shrink-0 rounded-full p-0.5 hover:bg-foreground/10 transition-colors",
|
|
916
|
-
title: "Remove attachment",
|
|
917
|
-
children: /* @__PURE__ */ jsx(X, { className: "h-2.5 w-2.5" })
|
|
918
|
-
}
|
|
919
|
-
)
|
|
920
|
-
]
|
|
921
|
-
},
|
|
922
|
-
a.id
|
|
923
|
-
)) });
|
|
924
|
-
}
|
|
925
1077
|
var cn3 = (...inputs) => twMerge(clsx(inputs));
|
|
926
1078
|
function ChatInput({
|
|
927
1079
|
input,
|
|
@@ -1008,6 +1160,18 @@ function ChatInput({
|
|
|
1008
1160
|
e.target.style.height = `${Math.min(e.target.scrollHeight, 128)}px`;
|
|
1009
1161
|
},
|
|
1010
1162
|
onKeyDown: handleKeyDown,
|
|
1163
|
+
onPaste: (e) => {
|
|
1164
|
+
if (!hasAttachments || !onAttach) return;
|
|
1165
|
+
const files = Array.from(e.clipboardData?.files ?? []).filter(
|
|
1166
|
+
(f) => f.type.startsWith("image/")
|
|
1167
|
+
);
|
|
1168
|
+
if (files.length > 0) {
|
|
1169
|
+
e.preventDefault();
|
|
1170
|
+
const dt = new DataTransfer();
|
|
1171
|
+
files.forEach((f) => dt.items.add(f));
|
|
1172
|
+
onAttach(dt.files);
|
|
1173
|
+
}
|
|
1174
|
+
},
|
|
1011
1175
|
disabled: streaming || voiceState === "recording" || voiceState === "transcribing",
|
|
1012
1176
|
autoFocus: true
|
|
1013
1177
|
}
|