@cadraos/sdk 0.1.5 → 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 +1069 -34
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +221 -7
- package/dist/chat/index.d.ts +221 -7
- package/dist/chat/index.js +1069 -37
- package/dist/chat/index.js.map +1 -1
- package/dist/index.cjs +23 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -2
- package/dist/index.d.ts +54 -2
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/chat/index.cjs
CHANGED
|
@@ -1530,6 +1530,29 @@ var ToolsAPI = class extends BaseResourceAPI {
|
|
|
1530
1530
|
const idempotencyKey = generateIdempotencyKey();
|
|
1531
1531
|
return this.httpPost(`/${uuid}/execute`, { input }, { idempotencyKey });
|
|
1532
1532
|
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Execute a tool directly, bypassing the agent runtime.
|
|
1535
|
+
*
|
|
1536
|
+
* Enqueues the tool for async processing via BullMQ (no LLM, no ReAct loop).
|
|
1537
|
+
* Use SSE or polling with the returned executionId to get the result.
|
|
1538
|
+
*
|
|
1539
|
+
* @param request - Direct execution request
|
|
1540
|
+
* @returns Queued execution response with executionId for tracking
|
|
1541
|
+
*
|
|
1542
|
+
* @example
|
|
1543
|
+
* ```typescript
|
|
1544
|
+
* const result = await client.tools.executeDirect({
|
|
1545
|
+
* toolUuid: 'tool-uuid',
|
|
1546
|
+
* parameters: { prompt: 'Generate an image of a sunset' },
|
|
1547
|
+
* });
|
|
1548
|
+
* console.log(result.executionId); // Use for polling/SSE
|
|
1549
|
+
* console.log(result.status); // 'queued'
|
|
1550
|
+
* ```
|
|
1551
|
+
*/
|
|
1552
|
+
async executeDirect(request) {
|
|
1553
|
+
const idempotencyKey = generateIdempotencyKey();
|
|
1554
|
+
return this.httpPost("/execute-direct", request, { idempotencyKey });
|
|
1555
|
+
}
|
|
1533
1556
|
/**
|
|
1534
1557
|
* Validate input against tool schema
|
|
1535
1558
|
*
|
|
@@ -3198,8 +3221,9 @@ function useAgentChat(options) {
|
|
|
3198
3221
|
[adapter, handleExecutionLog, updateStreamingMessage, updateToolCall, handleClarification, handleComplete, handleError]
|
|
3199
3222
|
);
|
|
3200
3223
|
const sendMessage = react.useCallback(
|
|
3201
|
-
async (content) => {
|
|
3224
|
+
async (content, options2) => {
|
|
3202
3225
|
if (!content.trim()) return;
|
|
3226
|
+
const attachmentUuids = options2?.attachmentUuids;
|
|
3203
3227
|
lastMessageContentRef.current = content;
|
|
3204
3228
|
setIsLoading(true);
|
|
3205
3229
|
setError(null);
|
|
@@ -3230,7 +3254,8 @@ function useAgentChat(options) {
|
|
|
3230
3254
|
agentId,
|
|
3231
3255
|
teamId,
|
|
3232
3256
|
sessionId: currentSessionId ?? void 0,
|
|
3233
|
-
content: content.trim()
|
|
3257
|
+
content: content.trim(),
|
|
3258
|
+
attachmentUuids
|
|
3234
3259
|
});
|
|
3235
3260
|
console.log("[useAgentChat] Execution started:", result);
|
|
3236
3261
|
if (!currentSessionId && result.sessionId) {
|
|
@@ -3439,6 +3464,222 @@ function useAgentChat(options) {
|
|
|
3439
3464
|
loadSession
|
|
3440
3465
|
};
|
|
3441
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
|
+
}
|
|
3442
3683
|
function cn(...classes) {
|
|
3443
3684
|
return classes.filter(Boolean).join(" ");
|
|
3444
3685
|
}
|
|
@@ -4088,6 +4329,51 @@ function ChatContainer({
|
|
|
4088
4329
|
function cn3(...classes) {
|
|
4089
4330
|
return classes.filter(Boolean).join(" ");
|
|
4090
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
|
+
}
|
|
4091
4377
|
function ChatInput({
|
|
4092
4378
|
onSend,
|
|
4093
4379
|
onStop,
|
|
@@ -4095,10 +4381,15 @@ function ChatInput({
|
|
|
4095
4381
|
disabled = false,
|
|
4096
4382
|
isLoading = false,
|
|
4097
4383
|
enableAttachments = false,
|
|
4384
|
+
attachments = [],
|
|
4385
|
+
onRemoveAttachment,
|
|
4386
|
+
dropZoneRef,
|
|
4387
|
+
isUploadingAttachments = false,
|
|
4098
4388
|
className
|
|
4099
4389
|
}) {
|
|
4100
4390
|
const [content, setContent] = react.useState("");
|
|
4101
4391
|
const textareaRef = react.useRef(null);
|
|
4392
|
+
const hasAttachments = enableAttachments && attachments.length > 0;
|
|
4102
4393
|
react.useEffect(() => {
|
|
4103
4394
|
const textarea = textareaRef.current;
|
|
4104
4395
|
if (textarea) {
|
|
@@ -4108,10 +4399,10 @@ function ChatInput({
|
|
|
4108
4399
|
}, [content]);
|
|
4109
4400
|
const handleSubmit = react.useCallback(async () => {
|
|
4110
4401
|
const trimmedContent = content.trim();
|
|
4111
|
-
if (!trimmedContent || disabled || isLoading) return;
|
|
4402
|
+
if (!trimmedContent && !hasAttachments || disabled || isLoading || isUploadingAttachments) return;
|
|
4112
4403
|
setContent("");
|
|
4113
4404
|
await onSend(trimmedContent);
|
|
4114
|
-
}, [content, disabled, isLoading, onSend]);
|
|
4405
|
+
}, [content, hasAttachments, disabled, isLoading, isUploadingAttachments, onSend]);
|
|
4115
4406
|
const handleKeyDown = react.useCallback(
|
|
4116
4407
|
(e) => {
|
|
4117
4408
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
@@ -4124,7 +4415,10 @@ function ChatInput({
|
|
|
4124
4415
|
const handleStopClick = react.useCallback(() => {
|
|
4125
4416
|
onStop?.();
|
|
4126
4417
|
}, [onStop]);
|
|
4127
|
-
const
|
|
4418
|
+
const handleAttachClick = react.useCallback(() => {
|
|
4419
|
+
dropZoneRef?.current?.openFileDialog();
|
|
4420
|
+
}, [dropZoneRef]);
|
|
4421
|
+
const canSend = (content.trim().length > 0 || hasAttachments) && !disabled && !isLoading && !isUploadingAttachments;
|
|
4128
4422
|
const showStopButton = isLoading && onStop;
|
|
4129
4423
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4130
4424
|
"div",
|
|
@@ -4134,7 +4428,34 @@ function ChatInput({
|
|
|
4134
4428
|
className
|
|
4135
4429
|
),
|
|
4136
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
|
+
)) }),
|
|
4137
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
|
+
),
|
|
4138
4459
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4139
4460
|
"textarea",
|
|
4140
4461
|
{
|
|
@@ -4142,7 +4463,7 @@ function ChatInput({
|
|
|
4142
4463
|
value: content,
|
|
4143
4464
|
onChange: (e) => setContent(e.target.value),
|
|
4144
4465
|
onKeyDown: handleKeyDown,
|
|
4145
|
-
placeholder,
|
|
4466
|
+
placeholder: hasAttachments && !content ? `${attachments.length} file${attachments.length > 1 ? "s" : ""} attached. Add a message or press Enter to send.` : placeholder,
|
|
4146
4467
|
disabled,
|
|
4147
4468
|
rows: 1,
|
|
4148
4469
|
className: cn3(
|
|
@@ -4192,9 +4513,664 @@ function ChatInput({
|
|
|
4192
4513
|
}
|
|
4193
4514
|
);
|
|
4194
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
|
+
}
|
|
4195
4567
|
function cn4(...classes) {
|
|
4196
4568
|
return classes.filter(Boolean).join(" ");
|
|
4197
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
|
+
}
|
|
4198
5174
|
function AskUserPrompt({
|
|
4199
5175
|
question,
|
|
4200
5176
|
options,
|
|
@@ -4228,7 +5204,7 @@ function AskUserPrompt({
|
|
|
4228
5204
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4229
5205
|
"div",
|
|
4230
5206
|
{
|
|
4231
|
-
className:
|
|
5207
|
+
className: cn5(
|
|
4232
5208
|
"rounded-lg border p-4 space-y-3",
|
|
4233
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",
|
|
4234
5210
|
className
|
|
@@ -4240,7 +5216,7 @@ function AskUserPrompt({
|
|
|
4240
5216
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4241
5217
|
"span",
|
|
4242
5218
|
{
|
|
4243
|
-
className:
|
|
5219
|
+
className: cn5(
|
|
4244
5220
|
"ml-auto text-xs px-2 py-1 rounded-full font-medium",
|
|
4245
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"
|
|
4246
5222
|
),
|
|
@@ -4255,7 +5231,7 @@ function AskUserPrompt({
|
|
|
4255
5231
|
type: "button",
|
|
4256
5232
|
onClick: () => handleOptionSelect(option),
|
|
4257
5233
|
disabled,
|
|
4258
|
-
className:
|
|
5234
|
+
className: cn5(
|
|
4259
5235
|
"px-3 py-1.5 text-sm rounded-md border transition-all",
|
|
4260
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",
|
|
4261
5237
|
disabled && "opacity-50 cursor-not-allowed"
|
|
@@ -4275,7 +5251,7 @@ function AskUserPrompt({
|
|
|
4275
5251
|
},
|
|
4276
5252
|
onKeyDown: handleKeyDown,
|
|
4277
5253
|
placeholder: options?.length ? "Or type a custom response..." : "Type your response...",
|
|
4278
|
-
className:
|
|
5254
|
+
className: cn5(
|
|
4279
5255
|
"flex-1 min-h-[60px] px-3 py-2 text-sm resize-none rounded-md border",
|
|
4280
5256
|
"border-gray-300 dark:border-gray-600",
|
|
4281
5257
|
"bg-white dark:bg-gray-800",
|
|
@@ -4293,7 +5269,7 @@ function AskUserPrompt({
|
|
|
4293
5269
|
type: "button",
|
|
4294
5270
|
onClick: handleSubmit,
|
|
4295
5271
|
disabled: isSubmitDisabled,
|
|
4296
|
-
className:
|
|
5272
|
+
className: cn5(
|
|
4297
5273
|
"self-end px-4 py-2 rounded-md text-white transition-colors",
|
|
4298
5274
|
"bg-blue-600 hover:bg-blue-700",
|
|
4299
5275
|
"disabled:opacity-50 disabled:cursor-not-allowed"
|
|
@@ -4314,7 +5290,7 @@ function AskUserPrompt({
|
|
|
4314
5290
|
}
|
|
4315
5291
|
);
|
|
4316
5292
|
}
|
|
4317
|
-
function
|
|
5293
|
+
function cn6(...classes) {
|
|
4318
5294
|
return classes.filter(Boolean).join(" ");
|
|
4319
5295
|
}
|
|
4320
5296
|
function MarkdownContent({
|
|
@@ -4375,7 +5351,7 @@ function MarkdownContent({
|
|
|
4375
5351
|
if (isCodeBlock) {
|
|
4376
5352
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative my-2", children: [
|
|
4377
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 }),
|
|
4378
|
-
/* @__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 }) })
|
|
4379
5355
|
] });
|
|
4380
5356
|
}
|
|
4381
5357
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4422,7 +5398,7 @@ function MarkdownContent({
|
|
|
4422
5398
|
() => customComponents ? { ...defaultComponents, ...customComponents } : defaultComponents,
|
|
4423
5399
|
[defaultComponents, customComponents]
|
|
4424
5400
|
);
|
|
4425
|
-
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(
|
|
4426
5402
|
ReactMarkdown__default.default,
|
|
4427
5403
|
{
|
|
4428
5404
|
remarkPlugins: [remarkGfm__default.default],
|
|
@@ -4432,7 +5408,7 @@ function MarkdownContent({
|
|
|
4432
5408
|
}
|
|
4433
5409
|
) });
|
|
4434
5410
|
}
|
|
4435
|
-
function
|
|
5411
|
+
function cn7(...classes) {
|
|
4436
5412
|
return classes.filter(Boolean).join(" ");
|
|
4437
5413
|
}
|
|
4438
5414
|
function formatToolName(toolName) {
|
|
@@ -4580,7 +5556,7 @@ function ToolCallEntry({
|
|
|
4580
5556
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4581
5557
|
"div",
|
|
4582
5558
|
{
|
|
4583
|
-
className:
|
|
5559
|
+
className: cn7(
|
|
4584
5560
|
"rounded-lg border text-sm",
|
|
4585
5561
|
statusColor,
|
|
4586
5562
|
className
|
|
@@ -4601,7 +5577,7 @@ function ToolCallEntry({
|
|
|
4601
5577
|
]
|
|
4602
5578
|
}
|
|
4603
5579
|
),
|
|
4604
|
-
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(
|
|
4605
5581
|
"grid gap-2",
|
|
4606
5582
|
resultImages.length === 1 ? "grid-cols-1" : "grid-cols-2"
|
|
4607
5583
|
), children: resultImages.map((img, idx) => {
|
|
@@ -4660,7 +5636,7 @@ function ToolCallEntry({
|
|
|
4660
5636
|
}
|
|
4661
5637
|
);
|
|
4662
5638
|
}
|
|
4663
|
-
function
|
|
5639
|
+
function cn8(...classes) {
|
|
4664
5640
|
return classes.filter(Boolean).join(" ");
|
|
4665
5641
|
}
|
|
4666
5642
|
function ChatMessage({
|
|
@@ -4682,7 +5658,7 @@ function ChatMessage({
|
|
|
4682
5658
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4683
5659
|
"div",
|
|
4684
5660
|
{
|
|
4685
|
-
className:
|
|
5661
|
+
className: cn8(
|
|
4686
5662
|
"flex gap-3 p-4",
|
|
4687
5663
|
isUser ? "flex-row-reverse" : "flex-row",
|
|
4688
5664
|
className
|
|
@@ -4691,7 +5667,7 @@ function ChatMessage({
|
|
|
4691
5667
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4692
5668
|
"div",
|
|
4693
5669
|
{
|
|
4694
|
-
className:
|
|
5670
|
+
className: cn8(
|
|
4695
5671
|
"flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center",
|
|
4696
5672
|
isUser ? "bg-blue-500 text-white" : "bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300"
|
|
4697
5673
|
),
|
|
@@ -4701,7 +5677,7 @@ function ChatMessage({
|
|
|
4701
5677
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4702
5678
|
"div",
|
|
4703
5679
|
{
|
|
4704
|
-
className:
|
|
5680
|
+
className: cn8(
|
|
4705
5681
|
"flex-1 min-w-0 space-y-2",
|
|
4706
5682
|
isUser ? "text-right" : "text-left"
|
|
4707
5683
|
),
|
|
@@ -4709,7 +5685,7 @@ function ChatMessage({
|
|
|
4709
5685
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4710
5686
|
"div",
|
|
4711
5687
|
{
|
|
4712
|
-
className:
|
|
5688
|
+
className: cn8(
|
|
4713
5689
|
"flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400",
|
|
4714
5690
|
isUser ? "justify-end" : "justify-start"
|
|
4715
5691
|
),
|
|
@@ -4742,7 +5718,7 @@ function ChatMessage({
|
|
|
4742
5718
|
message.content && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4743
5719
|
"div",
|
|
4744
5720
|
{
|
|
4745
|
-
className:
|
|
5721
|
+
className: cn8(
|
|
4746
5722
|
"rounded-lg p-3",
|
|
4747
5723
|
isUser ? "bg-blue-500 text-white" : "bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100"
|
|
4748
5724
|
),
|
|
@@ -4772,7 +5748,7 @@ function ChatMessage({
|
|
|
4772
5748
|
}
|
|
4773
5749
|
);
|
|
4774
5750
|
}
|
|
4775
|
-
function
|
|
5751
|
+
function cn9(...classes) {
|
|
4776
5752
|
return classes.filter(Boolean).join(" ");
|
|
4777
5753
|
}
|
|
4778
5754
|
function MessageList({
|
|
@@ -4802,7 +5778,7 @@ function MessageList({
|
|
|
4802
5778
|
"div",
|
|
4803
5779
|
{
|
|
4804
5780
|
ref: scrollRef,
|
|
4805
|
-
className:
|
|
5781
|
+
className: cn9(
|
|
4806
5782
|
"flex-1 overflow-y-auto",
|
|
4807
5783
|
className
|
|
4808
5784
|
),
|
|
@@ -4895,6 +5871,7 @@ function AgentChat({
|
|
|
4895
5871
|
isStreaming,
|
|
4896
5872
|
isLoadingHistory,
|
|
4897
5873
|
error,
|
|
5874
|
+
sessionId: currentSessionId,
|
|
4898
5875
|
clarification,
|
|
4899
5876
|
sendMessage,
|
|
4900
5877
|
respondToClarification,
|
|
@@ -4915,8 +5892,59 @@ function AgentChat({
|
|
|
4915
5892
|
onToolResult,
|
|
4916
5893
|
onClarification
|
|
4917
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
|
+
);
|
|
4918
5921
|
return /* @__PURE__ */ jsxRuntime.jsxs(ChatContainer, { maxHeight, className, theme, children: [
|
|
4919
|
-
/* @__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(
|
|
4920
5948
|
MessageList,
|
|
4921
5949
|
{
|
|
4922
5950
|
messages,
|
|
@@ -4931,7 +5959,7 @@ function AgentChat({
|
|
|
4931
5959
|
markdownUrlTransform,
|
|
4932
5960
|
markdownContentTransform,
|
|
4933
5961
|
onImageClick,
|
|
4934
|
-
sendMessage
|
|
5962
|
+
sendMessage: handleSend
|
|
4935
5963
|
}
|
|
4936
5964
|
),
|
|
4937
5965
|
clarification && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-gray-200 dark:border-gray-700 p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4946,17 +5974,21 @@ function AgentChat({
|
|
|
4946
5974
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4947
5975
|
ChatInput,
|
|
4948
5976
|
{
|
|
4949
|
-
onSend:
|
|
5977
|
+
onSend: handleSend,
|
|
4950
5978
|
onStop: isStreaming ? cancel : void 0,
|
|
4951
5979
|
placeholder,
|
|
4952
5980
|
disabled: !!clarification || isLoadingHistory,
|
|
4953
5981
|
isLoading,
|
|
4954
|
-
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
|
|
4955
5987
|
}
|
|
4956
5988
|
)
|
|
4957
5989
|
] });
|
|
4958
5990
|
}
|
|
4959
|
-
function
|
|
5991
|
+
function cn10(...classes) {
|
|
4960
5992
|
return classes.filter(Boolean).join(" ");
|
|
4961
5993
|
}
|
|
4962
5994
|
function TodoList({
|
|
@@ -4985,7 +6017,7 @@ function TodoList({
|
|
|
4985
6017
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4986
6018
|
"div",
|
|
4987
6019
|
{
|
|
4988
|
-
className:
|
|
6020
|
+
className: cn10(
|
|
4989
6021
|
"rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden",
|
|
4990
6022
|
className
|
|
4991
6023
|
),
|
|
@@ -4993,7 +6025,7 @@ function TodoList({
|
|
|
4993
6025
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4994
6026
|
"div",
|
|
4995
6027
|
{
|
|
4996
|
-
className:
|
|
6028
|
+
className: cn10(
|
|
4997
6029
|
"flex items-center gap-2 px-3 py-2 bg-gray-50 dark:bg-gray-800/50",
|
|
4998
6030
|
collapsible && "cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"
|
|
4999
6031
|
),
|
|
@@ -5024,7 +6056,7 @@ function TodoList({
|
|
|
5024
6056
|
!isCollapsed && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "divide-y divide-gray-100 dark:divide-gray-700", children: todos.map((todo, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5025
6057
|
"li",
|
|
5026
6058
|
{
|
|
5027
|
-
className:
|
|
6059
|
+
className: cn10(
|
|
5028
6060
|
"px-3 py-2 flex items-start gap-2 text-sm",
|
|
5029
6061
|
todo.status === "in_progress" && "bg-blue-50/50 dark:bg-blue-950/20",
|
|
5030
6062
|
todo.status === "completed" && "opacity-60"
|
|
@@ -5034,7 +6066,7 @@ function TodoList({
|
|
|
5034
6066
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5035
6067
|
"p",
|
|
5036
6068
|
{
|
|
5037
|
-
className:
|
|
6069
|
+
className: cn10(
|
|
5038
6070
|
"text-gray-700 dark:text-gray-300",
|
|
5039
6071
|
todo.status === "completed" && "line-through"
|
|
5040
6072
|
),
|
|
@@ -5071,6 +6103,7 @@ exports.CopyIcon = CopyIcon;
|
|
|
5071
6103
|
exports.DatabaseIcon = DatabaseIcon;
|
|
5072
6104
|
exports.DownloadIcon = DownloadIcon;
|
|
5073
6105
|
exports.ExternalLinkIcon = ExternalLinkIcon;
|
|
6106
|
+
exports.FileDropZone = FileDropZone;
|
|
5074
6107
|
exports.FileIcon = FileIcon;
|
|
5075
6108
|
exports.FileTextIcon = FileTextIcon;
|
|
5076
6109
|
exports.HelpCircleIcon = HelpCircleIcon;
|
|
@@ -5093,10 +6126,12 @@ exports.UserIcon = UserIcon;
|
|
|
5093
6126
|
exports.XIcon = XIcon;
|
|
5094
6127
|
exports.ZoomInIcon = ZoomInIcon;
|
|
5095
6128
|
exports.ZoomOutIcon = ZoomOutIcon;
|
|
6129
|
+
exports.compressImage = compressImage;
|
|
5096
6130
|
exports.createDefaultAdapter = createDefaultAdapter;
|
|
5097
6131
|
exports.useAgentChat = useAgentChat;
|
|
5098
6132
|
exports.useChatConfig = useChatConfig;
|
|
5099
6133
|
exports.useChatConfigOptional = useChatConfigOptional;
|
|
5100
6134
|
exports.useExecutionStream = useExecutionStream;
|
|
6135
|
+
exports.useFileAttachments = useFileAttachments;
|
|
5101
6136
|
//# sourceMappingURL=index.cjs.map
|
|
5102
6137
|
//# sourceMappingURL=index.cjs.map
|