@cadraos/sdk 0.1.6 → 0.1.7
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/chat/index.cjs +1046 -34
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +173 -7
- package/dist/chat/index.d.ts +173 -7
- package/dist/chat/index.js +1046 -37
- package/dist/chat/index.js.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/package.json +3 -3
package/dist/chat/index.cjs
CHANGED
|
@@ -3221,8 +3221,9 @@ function useAgentChat(options) {
|
|
|
3221
3221
|
[adapter, handleExecutionLog, updateStreamingMessage, updateToolCall, handleClarification, handleComplete, handleError]
|
|
3222
3222
|
);
|
|
3223
3223
|
const sendMessage = react.useCallback(
|
|
3224
|
-
async (content) => {
|
|
3224
|
+
async (content, options2) => {
|
|
3225
3225
|
if (!content.trim()) return;
|
|
3226
|
+
const attachmentUuids = options2?.attachmentUuids;
|
|
3226
3227
|
lastMessageContentRef.current = content;
|
|
3227
3228
|
setIsLoading(true);
|
|
3228
3229
|
setError(null);
|
|
@@ -3253,7 +3254,8 @@ function useAgentChat(options) {
|
|
|
3253
3254
|
agentId,
|
|
3254
3255
|
teamId,
|
|
3255
3256
|
sessionId: currentSessionId ?? void 0,
|
|
3256
|
-
content: content.trim()
|
|
3257
|
+
content: content.trim(),
|
|
3258
|
+
attachmentUuids
|
|
3257
3259
|
});
|
|
3258
3260
|
console.log("[useAgentChat] Execution started:", result);
|
|
3259
3261
|
if (!currentSessionId && result.sessionId) {
|
|
@@ -3462,6 +3464,222 @@ function useAgentChat(options) {
|
|
|
3462
3464
|
loadSession
|
|
3463
3465
|
};
|
|
3464
3466
|
}
|
|
3467
|
+
|
|
3468
|
+
// src/chat/utils/compress-image.ts
|
|
3469
|
+
var DEFAULT_TARGET_SIZE_KB = 150;
|
|
3470
|
+
var MAX_DIMENSION = 1920;
|
|
3471
|
+
var QUALITY_START = 0.85;
|
|
3472
|
+
var QUALITY_STEP = 0.05;
|
|
3473
|
+
var QUALITY_MIN = 0.1;
|
|
3474
|
+
function loadImage(file) {
|
|
3475
|
+
return new Promise((resolve, reject) => {
|
|
3476
|
+
const img = new Image();
|
|
3477
|
+
const url = URL.createObjectURL(file);
|
|
3478
|
+
img.onload = () => {
|
|
3479
|
+
URL.revokeObjectURL(url);
|
|
3480
|
+
resolve(img);
|
|
3481
|
+
};
|
|
3482
|
+
img.onerror = () => {
|
|
3483
|
+
URL.revokeObjectURL(url);
|
|
3484
|
+
reject(new Error(`Failed to load image: ${file.name}`));
|
|
3485
|
+
};
|
|
3486
|
+
img.src = url;
|
|
3487
|
+
});
|
|
3488
|
+
}
|
|
3489
|
+
function getScaledDimensions(width, height, maxDim) {
|
|
3490
|
+
if (width <= maxDim && height <= maxDim) {
|
|
3491
|
+
return { width, height };
|
|
3492
|
+
}
|
|
3493
|
+
const ratio = Math.min(maxDim / width, maxDim / height);
|
|
3494
|
+
return {
|
|
3495
|
+
width: Math.round(width * ratio),
|
|
3496
|
+
height: Math.round(height * ratio)
|
|
3497
|
+
};
|
|
3498
|
+
}
|
|
3499
|
+
function canvasToBlob(img, width, height, format, quality) {
|
|
3500
|
+
return new Promise((resolve, reject) => {
|
|
3501
|
+
const canvas = document.createElement("canvas");
|
|
3502
|
+
canvas.width = width;
|
|
3503
|
+
canvas.height = height;
|
|
3504
|
+
const ctx = canvas.getContext("2d");
|
|
3505
|
+
if (!ctx) {
|
|
3506
|
+
reject(new Error("Failed to get canvas 2d context"));
|
|
3507
|
+
return;
|
|
3508
|
+
}
|
|
3509
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
3510
|
+
canvas.toBlob(
|
|
3511
|
+
(blob) => {
|
|
3512
|
+
if (!blob) {
|
|
3513
|
+
reject(new Error("Canvas toBlob returned null"));
|
|
3514
|
+
return;
|
|
3515
|
+
}
|
|
3516
|
+
resolve(blob);
|
|
3517
|
+
},
|
|
3518
|
+
format,
|
|
3519
|
+
quality
|
|
3520
|
+
);
|
|
3521
|
+
});
|
|
3522
|
+
}
|
|
3523
|
+
var webpSupported = null;
|
|
3524
|
+
function supportsWebP() {
|
|
3525
|
+
if (webpSupported !== null) return webpSupported;
|
|
3526
|
+
try {
|
|
3527
|
+
const canvas = document.createElement("canvas");
|
|
3528
|
+
canvas.width = 1;
|
|
3529
|
+
canvas.height = 1;
|
|
3530
|
+
webpSupported = canvas.toDataURL("image/webp").startsWith("data:image/webp");
|
|
3531
|
+
} catch {
|
|
3532
|
+
webpSupported = false;
|
|
3533
|
+
}
|
|
3534
|
+
return webpSupported;
|
|
3535
|
+
}
|
|
3536
|
+
function getOutputFormat(_originalType) {
|
|
3537
|
+
if (supportsWebP()) {
|
|
3538
|
+
return { mime: "image/webp", ext: ".webp" };
|
|
3539
|
+
}
|
|
3540
|
+
return { mime: "image/jpeg", ext: ".jpg" };
|
|
3541
|
+
}
|
|
3542
|
+
function replaceExtension(filename, newExt) {
|
|
3543
|
+
const dotIndex = filename.lastIndexOf(".");
|
|
3544
|
+
if (dotIndex === -1) return filename + newExt;
|
|
3545
|
+
return filename.substring(0, dotIndex) + newExt;
|
|
3546
|
+
}
|
|
3547
|
+
async function compressImage(file, targetSizeKB = DEFAULT_TARGET_SIZE_KB) {
|
|
3548
|
+
if (!file.type.startsWith("image/")) {
|
|
3549
|
+
return file;
|
|
3550
|
+
}
|
|
3551
|
+
if (file.type === "image/svg+xml") {
|
|
3552
|
+
return file;
|
|
3553
|
+
}
|
|
3554
|
+
if (file.type === "image/gif") {
|
|
3555
|
+
return file;
|
|
3556
|
+
}
|
|
3557
|
+
const targetBytes = targetSizeKB * 1024;
|
|
3558
|
+
if (file.size <= targetBytes) {
|
|
3559
|
+
return file;
|
|
3560
|
+
}
|
|
3561
|
+
const img = await loadImage(file);
|
|
3562
|
+
const { width, height } = getScaledDimensions(
|
|
3563
|
+
img.naturalWidth,
|
|
3564
|
+
img.naturalHeight,
|
|
3565
|
+
MAX_DIMENSION
|
|
3566
|
+
);
|
|
3567
|
+
const { mime, ext } = getOutputFormat(file.type);
|
|
3568
|
+
let quality = QUALITY_START;
|
|
3569
|
+
let blob = null;
|
|
3570
|
+
while (quality >= QUALITY_MIN) {
|
|
3571
|
+
blob = await canvasToBlob(img, width, height, mime, quality);
|
|
3572
|
+
if (blob.size <= targetBytes) {
|
|
3573
|
+
break;
|
|
3574
|
+
}
|
|
3575
|
+
quality -= QUALITY_STEP;
|
|
3576
|
+
}
|
|
3577
|
+
if (!blob) {
|
|
3578
|
+
blob = await canvasToBlob(img, width, height, mime, QUALITY_MIN);
|
|
3579
|
+
}
|
|
3580
|
+
if (blob.size >= file.size) {
|
|
3581
|
+
return file;
|
|
3582
|
+
}
|
|
3583
|
+
const newFilename = replaceExtension(file.name, ext);
|
|
3584
|
+
return new File([blob], newFilename, {
|
|
3585
|
+
type: mime,
|
|
3586
|
+
lastModified: Date.now()
|
|
3587
|
+
});
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
// src/chat/hooks/useFileAttachments.ts
|
|
3591
|
+
function useFileAttachments(options) {
|
|
3592
|
+
const { onUpload, onConfirmUpload, onSubmitUpload, onError, maxFiles = 10, maxTotalSizeMB = 100 } = options;
|
|
3593
|
+
const [files, setFiles] = react.useState([]);
|
|
3594
|
+
const filesRef = react.useRef(files);
|
|
3595
|
+
filesRef.current = files;
|
|
3596
|
+
const onFilesChange = react.useCallback((newFiles) => {
|
|
3597
|
+
filesRef.current = newFiles;
|
|
3598
|
+
setFiles(newFiles);
|
|
3599
|
+
}, []);
|
|
3600
|
+
const removeFile = react.useCallback((uuid) => {
|
|
3601
|
+
setFiles((prev) => {
|
|
3602
|
+
const file = prev.find((f) => f.uuid === uuid);
|
|
3603
|
+
if (file?.previewUrl) {
|
|
3604
|
+
URL.revokeObjectURL(file.previewUrl);
|
|
3605
|
+
}
|
|
3606
|
+
return prev.filter((f) => f.uuid !== uuid);
|
|
3607
|
+
});
|
|
3608
|
+
}, []);
|
|
3609
|
+
const getReadyUuids = react.useCallback(() => {
|
|
3610
|
+
return filesRef.current.filter((f) => f.status === "ready").map((f) => f.uuid);
|
|
3611
|
+
}, []);
|
|
3612
|
+
const getReadyUrls = react.useCallback(() => {
|
|
3613
|
+
return filesRef.current.filter((f) => f.status === "ready").map((f) => f.uuid);
|
|
3614
|
+
}, []);
|
|
3615
|
+
const prepareForSubmit = react.useCallback(async () => {
|
|
3616
|
+
const readyFiles = filesRef.current.filter((f) => f.status === "ready");
|
|
3617
|
+
console.log("[useFileAttachments] prepareForSubmit - readyFiles:", readyFiles.length, "hasSubmitUpload:", !!onSubmitUpload);
|
|
3618
|
+
if (readyFiles.length === 0) return [];
|
|
3619
|
+
if (!onSubmitUpload) {
|
|
3620
|
+
return readyFiles.map((f) => f.uuid);
|
|
3621
|
+
}
|
|
3622
|
+
const aiUuids = [];
|
|
3623
|
+
for (const attachment of readyFiles) {
|
|
3624
|
+
try {
|
|
3625
|
+
let fileToSend = attachment.file;
|
|
3626
|
+
if (attachment.category === "image") {
|
|
3627
|
+
try {
|
|
3628
|
+
fileToSend = await compressImage(attachment.file);
|
|
3629
|
+
} catch {
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
console.log("[useFileAttachments] Uploading to AI service:", attachment.file.name, "localUuid:", attachment.uuid.substring(0, 40));
|
|
3633
|
+
const aiUuid = await onSubmitUpload(
|
|
3634
|
+
fileToSend,
|
|
3635
|
+
attachment.uuid,
|
|
3636
|
+
{ filename: fileToSend.name, mimeType: fileToSend.type, size: fileToSend.size }
|
|
3637
|
+
);
|
|
3638
|
+
console.log("[useFileAttachments] AI service returned UUID:", aiUuid);
|
|
3639
|
+
aiUuids.push(aiUuid);
|
|
3640
|
+
} catch (err) {
|
|
3641
|
+
const msg = err instanceof Error ? err.message : "Submit upload failed";
|
|
3642
|
+
console.error("[useFileAttachments] Submit upload failed for:", attachment.file.name, msg);
|
|
3643
|
+
onError?.(msg);
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3646
|
+
console.log("[useFileAttachments] prepareForSubmit result:", aiUuids);
|
|
3647
|
+
return aiUuids;
|
|
3648
|
+
}, [onSubmitUpload, onError]);
|
|
3649
|
+
const clear = react.useCallback(() => {
|
|
3650
|
+
for (const file of filesRef.current) {
|
|
3651
|
+
if (file.previewUrl) {
|
|
3652
|
+
URL.revokeObjectURL(file.previewUrl);
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
setFiles([]);
|
|
3656
|
+
}, []);
|
|
3657
|
+
const isUploading = files.some(
|
|
3658
|
+
(f) => f.status === "uploading" || f.status === "pending" || f.status === "processing"
|
|
3659
|
+
);
|
|
3660
|
+
const hasReadyFiles = files.some((f) => f.status === "ready");
|
|
3661
|
+
const dropZoneProps = {
|
|
3662
|
+
files,
|
|
3663
|
+
onFilesChange,
|
|
3664
|
+
onUpload,
|
|
3665
|
+
onConfirmUpload,
|
|
3666
|
+
onError,
|
|
3667
|
+
maxFiles,
|
|
3668
|
+
maxTotalSizeMB
|
|
3669
|
+
};
|
|
3670
|
+
return {
|
|
3671
|
+
files,
|
|
3672
|
+
dropZoneProps,
|
|
3673
|
+
removeFile,
|
|
3674
|
+
getReadyUuids,
|
|
3675
|
+
getReadyUrls,
|
|
3676
|
+
prepareForSubmit,
|
|
3677
|
+
clear,
|
|
3678
|
+
isUploading,
|
|
3679
|
+
hasReadyFiles,
|
|
3680
|
+
fileCount: files.length
|
|
3681
|
+
};
|
|
3682
|
+
}
|
|
3465
3683
|
function cn(...classes) {
|
|
3466
3684
|
return classes.filter(Boolean).join(" ");
|
|
3467
3685
|
}
|
|
@@ -4111,6 +4329,51 @@ function ChatContainer({
|
|
|
4111
4329
|
function cn3(...classes) {
|
|
4112
4330
|
return classes.filter(Boolean).join(" ");
|
|
4113
4331
|
}
|
|
4332
|
+
function formatFileSize(bytes) {
|
|
4333
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
4334
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
4335
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
4336
|
+
}
|
|
4337
|
+
function AttachmentChip({ attachment, onRemove }) {
|
|
4338
|
+
const isLoading = attachment.status === "uploading" || attachment.status === "pending" || attachment.status === "processing";
|
|
4339
|
+
const isError = attachment.status === "error" || attachment.status === "failed";
|
|
4340
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4341
|
+
"div",
|
|
4342
|
+
{
|
|
4343
|
+
className: cn3(
|
|
4344
|
+
"inline-flex items-center gap-1.5 px-2 py-1 rounded-md text-xs max-w-[180px]",
|
|
4345
|
+
isError ? "bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300 border border-red-200 dark:border-red-800" : "bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 border border-gray-200 dark:border-gray-600"
|
|
4346
|
+
),
|
|
4347
|
+
children: [
|
|
4348
|
+
attachment.previewUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4349
|
+
"img",
|
|
4350
|
+
{
|
|
4351
|
+
src: attachment.previewUrl,
|
|
4352
|
+
alt: "",
|
|
4353
|
+
className: "w-5 h-5 rounded object-cover flex-shrink-0"
|
|
4354
|
+
}
|
|
4355
|
+
),
|
|
4356
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: attachment.file.name }),
|
|
4357
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, { className: "h-3 w-3 flex-shrink-0" }) : isError ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 text-red-500", title: attachment.error, children: "!" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 text-gray-400 dark:text-gray-500", children: formatFileSize(attachment.actualSize ?? attachment.file.size) }),
|
|
4358
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4359
|
+
"button",
|
|
4360
|
+
{
|
|
4361
|
+
type: "button",
|
|
4362
|
+
onClick: (e) => {
|
|
4363
|
+
e.preventDefault();
|
|
4364
|
+
e.stopPropagation();
|
|
4365
|
+
onRemove(attachment.uuid);
|
|
4366
|
+
},
|
|
4367
|
+
className: "flex-shrink-0 p-0.5 rounded hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors",
|
|
4368
|
+
"aria-label": `Remove ${attachment.file.name}`,
|
|
4369
|
+
disabled: isLoading,
|
|
4370
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, { className: "h-3 w-3" })
|
|
4371
|
+
}
|
|
4372
|
+
)
|
|
4373
|
+
]
|
|
4374
|
+
}
|
|
4375
|
+
);
|
|
4376
|
+
}
|
|
4114
4377
|
function ChatInput({
|
|
4115
4378
|
onSend,
|
|
4116
4379
|
onStop,
|
|
@@ -4118,10 +4381,15 @@ function ChatInput({
|
|
|
4118
4381
|
disabled = false,
|
|
4119
4382
|
isLoading = false,
|
|
4120
4383
|
enableAttachments = false,
|
|
4384
|
+
attachments = [],
|
|
4385
|
+
onRemoveAttachment,
|
|
4386
|
+
dropZoneRef,
|
|
4387
|
+
isUploadingAttachments = false,
|
|
4121
4388
|
className
|
|
4122
4389
|
}) {
|
|
4123
4390
|
const [content, setContent] = react.useState("");
|
|
4124
4391
|
const textareaRef = react.useRef(null);
|
|
4392
|
+
const hasAttachments = enableAttachments && attachments.length > 0;
|
|
4125
4393
|
react.useEffect(() => {
|
|
4126
4394
|
const textarea = textareaRef.current;
|
|
4127
4395
|
if (textarea) {
|
|
@@ -4131,10 +4399,10 @@ function ChatInput({
|
|
|
4131
4399
|
}, [content]);
|
|
4132
4400
|
const handleSubmit = react.useCallback(async () => {
|
|
4133
4401
|
const trimmedContent = content.trim();
|
|
4134
|
-
if (!trimmedContent || disabled || isLoading) return;
|
|
4402
|
+
if (!trimmedContent && !hasAttachments || disabled || isLoading || isUploadingAttachments) return;
|
|
4135
4403
|
setContent("");
|
|
4136
4404
|
await onSend(trimmedContent);
|
|
4137
|
-
}, [content, disabled, isLoading, onSend]);
|
|
4405
|
+
}, [content, hasAttachments, disabled, isLoading, isUploadingAttachments, onSend]);
|
|
4138
4406
|
const handleKeyDown = react.useCallback(
|
|
4139
4407
|
(e) => {
|
|
4140
4408
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
@@ -4147,7 +4415,10 @@ function ChatInput({
|
|
|
4147
4415
|
const handleStopClick = react.useCallback(() => {
|
|
4148
4416
|
onStop?.();
|
|
4149
4417
|
}, [onStop]);
|
|
4150
|
-
const
|
|
4418
|
+
const handleAttachClick = react.useCallback(() => {
|
|
4419
|
+
dropZoneRef?.current?.openFileDialog();
|
|
4420
|
+
}, [dropZoneRef]);
|
|
4421
|
+
const canSend = (content.trim().length > 0 || hasAttachments) && !disabled && !isLoading && !isUploadingAttachments;
|
|
4151
4422
|
const showStopButton = isLoading && onStop;
|
|
4152
4423
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4153
4424
|
"div",
|
|
@@ -4157,7 +4428,34 @@ function ChatInput({
|
|
|
4157
4428
|
className
|
|
4158
4429
|
),
|
|
4159
4430
|
children: [
|
|
4431
|
+
hasAttachments && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5 mb-2", children: attachments.map((attachment) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4432
|
+
AttachmentChip,
|
|
4433
|
+
{
|
|
4434
|
+
attachment,
|
|
4435
|
+
onRemove: onRemoveAttachment ?? (() => {
|
|
4436
|
+
})
|
|
4437
|
+
},
|
|
4438
|
+
attachment.uuid
|
|
4439
|
+
)) }),
|
|
4160
4440
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-2", children: [
|
|
4441
|
+
enableAttachments && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4442
|
+
"button",
|
|
4443
|
+
{
|
|
4444
|
+
type: "button",
|
|
4445
|
+
onClick: handleAttachClick,
|
|
4446
|
+
disabled: disabled || isLoading,
|
|
4447
|
+
className: cn3(
|
|
4448
|
+
"flex-shrink-0 p-3 rounded-lg transition-colors",
|
|
4449
|
+
"text-gray-500 dark:text-gray-400",
|
|
4450
|
+
"hover:bg-gray-100 dark:hover:bg-gray-700",
|
|
4451
|
+
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
4452
|
+
"focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
|
4453
|
+
),
|
|
4454
|
+
"aria-label": "Attach files",
|
|
4455
|
+
title: "Attach files",
|
|
4456
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(PaperclipIcon, { className: "h-5 w-5" })
|
|
4457
|
+
}
|
|
4458
|
+
),
|
|
4161
4459
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4162
4460
|
"textarea",
|
|
4163
4461
|
{
|
|
@@ -4165,7 +4463,7 @@ function ChatInput({
|
|
|
4165
4463
|
value: content,
|
|
4166
4464
|
onChange: (e) => setContent(e.target.value),
|
|
4167
4465
|
onKeyDown: handleKeyDown,
|
|
4168
|
-
placeholder,
|
|
4466
|
+
placeholder: hasAttachments && !content ? `${attachments.length} file${attachments.length > 1 ? "s" : ""} attached. Add a message or press Enter to send.` : placeholder,
|
|
4169
4467
|
disabled,
|
|
4170
4468
|
rows: 1,
|
|
4171
4469
|
className: cn3(
|
|
@@ -4215,9 +4513,664 @@ function ChatInput({
|
|
|
4215
4513
|
}
|
|
4216
4514
|
);
|
|
4217
4515
|
}
|
|
4516
|
+
function Upload({ className }) {
|
|
4517
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
4518
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
4519
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "17 8 12 3 7 8" }),
|
|
4520
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", x2: "12", y1: "3", y2: "15" })
|
|
4521
|
+
] });
|
|
4522
|
+
}
|
|
4523
|
+
function X({ className }) {
|
|
4524
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
4525
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18" }),
|
|
4526
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 6 12 12" })
|
|
4527
|
+
] });
|
|
4528
|
+
}
|
|
4529
|
+
function FileText({ className }) {
|
|
4530
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
4531
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
4532
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }),
|
|
4533
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16", x2: "8", y1: "13", y2: "13" }),
|
|
4534
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16", x2: "8", y1: "17", y2: "17" }),
|
|
4535
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "10", x2: "8", y1: "9", y2: "9" })
|
|
4536
|
+
] });
|
|
4537
|
+
}
|
|
4538
|
+
function ImageIcon2({ className }) {
|
|
4539
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
4540
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
|
|
4541
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "9", r: "2" }),
|
|
4542
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
|
|
4543
|
+
] });
|
|
4544
|
+
}
|
|
4545
|
+
function Music({ className }) {
|
|
4546
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
4547
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 18V5l12-2v13" }),
|
|
4548
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "6", cy: "18", r: "3" }),
|
|
4549
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "18", cy: "16", r: "3" })
|
|
4550
|
+
] });
|
|
4551
|
+
}
|
|
4552
|
+
function Loader2({ className }) {
|
|
4553
|
+
return /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
|
|
4554
|
+
}
|
|
4555
|
+
function FileIcon2({ className }) {
|
|
4556
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
4557
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
4558
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
4559
|
+
] });
|
|
4560
|
+
}
|
|
4561
|
+
function RotateCw({ className }) {
|
|
4562
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
4563
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" }),
|
|
4564
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 3v5h-5" })
|
|
4565
|
+
] });
|
|
4566
|
+
}
|
|
4218
4567
|
function cn4(...classes) {
|
|
4219
4568
|
return classes.filter(Boolean).join(" ");
|
|
4220
4569
|
}
|
|
4570
|
+
var SUPPORTED_TYPES = {
|
|
4571
|
+
document: {
|
|
4572
|
+
mimeTypes: [
|
|
4573
|
+
"application/pdf",
|
|
4574
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
4575
|
+
"application/msword",
|
|
4576
|
+
"text/plain",
|
|
4577
|
+
"text/markdown",
|
|
4578
|
+
"text/csv",
|
|
4579
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
4580
|
+
"application/vnd.ms-excel"
|
|
4581
|
+
],
|
|
4582
|
+
extensions: [
|
|
4583
|
+
".pdf",
|
|
4584
|
+
".docx",
|
|
4585
|
+
".doc",
|
|
4586
|
+
".txt",
|
|
4587
|
+
".md",
|
|
4588
|
+
".csv",
|
|
4589
|
+
".xlsx",
|
|
4590
|
+
".xls"
|
|
4591
|
+
],
|
|
4592
|
+
maxSizeMB: 25,
|
|
4593
|
+
label: "Documents"
|
|
4594
|
+
},
|
|
4595
|
+
image: {
|
|
4596
|
+
mimeTypes: [
|
|
4597
|
+
"image/png",
|
|
4598
|
+
"image/jpeg",
|
|
4599
|
+
"image/gif",
|
|
4600
|
+
"image/webp",
|
|
4601
|
+
"image/svg+xml"
|
|
4602
|
+
],
|
|
4603
|
+
extensions: [".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"],
|
|
4604
|
+
maxSizeMB: 10,
|
|
4605
|
+
label: "Images"
|
|
4606
|
+
},
|
|
4607
|
+
audio: {
|
|
4608
|
+
mimeTypes: [
|
|
4609
|
+
"audio/mpeg",
|
|
4610
|
+
"audio/wav",
|
|
4611
|
+
"audio/mp4",
|
|
4612
|
+
"audio/ogg",
|
|
4613
|
+
"audio/webm"
|
|
4614
|
+
],
|
|
4615
|
+
extensions: [".mp3", ".wav", ".m4a", ".ogg", ".webm"],
|
|
4616
|
+
maxSizeMB: 50,
|
|
4617
|
+
label: "Audio"
|
|
4618
|
+
}
|
|
4619
|
+
};
|
|
4620
|
+
var MAX_FILES_PER_DROP = 10;
|
|
4621
|
+
var MAX_TOTAL_SIZE_MB = 100;
|
|
4622
|
+
function categorizeFile(mimeType) {
|
|
4623
|
+
for (const [category, config] of Object.entries(SUPPORTED_TYPES)) {
|
|
4624
|
+
if (config.mimeTypes.includes(mimeType)) {
|
|
4625
|
+
return category;
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
return null;
|
|
4629
|
+
}
|
|
4630
|
+
function validateFile(file) {
|
|
4631
|
+
const category = categorizeFile(file.type);
|
|
4632
|
+
if (!category) {
|
|
4633
|
+
return {
|
|
4634
|
+
valid: false,
|
|
4635
|
+
error: `Unsupported file type: ${file.type || file.name.split(".").pop()}`
|
|
4636
|
+
};
|
|
4637
|
+
}
|
|
4638
|
+
const config = SUPPORTED_TYPES[category];
|
|
4639
|
+
const maxSizeBytes = config.maxSizeMB * 1024 * 1024;
|
|
4640
|
+
if (file.size > maxSizeBytes) {
|
|
4641
|
+
return {
|
|
4642
|
+
valid: false,
|
|
4643
|
+
error: `${file.name} exceeds ${config.maxSizeMB}MB limit for ${config.label.toLowerCase()}`
|
|
4644
|
+
};
|
|
4645
|
+
}
|
|
4646
|
+
return { valid: true, category };
|
|
4647
|
+
}
|
|
4648
|
+
function formatFileSize2(bytes) {
|
|
4649
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
4650
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
4651
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
4652
|
+
}
|
|
4653
|
+
function getCategoryIcon(category) {
|
|
4654
|
+
switch (category) {
|
|
4655
|
+
case "document":
|
|
4656
|
+
return FileText;
|
|
4657
|
+
case "image":
|
|
4658
|
+
return ImageIcon2;
|
|
4659
|
+
case "audio":
|
|
4660
|
+
return Music;
|
|
4661
|
+
default:
|
|
4662
|
+
return FileIcon2;
|
|
4663
|
+
}
|
|
4664
|
+
}
|
|
4665
|
+
var FileDropZone = react.forwardRef(
|
|
4666
|
+
function FileDropZone2({
|
|
4667
|
+
onFilesChange,
|
|
4668
|
+
files,
|
|
4669
|
+
onUpload,
|
|
4670
|
+
onConfirmUpload,
|
|
4671
|
+
onError,
|
|
4672
|
+
maxFiles = MAX_FILES_PER_DROP,
|
|
4673
|
+
maxTotalSizeMB = MAX_TOTAL_SIZE_MB,
|
|
4674
|
+
disabled = false,
|
|
4675
|
+
className,
|
|
4676
|
+
accept,
|
|
4677
|
+
compact = false,
|
|
4678
|
+
children
|
|
4679
|
+
}, ref) {
|
|
4680
|
+
const [isDragging, setIsDragging] = react.useState(false);
|
|
4681
|
+
const fileInputRef = react.useRef(null);
|
|
4682
|
+
const dropZoneRef = react.useRef(null);
|
|
4683
|
+
const dragCounter = react.useRef(0);
|
|
4684
|
+
react.useImperativeHandle(
|
|
4685
|
+
ref,
|
|
4686
|
+
() => ({
|
|
4687
|
+
openFileDialog: () => {
|
|
4688
|
+
if (!disabled) {
|
|
4689
|
+
fileInputRef.current?.click();
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
}),
|
|
4693
|
+
[disabled]
|
|
4694
|
+
);
|
|
4695
|
+
const currentTotalSize = files.reduce((sum, f) => sum + f.file.size, 0);
|
|
4696
|
+
const uploadFile = react.useCallback(
|
|
4697
|
+
async (file, _tempId) => {
|
|
4698
|
+
const result = await onUpload(file, {
|
|
4699
|
+
filename: file.name,
|
|
4700
|
+
mimeType: file.type,
|
|
4701
|
+
size: file.size
|
|
4702
|
+
});
|
|
4703
|
+
if (result.uploadUrl) {
|
|
4704
|
+
const uploadResponse = await fetch(result.uploadUrl, {
|
|
4705
|
+
method: "PUT",
|
|
4706
|
+
body: file,
|
|
4707
|
+
headers: { "Content-Type": file.type }
|
|
4708
|
+
});
|
|
4709
|
+
if (!uploadResponse.ok) {
|
|
4710
|
+
throw new Error(`Upload failed: ${uploadResponse.statusText}`);
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
if (onConfirmUpload) {
|
|
4714
|
+
await onConfirmUpload(result.uuid);
|
|
4715
|
+
}
|
|
4716
|
+
return result.uuid;
|
|
4717
|
+
},
|
|
4718
|
+
[onUpload, onConfirmUpload]
|
|
4719
|
+
);
|
|
4720
|
+
const handleFiles = react.useCallback(
|
|
4721
|
+
async (selectedFiles) => {
|
|
4722
|
+
if (disabled) return;
|
|
4723
|
+
if (files.length + selectedFiles.length > maxFiles) {
|
|
4724
|
+
onError?.(`Maximum ${maxFiles} files allowed`);
|
|
4725
|
+
return;
|
|
4726
|
+
}
|
|
4727
|
+
const validFiles = [];
|
|
4728
|
+
let newTotalSize = currentTotalSize;
|
|
4729
|
+
for (const file of selectedFiles) {
|
|
4730
|
+
const validation = validateFile(file);
|
|
4731
|
+
if (!validation.valid) {
|
|
4732
|
+
onError?.(validation.error);
|
|
4733
|
+
continue;
|
|
4734
|
+
}
|
|
4735
|
+
newTotalSize += file.size;
|
|
4736
|
+
if (newTotalSize > maxTotalSizeMB * 1024 * 1024) {
|
|
4737
|
+
onError?.(`Total upload size exceeds ${maxTotalSizeMB}MB`);
|
|
4738
|
+
break;
|
|
4739
|
+
}
|
|
4740
|
+
validFiles.push({ file, category: validation.category });
|
|
4741
|
+
}
|
|
4742
|
+
if (validFiles.length === 0) return;
|
|
4743
|
+
const processedFiles = [];
|
|
4744
|
+
for (const entry of validFiles) {
|
|
4745
|
+
if (entry.category === "image") {
|
|
4746
|
+
try {
|
|
4747
|
+
const compressed = await compressImage(entry.file);
|
|
4748
|
+
processedFiles.push({ file: compressed, category: entry.category });
|
|
4749
|
+
} catch (err) {
|
|
4750
|
+
console.warn("Image compression failed, using original:", err);
|
|
4751
|
+
processedFiles.push(entry);
|
|
4752
|
+
}
|
|
4753
|
+
} else {
|
|
4754
|
+
processedFiles.push(entry);
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
const newAttachments = processedFiles.map(
|
|
4758
|
+
({ file, category }) => {
|
|
4759
|
+
const tempId = `temp-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
4760
|
+
return {
|
|
4761
|
+
uuid: tempId,
|
|
4762
|
+
file,
|
|
4763
|
+
status: "pending",
|
|
4764
|
+
progress: 0,
|
|
4765
|
+
category,
|
|
4766
|
+
previewUrl: category === "image" ? URL.createObjectURL(file) : void 0
|
|
4767
|
+
};
|
|
4768
|
+
}
|
|
4769
|
+
);
|
|
4770
|
+
let currentFiles = [...files, ...newAttachments];
|
|
4771
|
+
onFilesChange(currentFiles);
|
|
4772
|
+
for (const attachment of newAttachments) {
|
|
4773
|
+
try {
|
|
4774
|
+
currentFiles = currentFiles.map(
|
|
4775
|
+
(f) => f.uuid === attachment.uuid ? { ...f, status: "uploading", progress: 10 } : f
|
|
4776
|
+
);
|
|
4777
|
+
onFilesChange(currentFiles);
|
|
4778
|
+
const realUuid = await uploadFile(attachment.file, attachment.uuid);
|
|
4779
|
+
currentFiles = currentFiles.map(
|
|
4780
|
+
(f) => f.uuid === attachment.uuid ? {
|
|
4781
|
+
...f,
|
|
4782
|
+
uuid: realUuid,
|
|
4783
|
+
status: "ready",
|
|
4784
|
+
progress: 100,
|
|
4785
|
+
hasThumbnail: f.category === "image"
|
|
4786
|
+
} : f
|
|
4787
|
+
);
|
|
4788
|
+
onFilesChange(currentFiles);
|
|
4789
|
+
} catch (error) {
|
|
4790
|
+
console.error("Upload error:", error);
|
|
4791
|
+
currentFiles = currentFiles.map(
|
|
4792
|
+
(f) => f.uuid === attachment.uuid ? {
|
|
4793
|
+
...f,
|
|
4794
|
+
status: "error",
|
|
4795
|
+
error: error instanceof Error ? error.message : "Upload failed"
|
|
4796
|
+
} : f
|
|
4797
|
+
);
|
|
4798
|
+
onFilesChange(currentFiles);
|
|
4799
|
+
onError?.(`Failed to upload ${attachment.file.name}`);
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
},
|
|
4803
|
+
[
|
|
4804
|
+
disabled,
|
|
4805
|
+
files,
|
|
4806
|
+
maxFiles,
|
|
4807
|
+
currentTotalSize,
|
|
4808
|
+
maxTotalSizeMB,
|
|
4809
|
+
onFilesChange,
|
|
4810
|
+
uploadFile,
|
|
4811
|
+
onError
|
|
4812
|
+
]
|
|
4813
|
+
);
|
|
4814
|
+
const handleRemove = react.useCallback(
|
|
4815
|
+
(uuid) => {
|
|
4816
|
+
const file = files.find((f) => f.uuid === uuid);
|
|
4817
|
+
if (file?.previewUrl) {
|
|
4818
|
+
URL.revokeObjectURL(file.previewUrl);
|
|
4819
|
+
}
|
|
4820
|
+
onFilesChange(files.filter((f) => f.uuid !== uuid));
|
|
4821
|
+
},
|
|
4822
|
+
[files, onFilesChange]
|
|
4823
|
+
);
|
|
4824
|
+
const handleRetry = react.useCallback(
|
|
4825
|
+
async (uuid) => {
|
|
4826
|
+
const attachment = files.find((f) => f.uuid === uuid);
|
|
4827
|
+
if (!attachment || attachment.status !== "error") return;
|
|
4828
|
+
let currentFiles = files.map(
|
|
4829
|
+
(f) => f.uuid === uuid ? { ...f, status: "pending", progress: 0, error: void 0 } : f
|
|
4830
|
+
);
|
|
4831
|
+
onFilesChange(currentFiles);
|
|
4832
|
+
try {
|
|
4833
|
+
currentFiles = currentFiles.map(
|
|
4834
|
+
(f) => f.uuid === uuid ? { ...f, status: "uploading", progress: 10 } : f
|
|
4835
|
+
);
|
|
4836
|
+
onFilesChange(currentFiles);
|
|
4837
|
+
const realUuid = await uploadFile(attachment.file, uuid);
|
|
4838
|
+
currentFiles = currentFiles.map(
|
|
4839
|
+
(f) => f.uuid === uuid ? {
|
|
4840
|
+
...f,
|
|
4841
|
+
uuid: realUuid,
|
|
4842
|
+
status: "ready",
|
|
4843
|
+
progress: 100,
|
|
4844
|
+
hasThumbnail: f.category === "image"
|
|
4845
|
+
} : f
|
|
4846
|
+
);
|
|
4847
|
+
onFilesChange(currentFiles);
|
|
4848
|
+
} catch (error) {
|
|
4849
|
+
console.error("Retry upload error:", error);
|
|
4850
|
+
currentFiles = currentFiles.map(
|
|
4851
|
+
(f) => f.uuid === uuid ? {
|
|
4852
|
+
...f,
|
|
4853
|
+
status: "error",
|
|
4854
|
+
error: error instanceof Error ? error.message : "Upload failed"
|
|
4855
|
+
} : f
|
|
4856
|
+
);
|
|
4857
|
+
onFilesChange(currentFiles);
|
|
4858
|
+
onError?.(`Failed to upload ${attachment.file.name}`);
|
|
4859
|
+
}
|
|
4860
|
+
},
|
|
4861
|
+
[files, onFilesChange, uploadFile, onError]
|
|
4862
|
+
);
|
|
4863
|
+
const hasDraggedFiles = react.useCallback((e) => {
|
|
4864
|
+
if (e.dataTransfer.types) {
|
|
4865
|
+
for (const type of e.dataTransfer.types) {
|
|
4866
|
+
if (type === "Files") return true;
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4869
|
+
return false;
|
|
4870
|
+
}, []);
|
|
4871
|
+
const handleDragEnter = react.useCallback(
|
|
4872
|
+
(e) => {
|
|
4873
|
+
e.preventDefault();
|
|
4874
|
+
e.stopPropagation();
|
|
4875
|
+
if (!hasDraggedFiles(e)) return;
|
|
4876
|
+
dragCounter.current++;
|
|
4877
|
+
if (!disabled && dragCounter.current === 1) {
|
|
4878
|
+
setIsDragging(true);
|
|
4879
|
+
}
|
|
4880
|
+
},
|
|
4881
|
+
[disabled, hasDraggedFiles]
|
|
4882
|
+
);
|
|
4883
|
+
const handleDragOver = react.useCallback(
|
|
4884
|
+
(e) => {
|
|
4885
|
+
e.preventDefault();
|
|
4886
|
+
e.stopPropagation();
|
|
4887
|
+
if (hasDraggedFiles(e)) {
|
|
4888
|
+
e.dataTransfer.dropEffect = disabled ? "none" : "copy";
|
|
4889
|
+
}
|
|
4890
|
+
},
|
|
4891
|
+
[disabled, hasDraggedFiles]
|
|
4892
|
+
);
|
|
4893
|
+
const handleDragLeave = react.useCallback((e) => {
|
|
4894
|
+
e.preventDefault();
|
|
4895
|
+
e.stopPropagation();
|
|
4896
|
+
if (dragCounter.current > 0) {
|
|
4897
|
+
dragCounter.current--;
|
|
4898
|
+
if (dragCounter.current === 0) {
|
|
4899
|
+
setIsDragging(false);
|
|
4900
|
+
}
|
|
4901
|
+
}
|
|
4902
|
+
}, []);
|
|
4903
|
+
const handleDrop = react.useCallback(
|
|
4904
|
+
(e) => {
|
|
4905
|
+
e.preventDefault();
|
|
4906
|
+
e.stopPropagation();
|
|
4907
|
+
dragCounter.current = 0;
|
|
4908
|
+
setIsDragging(false);
|
|
4909
|
+
if (disabled) return;
|
|
4910
|
+
const droppedFiles = Array.from(e.dataTransfer.files);
|
|
4911
|
+
if (droppedFiles.length > 0) {
|
|
4912
|
+
handleFiles(droppedFiles);
|
|
4913
|
+
}
|
|
4914
|
+
},
|
|
4915
|
+
[handleFiles, disabled]
|
|
4916
|
+
);
|
|
4917
|
+
const handleFileSelect = react.useCallback(
|
|
4918
|
+
(e) => {
|
|
4919
|
+
const selectedFiles = e.target.files;
|
|
4920
|
+
if (selectedFiles) {
|
|
4921
|
+
handleFiles(Array.from(selectedFiles));
|
|
4922
|
+
}
|
|
4923
|
+
if (fileInputRef.current) {
|
|
4924
|
+
fileInputRef.current.value = "";
|
|
4925
|
+
}
|
|
4926
|
+
},
|
|
4927
|
+
[handleFiles]
|
|
4928
|
+
);
|
|
4929
|
+
const handleClick = react.useCallback(() => {
|
|
4930
|
+
if (!disabled) {
|
|
4931
|
+
fileInputRef.current?.click();
|
|
4932
|
+
}
|
|
4933
|
+
}, [disabled]);
|
|
4934
|
+
const checkDataTransferHasFiles = react.useCallback(
|
|
4935
|
+
(dataTransfer) => {
|
|
4936
|
+
if (!dataTransfer?.types) return false;
|
|
4937
|
+
for (let i = 0; i < dataTransfer.types.length; i++) {
|
|
4938
|
+
if (dataTransfer.types[i] === "Files") return true;
|
|
4939
|
+
}
|
|
4940
|
+
return false;
|
|
4941
|
+
},
|
|
4942
|
+
[]
|
|
4943
|
+
);
|
|
4944
|
+
react.useEffect(() => {
|
|
4945
|
+
if (!children || !dropZoneRef.current) return;
|
|
4946
|
+
const element = dropZoneRef.current;
|
|
4947
|
+
let enterCounter = 0;
|
|
4948
|
+
const handleDragEnterNative = (e) => {
|
|
4949
|
+
e.preventDefault();
|
|
4950
|
+
e.stopPropagation();
|
|
4951
|
+
if (!checkDataTransferHasFiles(e.dataTransfer)) return;
|
|
4952
|
+
enterCounter++;
|
|
4953
|
+
if (enterCounter === 1 && !disabled) {
|
|
4954
|
+
setIsDragging(true);
|
|
4955
|
+
}
|
|
4956
|
+
};
|
|
4957
|
+
const handleDragLeaveNative = (e) => {
|
|
4958
|
+
e.preventDefault();
|
|
4959
|
+
e.stopPropagation();
|
|
4960
|
+
if (!checkDataTransferHasFiles(e.dataTransfer)) return;
|
|
4961
|
+
enterCounter--;
|
|
4962
|
+
if (enterCounter <= 0) {
|
|
4963
|
+
enterCounter = 0;
|
|
4964
|
+
setIsDragging(false);
|
|
4965
|
+
}
|
|
4966
|
+
};
|
|
4967
|
+
const handleDragOverNative = (e) => {
|
|
4968
|
+
e.preventDefault();
|
|
4969
|
+
e.stopPropagation();
|
|
4970
|
+
if (checkDataTransferHasFiles(e.dataTransfer) && !disabled && e.dataTransfer) {
|
|
4971
|
+
e.dataTransfer.dropEffect = "copy";
|
|
4972
|
+
}
|
|
4973
|
+
};
|
|
4974
|
+
const handleDropNative = (e) => {
|
|
4975
|
+
e.preventDefault();
|
|
4976
|
+
e.stopPropagation();
|
|
4977
|
+
enterCounter = 0;
|
|
4978
|
+
setIsDragging(false);
|
|
4979
|
+
if (disabled) return;
|
|
4980
|
+
const droppedFiles = e.dataTransfer?.files;
|
|
4981
|
+
if (droppedFiles && droppedFiles.length > 0) {
|
|
4982
|
+
handleFiles(Array.from(droppedFiles));
|
|
4983
|
+
}
|
|
4984
|
+
};
|
|
4985
|
+
element.addEventListener("dragenter", handleDragEnterNative, true);
|
|
4986
|
+
element.addEventListener("dragleave", handleDragLeaveNative, true);
|
|
4987
|
+
element.addEventListener("dragover", handleDragOverNative, true);
|
|
4988
|
+
element.addEventListener("drop", handleDropNative, true);
|
|
4989
|
+
return () => {
|
|
4990
|
+
element.removeEventListener("dragenter", handleDragEnterNative, true);
|
|
4991
|
+
element.removeEventListener("dragleave", handleDragLeaveNative, true);
|
|
4992
|
+
element.removeEventListener("dragover", handleDragOverNative, true);
|
|
4993
|
+
element.removeEventListener("drop", handleDropNative, true);
|
|
4994
|
+
};
|
|
4995
|
+
}, [children, disabled, handleFiles, checkDataTransferHasFiles]);
|
|
4996
|
+
react.useEffect(() => {
|
|
4997
|
+
if (disabled) {
|
|
4998
|
+
dragCounter.current = 0;
|
|
4999
|
+
setIsDragging(false);
|
|
5000
|
+
}
|
|
5001
|
+
}, [disabled]);
|
|
5002
|
+
const acceptTypes = accept || Object.values(SUPPORTED_TYPES).flatMap((t) => [...t.mimeTypes, ...t.extensions]).join(",");
|
|
5003
|
+
const hasFiles = files.length > 0;
|
|
5004
|
+
const isUploading = files.some((f) => f.status === "uploading");
|
|
5005
|
+
if (children) {
|
|
5006
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5007
|
+
"div",
|
|
5008
|
+
{
|
|
5009
|
+
ref: dropZoneRef,
|
|
5010
|
+
"data-dropzone": "wrapper",
|
|
5011
|
+
className: cn4("relative w-full h-full", className),
|
|
5012
|
+
style: { minHeight: "inherit" },
|
|
5013
|
+
children: [
|
|
5014
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5015
|
+
"input",
|
|
5016
|
+
{
|
|
5017
|
+
ref: fileInputRef,
|
|
5018
|
+
type: "file",
|
|
5019
|
+
multiple: true,
|
|
5020
|
+
accept: acceptTypes,
|
|
5021
|
+
onChange: handleFileSelect,
|
|
5022
|
+
className: "hidden",
|
|
5023
|
+
disabled
|
|
5024
|
+
}
|
|
5025
|
+
),
|
|
5026
|
+
children,
|
|
5027
|
+
isDragging && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5028
|
+
"div",
|
|
5029
|
+
{
|
|
5030
|
+
className: "absolute inset-0 z-50 bg-primary/10 border-2 border-dashed border-primary rounded-xl flex flex-col items-center justify-center backdrop-blur-sm transition-opacity duration-200",
|
|
5031
|
+
style: { pointerEvents: "none" },
|
|
5032
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-background/80 rounded-xl p-8 flex flex-col items-center shadow-lg", children: [
|
|
5033
|
+
/* @__PURE__ */ jsxRuntime.jsx(Upload, { className: "h-12 w-12 text-primary mb-4 animate-bounce" }),
|
|
5034
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg font-medium text-primary", children: "Drop files here" }),
|
|
5035
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-2", children: "Documents, images, or audio files" })
|
|
5036
|
+
] })
|
|
5037
|
+
}
|
|
5038
|
+
)
|
|
5039
|
+
]
|
|
5040
|
+
}
|
|
5041
|
+
);
|
|
5042
|
+
}
|
|
5043
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn4("w-full", className), children: [
|
|
5044
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5045
|
+
"input",
|
|
5046
|
+
{
|
|
5047
|
+
ref: fileInputRef,
|
|
5048
|
+
type: "file",
|
|
5049
|
+
multiple: true,
|
|
5050
|
+
accept: acceptTypes,
|
|
5051
|
+
onChange: handleFileSelect,
|
|
5052
|
+
className: "hidden",
|
|
5053
|
+
disabled
|
|
5054
|
+
}
|
|
5055
|
+
),
|
|
5056
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
5057
|
+
"div",
|
|
5058
|
+
{
|
|
5059
|
+
onDragEnter: handleDragEnter,
|
|
5060
|
+
onDragOver: handleDragOver,
|
|
5061
|
+
onDragLeave: handleDragLeave,
|
|
5062
|
+
onDrop: handleDrop,
|
|
5063
|
+
onClick: handleClick,
|
|
5064
|
+
className: cn4(
|
|
5065
|
+
"relative rounded-lg border-2 border-dashed transition-all cursor-pointer",
|
|
5066
|
+
"flex flex-col items-center justify-center",
|
|
5067
|
+
compact ? "p-2 min-h-[60px]" : "p-4 min-h-[100px]",
|
|
5068
|
+
isDragging && !disabled ? "border-primary bg-primary/5" : "border-muted-foreground/25",
|
|
5069
|
+
disabled ? "opacity-50 cursor-not-allowed" : "hover:border-primary/50 hover:bg-muted/25"
|
|
5070
|
+
),
|
|
5071
|
+
children: [
|
|
5072
|
+
isUploading ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
5073
|
+
Loader2,
|
|
5074
|
+
{
|
|
5075
|
+
className: cn4(
|
|
5076
|
+
"animate-spin text-muted-foreground",
|
|
5077
|
+
compact ? "h-5 w-5 mb-1" : "h-8 w-8 mb-2"
|
|
5078
|
+
)
|
|
5079
|
+
}
|
|
5080
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
5081
|
+
Upload,
|
|
5082
|
+
{
|
|
5083
|
+
className: cn4(
|
|
5084
|
+
"text-muted-foreground",
|
|
5085
|
+
compact ? "h-5 w-5 mb-1" : "h-8 w-8 mb-2"
|
|
5086
|
+
)
|
|
5087
|
+
}
|
|
5088
|
+
),
|
|
5089
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5090
|
+
"p",
|
|
5091
|
+
{
|
|
5092
|
+
className: cn4(
|
|
5093
|
+
"font-medium text-center text-muted-foreground",
|
|
5094
|
+
compact ? "text-xs" : "text-sm"
|
|
5095
|
+
),
|
|
5096
|
+
children: isDragging ? "Drop files here" : isUploading ? "Uploading..." : "Click to upload or drag and drop"
|
|
5097
|
+
}
|
|
5098
|
+
),
|
|
5099
|
+
!compact && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground/70 text-center mt-1", children: "Documents, images, or audio files" })
|
|
5100
|
+
]
|
|
5101
|
+
}
|
|
5102
|
+
),
|
|
5103
|
+
hasFiles && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn4("space-y-2", compact ? "mt-2" : "mt-3"), children: files.map((attachment) => {
|
|
5104
|
+
const Icon = getCategoryIcon(attachment.category);
|
|
5105
|
+
const isError = attachment.status === "error";
|
|
5106
|
+
const isLoading = attachment.status === "uploading" || attachment.status === "processing";
|
|
5107
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5108
|
+
"div",
|
|
5109
|
+
{
|
|
5110
|
+
className: cn4(
|
|
5111
|
+
"flex items-center gap-3 p-2 rounded-lg border",
|
|
5112
|
+
isError ? "border-destructive/50 bg-destructive/5" : "border-border bg-muted/25"
|
|
5113
|
+
),
|
|
5114
|
+
children: [
|
|
5115
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-shrink-0 w-10 h-10 rounded overflow-hidden bg-muted flex items-center justify-center", children: [
|
|
5116
|
+
attachment.previewUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
5117
|
+
"img",
|
|
5118
|
+
{
|
|
5119
|
+
src: attachment.previewUrl,
|
|
5120
|
+
alt: attachment.file.name,
|
|
5121
|
+
className: "w-full h-full object-cover"
|
|
5122
|
+
}
|
|
5123
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-5 w-5 text-muted-foreground" }),
|
|
5124
|
+
isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-background/80 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(Loader2, { className: "h-4 w-4 animate-spin" }) })
|
|
5125
|
+
] }),
|
|
5126
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
5127
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium truncate", children: attachment.file.name }),
|
|
5128
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
5129
|
+
formatFileSize2(attachment.file.size),
|
|
5130
|
+
isError && attachment.error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-destructive ml-2", children: attachment.error })
|
|
5131
|
+
] })
|
|
5132
|
+
] }),
|
|
5133
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
5134
|
+
attachment.progress,
|
|
5135
|
+
"%"
|
|
5136
|
+
] }) : attachment.status === "ready" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-green-600", children: "Ready" }) : null }),
|
|
5137
|
+
isError && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5138
|
+
"button",
|
|
5139
|
+
{
|
|
5140
|
+
type: "button",
|
|
5141
|
+
className: "flex-shrink-0 h-8 w-8 inline-flex items-center justify-center rounded-md text-primary hover:bg-muted transition-colors",
|
|
5142
|
+
onClick: (e) => {
|
|
5143
|
+
e.stopPropagation();
|
|
5144
|
+
handleRetry(attachment.uuid);
|
|
5145
|
+
},
|
|
5146
|
+
title: "Retry upload",
|
|
5147
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(RotateCw, { className: "h-4 w-4" })
|
|
5148
|
+
}
|
|
5149
|
+
),
|
|
5150
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5151
|
+
"button",
|
|
5152
|
+
{
|
|
5153
|
+
type: "button",
|
|
5154
|
+
className: "flex-shrink-0 h-8 w-8 inline-flex items-center justify-center rounded-md hover:bg-muted transition-colors disabled:opacity-50",
|
|
5155
|
+
onClick: (e) => {
|
|
5156
|
+
e.stopPropagation();
|
|
5157
|
+
handleRemove(attachment.uuid);
|
|
5158
|
+
},
|
|
5159
|
+
disabled: isLoading,
|
|
5160
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(X, { className: "h-4 w-4" })
|
|
5161
|
+
}
|
|
5162
|
+
)
|
|
5163
|
+
]
|
|
5164
|
+
},
|
|
5165
|
+
attachment.uuid
|
|
5166
|
+
);
|
|
5167
|
+
}) })
|
|
5168
|
+
] });
|
|
5169
|
+
}
|
|
5170
|
+
);
|
|
5171
|
+
function cn5(...classes) {
|
|
5172
|
+
return classes.filter(Boolean).join(" ");
|
|
5173
|
+
}
|
|
4221
5174
|
function AskUserPrompt({
|
|
4222
5175
|
question,
|
|
4223
5176
|
options,
|
|
@@ -4251,7 +5204,7 @@ function AskUserPrompt({
|
|
|
4251
5204
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4252
5205
|
"div",
|
|
4253
5206
|
{
|
|
4254
|
-
className:
|
|
5207
|
+
className: cn5(
|
|
4255
5208
|
"rounded-lg border p-4 space-y-3",
|
|
4256
5209
|
status === "waiting" ? "border-blue-200 bg-blue-50/50 dark:border-blue-800 dark:bg-blue-950/20" : "border-green-200 bg-green-50/50 dark:border-green-800 dark:bg-green-950/20",
|
|
4257
5210
|
className
|
|
@@ -4263,7 +5216,7 @@ function AskUserPrompt({
|
|
|
4263
5216
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4264
5217
|
"span",
|
|
4265
5218
|
{
|
|
4266
|
-
className:
|
|
5219
|
+
className: cn5(
|
|
4267
5220
|
"ml-auto text-xs px-2 py-1 rounded-full font-medium",
|
|
4268
5221
|
status === "waiting" ? "bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300" : "bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"
|
|
4269
5222
|
),
|
|
@@ -4278,7 +5231,7 @@ function AskUserPrompt({
|
|
|
4278
5231
|
type: "button",
|
|
4279
5232
|
onClick: () => handleOptionSelect(option),
|
|
4280
5233
|
disabled,
|
|
4281
|
-
className:
|
|
5234
|
+
className: cn5(
|
|
4282
5235
|
"px-3 py-1.5 text-sm rounded-md border transition-all",
|
|
4283
5236
|
selectedOption === option ? "bg-blue-600 text-white border-blue-600 hover:bg-blue-700" : "bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300",
|
|
4284
5237
|
disabled && "opacity-50 cursor-not-allowed"
|
|
@@ -4298,7 +5251,7 @@ function AskUserPrompt({
|
|
|
4298
5251
|
},
|
|
4299
5252
|
onKeyDown: handleKeyDown,
|
|
4300
5253
|
placeholder: options?.length ? "Or type a custom response..." : "Type your response...",
|
|
4301
|
-
className:
|
|
5254
|
+
className: cn5(
|
|
4302
5255
|
"flex-1 min-h-[60px] px-3 py-2 text-sm resize-none rounded-md border",
|
|
4303
5256
|
"border-gray-300 dark:border-gray-600",
|
|
4304
5257
|
"bg-white dark:bg-gray-800",
|
|
@@ -4316,7 +5269,7 @@ function AskUserPrompt({
|
|
|
4316
5269
|
type: "button",
|
|
4317
5270
|
onClick: handleSubmit,
|
|
4318
5271
|
disabled: isSubmitDisabled,
|
|
4319
|
-
className:
|
|
5272
|
+
className: cn5(
|
|
4320
5273
|
"self-end px-4 py-2 rounded-md text-white transition-colors",
|
|
4321
5274
|
"bg-blue-600 hover:bg-blue-700",
|
|
4322
5275
|
"disabled:opacity-50 disabled:cursor-not-allowed"
|
|
@@ -4337,7 +5290,7 @@ function AskUserPrompt({
|
|
|
4337
5290
|
}
|
|
4338
5291
|
);
|
|
4339
5292
|
}
|
|
4340
|
-
function
|
|
5293
|
+
function cn6(...classes) {
|
|
4341
5294
|
return classes.filter(Boolean).join(" ");
|
|
4342
5295
|
}
|
|
4343
5296
|
function MarkdownContent({
|
|
@@ -4398,7 +5351,7 @@ function MarkdownContent({
|
|
|
4398
5351
|
if (isCodeBlock) {
|
|
4399
5352
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative my-2", children: [
|
|
4400
5353
|
language && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-0 right-0 px-2 py-1 text-xs font-mono text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-800 rounded-bl rounded-tr border-b border-l border-gray-200 dark:border-gray-700", children: language }),
|
|
4401
|
-
/* @__PURE__ */ jsxRuntime.jsx("pre", { className: "bg-gray-100 dark:bg-gray-800 rounded-lg p-4 overflow-x-auto text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("code", { className:
|
|
5354
|
+
/* @__PURE__ */ jsxRuntime.jsx("pre", { className: "bg-gray-100 dark:bg-gray-800 rounded-lg p-4 overflow-x-auto text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("code", { className: cn6("font-mono", codeClassName), ...props, children }) })
|
|
4402
5355
|
] });
|
|
4403
5356
|
}
|
|
4404
5357
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4445,7 +5398,7 @@ function MarkdownContent({
|
|
|
4445
5398
|
() => customComponents ? { ...defaultComponents, ...customComponents } : defaultComponents,
|
|
4446
5399
|
[defaultComponents, customComponents]
|
|
4447
5400
|
);
|
|
4448
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className:
|
|
5401
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn6("prose prose-sm dark:prose-invert max-w-none", className), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4449
5402
|
ReactMarkdown__default.default,
|
|
4450
5403
|
{
|
|
4451
5404
|
remarkPlugins: [remarkGfm__default.default],
|
|
@@ -4455,7 +5408,7 @@ function MarkdownContent({
|
|
|
4455
5408
|
}
|
|
4456
5409
|
) });
|
|
4457
5410
|
}
|
|
4458
|
-
function
|
|
5411
|
+
function cn7(...classes) {
|
|
4459
5412
|
return classes.filter(Boolean).join(" ");
|
|
4460
5413
|
}
|
|
4461
5414
|
function formatToolName(toolName) {
|
|
@@ -4603,7 +5556,7 @@ function ToolCallEntry({
|
|
|
4603
5556
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4604
5557
|
"div",
|
|
4605
5558
|
{
|
|
4606
|
-
className:
|
|
5559
|
+
className: cn7(
|
|
4607
5560
|
"rounded-lg border text-sm",
|
|
4608
5561
|
statusColor,
|
|
4609
5562
|
className
|
|
@@ -4624,7 +5577,7 @@ function ToolCallEntry({
|
|
|
4624
5577
|
]
|
|
4625
5578
|
}
|
|
4626
5579
|
),
|
|
4627
|
-
hasImages && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className:
|
|
5580
|
+
hasImages && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn7(
|
|
4628
5581
|
"grid gap-2",
|
|
4629
5582
|
resultImages.length === 1 ? "grid-cols-1" : "grid-cols-2"
|
|
4630
5583
|
), children: resultImages.map((img, idx) => {
|
|
@@ -4683,7 +5636,7 @@ function ToolCallEntry({
|
|
|
4683
5636
|
}
|
|
4684
5637
|
);
|
|
4685
5638
|
}
|
|
4686
|
-
function
|
|
5639
|
+
function cn8(...classes) {
|
|
4687
5640
|
return classes.filter(Boolean).join(" ");
|
|
4688
5641
|
}
|
|
4689
5642
|
function ChatMessage({
|
|
@@ -4705,7 +5658,7 @@ function ChatMessage({
|
|
|
4705
5658
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4706
5659
|
"div",
|
|
4707
5660
|
{
|
|
4708
|
-
className:
|
|
5661
|
+
className: cn8(
|
|
4709
5662
|
"flex gap-3 p-4",
|
|
4710
5663
|
isUser ? "flex-row-reverse" : "flex-row",
|
|
4711
5664
|
className
|
|
@@ -4714,7 +5667,7 @@ function ChatMessage({
|
|
|
4714
5667
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4715
5668
|
"div",
|
|
4716
5669
|
{
|
|
4717
|
-
className:
|
|
5670
|
+
className: cn8(
|
|
4718
5671
|
"flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center",
|
|
4719
5672
|
isUser ? "bg-blue-500 text-white" : "bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300"
|
|
4720
5673
|
),
|
|
@@ -4724,7 +5677,7 @@ function ChatMessage({
|
|
|
4724
5677
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4725
5678
|
"div",
|
|
4726
5679
|
{
|
|
4727
|
-
className:
|
|
5680
|
+
className: cn8(
|
|
4728
5681
|
"flex-1 min-w-0 space-y-2",
|
|
4729
5682
|
isUser ? "text-right" : "text-left"
|
|
4730
5683
|
),
|
|
@@ -4732,7 +5685,7 @@ function ChatMessage({
|
|
|
4732
5685
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4733
5686
|
"div",
|
|
4734
5687
|
{
|
|
4735
|
-
className:
|
|
5688
|
+
className: cn8(
|
|
4736
5689
|
"flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400",
|
|
4737
5690
|
isUser ? "justify-end" : "justify-start"
|
|
4738
5691
|
),
|
|
@@ -4765,7 +5718,7 @@ function ChatMessage({
|
|
|
4765
5718
|
message.content && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4766
5719
|
"div",
|
|
4767
5720
|
{
|
|
4768
|
-
className:
|
|
5721
|
+
className: cn8(
|
|
4769
5722
|
"rounded-lg p-3",
|
|
4770
5723
|
isUser ? "bg-blue-500 text-white" : "bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100"
|
|
4771
5724
|
),
|
|
@@ -4795,7 +5748,7 @@ function ChatMessage({
|
|
|
4795
5748
|
}
|
|
4796
5749
|
);
|
|
4797
5750
|
}
|
|
4798
|
-
function
|
|
5751
|
+
function cn9(...classes) {
|
|
4799
5752
|
return classes.filter(Boolean).join(" ");
|
|
4800
5753
|
}
|
|
4801
5754
|
function MessageList({
|
|
@@ -4825,7 +5778,7 @@ function MessageList({
|
|
|
4825
5778
|
"div",
|
|
4826
5779
|
{
|
|
4827
5780
|
ref: scrollRef,
|
|
4828
|
-
className:
|
|
5781
|
+
className: cn9(
|
|
4829
5782
|
"flex-1 overflow-y-auto",
|
|
4830
5783
|
className
|
|
4831
5784
|
),
|
|
@@ -4918,6 +5871,7 @@ function AgentChat({
|
|
|
4918
5871
|
isStreaming,
|
|
4919
5872
|
isLoadingHistory,
|
|
4920
5873
|
error,
|
|
5874
|
+
sessionId: currentSessionId,
|
|
4921
5875
|
clarification,
|
|
4922
5876
|
sendMessage,
|
|
4923
5877
|
respondToClarification,
|
|
@@ -4938,8 +5892,59 @@ function AgentChat({
|
|
|
4938
5892
|
onToolResult,
|
|
4939
5893
|
onClarification
|
|
4940
5894
|
});
|
|
5895
|
+
const dropZoneRef = react.useRef(null);
|
|
5896
|
+
const attachments = useFileAttachments({
|
|
5897
|
+
onUpload: enableAttachments && adapter ? async (_file, meta) => {
|
|
5898
|
+
const result = await adapter.getUploadUrl({
|
|
5899
|
+
sessionId: currentSessionId ?? "",
|
|
5900
|
+
...meta
|
|
5901
|
+
});
|
|
5902
|
+
return { uuid: result.attachmentUuid, uploadUrl: result.uploadUrl };
|
|
5903
|
+
} : async () => ({ uuid: "", uploadUrl: "" }),
|
|
5904
|
+
onConfirmUpload: enableAttachments && adapter ? (uuid) => adapter.confirmUpload(uuid) : void 0,
|
|
5905
|
+
onError: (msg) => {
|
|
5906
|
+
console.error("[AgentChat] Attachment error:", msg);
|
|
5907
|
+
}
|
|
5908
|
+
});
|
|
5909
|
+
const handleSend = react.useCallback(
|
|
5910
|
+
async (content) => {
|
|
5911
|
+
if (enableAttachments && attachments.hasReadyFiles) {
|
|
5912
|
+
const uuids = attachments.getReadyUuids();
|
|
5913
|
+
await sendMessage(content, { attachmentUuids: uuids });
|
|
5914
|
+
attachments.clear();
|
|
5915
|
+
} else {
|
|
5916
|
+
await sendMessage(content);
|
|
5917
|
+
}
|
|
5918
|
+
},
|
|
5919
|
+
[enableAttachments, attachments, sendMessage]
|
|
5920
|
+
);
|
|
4941
5921
|
return /* @__PURE__ */ jsxRuntime.jsxs(ChatContainer, { maxHeight, className, theme, children: [
|
|
4942
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5922
|
+
enableAttachments ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
5923
|
+
FileDropZone,
|
|
5924
|
+
{
|
|
5925
|
+
ref: dropZoneRef,
|
|
5926
|
+
...attachments.dropZoneProps,
|
|
5927
|
+
disabled: isLoading || !!clarification || isLoadingHistory,
|
|
5928
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5929
|
+
MessageList,
|
|
5930
|
+
{
|
|
5931
|
+
messages,
|
|
5932
|
+
welcomeMessage,
|
|
5933
|
+
renderWelcome,
|
|
5934
|
+
isLoading,
|
|
5935
|
+
isStreaming,
|
|
5936
|
+
isLoadingHistory,
|
|
5937
|
+
enableMarkdown,
|
|
5938
|
+
showToolCalls,
|
|
5939
|
+
markdownComponents,
|
|
5940
|
+
markdownUrlTransform,
|
|
5941
|
+
markdownContentTransform,
|
|
5942
|
+
onImageClick,
|
|
5943
|
+
sendMessage: handleSend
|
|
5944
|
+
}
|
|
5945
|
+
)
|
|
5946
|
+
}
|
|
5947
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
4943
5948
|
MessageList,
|
|
4944
5949
|
{
|
|
4945
5950
|
messages,
|
|
@@ -4954,7 +5959,7 @@ function AgentChat({
|
|
|
4954
5959
|
markdownUrlTransform,
|
|
4955
5960
|
markdownContentTransform,
|
|
4956
5961
|
onImageClick,
|
|
4957
|
-
sendMessage
|
|
5962
|
+
sendMessage: handleSend
|
|
4958
5963
|
}
|
|
4959
5964
|
),
|
|
4960
5965
|
clarification && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-gray-200 dark:border-gray-700 p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4969,17 +5974,21 @@ function AgentChat({
|
|
|
4969
5974
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4970
5975
|
ChatInput,
|
|
4971
5976
|
{
|
|
4972
|
-
onSend:
|
|
5977
|
+
onSend: handleSend,
|
|
4973
5978
|
onStop: isStreaming ? cancel : void 0,
|
|
4974
5979
|
placeholder,
|
|
4975
5980
|
disabled: !!clarification || isLoadingHistory,
|
|
4976
5981
|
isLoading,
|
|
4977
|
-
enableAttachments
|
|
5982
|
+
enableAttachments,
|
|
5983
|
+
attachments: enableAttachments ? attachments.files : void 0,
|
|
5984
|
+
onRemoveAttachment: enableAttachments ? attachments.removeFile : void 0,
|
|
5985
|
+
dropZoneRef: enableAttachments ? dropZoneRef : void 0,
|
|
5986
|
+
isUploadingAttachments: enableAttachments ? attachments.isUploading : void 0
|
|
4978
5987
|
}
|
|
4979
5988
|
)
|
|
4980
5989
|
] });
|
|
4981
5990
|
}
|
|
4982
|
-
function
|
|
5991
|
+
function cn10(...classes) {
|
|
4983
5992
|
return classes.filter(Boolean).join(" ");
|
|
4984
5993
|
}
|
|
4985
5994
|
function TodoList({
|
|
@@ -5008,7 +6017,7 @@ function TodoList({
|
|
|
5008
6017
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5009
6018
|
"div",
|
|
5010
6019
|
{
|
|
5011
|
-
className:
|
|
6020
|
+
className: cn10(
|
|
5012
6021
|
"rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden",
|
|
5013
6022
|
className
|
|
5014
6023
|
),
|
|
@@ -5016,7 +6025,7 @@ function TodoList({
|
|
|
5016
6025
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
5017
6026
|
"div",
|
|
5018
6027
|
{
|
|
5019
|
-
className:
|
|
6028
|
+
className: cn10(
|
|
5020
6029
|
"flex items-center gap-2 px-3 py-2 bg-gray-50 dark:bg-gray-800/50",
|
|
5021
6030
|
collapsible && "cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"
|
|
5022
6031
|
),
|
|
@@ -5047,7 +6056,7 @@ function TodoList({
|
|
|
5047
6056
|
!isCollapsed && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "divide-y divide-gray-100 dark:divide-gray-700", children: todos.map((todo, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5048
6057
|
"li",
|
|
5049
6058
|
{
|
|
5050
|
-
className:
|
|
6059
|
+
className: cn10(
|
|
5051
6060
|
"px-3 py-2 flex items-start gap-2 text-sm",
|
|
5052
6061
|
todo.status === "in_progress" && "bg-blue-50/50 dark:bg-blue-950/20",
|
|
5053
6062
|
todo.status === "completed" && "opacity-60"
|
|
@@ -5057,7 +6066,7 @@ function TodoList({
|
|
|
5057
6066
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5058
6067
|
"p",
|
|
5059
6068
|
{
|
|
5060
|
-
className:
|
|
6069
|
+
className: cn10(
|
|
5061
6070
|
"text-gray-700 dark:text-gray-300",
|
|
5062
6071
|
todo.status === "completed" && "line-through"
|
|
5063
6072
|
),
|
|
@@ -5094,6 +6103,7 @@ exports.CopyIcon = CopyIcon;
|
|
|
5094
6103
|
exports.DatabaseIcon = DatabaseIcon;
|
|
5095
6104
|
exports.DownloadIcon = DownloadIcon;
|
|
5096
6105
|
exports.ExternalLinkIcon = ExternalLinkIcon;
|
|
6106
|
+
exports.FileDropZone = FileDropZone;
|
|
5097
6107
|
exports.FileIcon = FileIcon;
|
|
5098
6108
|
exports.FileTextIcon = FileTextIcon;
|
|
5099
6109
|
exports.HelpCircleIcon = HelpCircleIcon;
|
|
@@ -5116,10 +6126,12 @@ exports.UserIcon = UserIcon;
|
|
|
5116
6126
|
exports.XIcon = XIcon;
|
|
5117
6127
|
exports.ZoomInIcon = ZoomInIcon;
|
|
5118
6128
|
exports.ZoomOutIcon = ZoomOutIcon;
|
|
6129
|
+
exports.compressImage = compressImage;
|
|
5119
6130
|
exports.createDefaultAdapter = createDefaultAdapter;
|
|
5120
6131
|
exports.useAgentChat = useAgentChat;
|
|
5121
6132
|
exports.useChatConfig = useChatConfig;
|
|
5122
6133
|
exports.useChatConfigOptional = useChatConfigOptional;
|
|
5123
6134
|
exports.useExecutionStream = useExecutionStream;
|
|
6135
|
+
exports.useFileAttachments = useFileAttachments;
|
|
5124
6136
|
//# sourceMappingURL=index.cjs.map
|
|
5125
6137
|
//# sourceMappingURL=index.cjs.map
|