@cadraos/sdk 0.1.6 → 0.1.8
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/README.md +38 -5
- package/dist/chat/index.cjs +1180 -36
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +229 -15
- package/dist/chat/index.d.ts +229 -15
- package/dist/chat/index.js +1180 -39
- 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 +6 -3
package/dist/chat/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { createContext,
|
|
2
|
-
import {
|
|
1
|
+
import { createContext, forwardRef, useState, useRef, useImperativeHandle, useCallback, useEffect, useMemo, useContext } from 'react';
|
|
2
|
+
import { fetchEventSource } from '@microsoft/fetch-event-source';
|
|
3
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
4
|
import ReactMarkdown from 'react-markdown';
|
|
4
5
|
import remarkGfm from 'remark-gfm';
|
|
5
6
|
|
|
@@ -2203,7 +2204,7 @@ function resolveClient(config) {
|
|
|
2203
2204
|
}
|
|
2204
2205
|
function resolveBaseUrl(config) {
|
|
2205
2206
|
if (config.baseUrl) return config.baseUrl;
|
|
2206
|
-
return "https://api.
|
|
2207
|
+
return "https://api.cadra.io/v1";
|
|
2207
2208
|
}
|
|
2208
2209
|
async function getAuthHeaders(config) {
|
|
2209
2210
|
const headers = {
|
|
@@ -2309,7 +2310,7 @@ function createDefaultAdapter(config) {
|
|
|
2309
2310
|
};
|
|
2310
2311
|
},
|
|
2311
2312
|
// -------------------------------------------------------------------------
|
|
2312
|
-
// Attachment Methods (raw fetch —
|
|
2313
|
+
// Attachment Methods (raw fetch — CadraClient has no AttachmentsAPI yet)
|
|
2313
2314
|
// -------------------------------------------------------------------------
|
|
2314
2315
|
async getUploadUrl(input) {
|
|
2315
2316
|
return rawRequest(config, "POST", "/attachments/upload-url", {
|
|
@@ -2389,8 +2390,106 @@ function createDefaultAdapter(config) {
|
|
|
2389
2390
|
// -------------------------------------------------------------------------
|
|
2390
2391
|
// SSE Stream URL (legacy backward compat)
|
|
2391
2392
|
// -------------------------------------------------------------------------
|
|
2393
|
+
/**
|
|
2394
|
+
* @deprecated Use subscribeToStream() instead. getStreamUrl() exposes
|
|
2395
|
+
* API keys in URLs which are logged by browsers, proxies, and servers.
|
|
2396
|
+
*/
|
|
2392
2397
|
getStreamUrl(executionId) {
|
|
2398
|
+
if (typeof window !== "undefined" && process.env.NODE_ENV !== "production") {
|
|
2399
|
+
const warnKey = "__cadra_getStreamUrl_deprecated__";
|
|
2400
|
+
const win = window;
|
|
2401
|
+
if (!win[warnKey]) {
|
|
2402
|
+
win[warnKey] = true;
|
|
2403
|
+
console.warn(
|
|
2404
|
+
"[CadraOS SDK] getStreamUrl() is deprecated. Use subscribeToStream() instead which provides secure header-based auth. API keys in URLs are a security risk."
|
|
2405
|
+
);
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2393
2408
|
return `${baseUrl}/agents/runs/${executionId}/stream`;
|
|
2409
|
+
},
|
|
2410
|
+
// -------------------------------------------------------------------------
|
|
2411
|
+
// Secure SSE Streaming (Recommended)
|
|
2412
|
+
// -------------------------------------------------------------------------
|
|
2413
|
+
subscribeToStream(executionId, callbacks) {
|
|
2414
|
+
const abortController = new AbortController();
|
|
2415
|
+
const url = `${baseUrl}/api/v1/agents/runs/${executionId}/stream`;
|
|
2416
|
+
const getHeaders = async () => {
|
|
2417
|
+
const headers = {
|
|
2418
|
+
Accept: "text/event-stream",
|
|
2419
|
+
"Cache-Control": "no-cache"
|
|
2420
|
+
};
|
|
2421
|
+
if (config.apiKey) {
|
|
2422
|
+
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
2423
|
+
} else if (config.token) {
|
|
2424
|
+
headers["Authorization"] = `Bearer ${config.token}`;
|
|
2425
|
+
} else if (config.tokenProvider) {
|
|
2426
|
+
const token = await config.tokenProvider();
|
|
2427
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
2428
|
+
}
|
|
2429
|
+
if (config.orgId !== void 0) {
|
|
2430
|
+
headers["X-Org-Id"] = String(config.orgId);
|
|
2431
|
+
}
|
|
2432
|
+
return headers;
|
|
2433
|
+
};
|
|
2434
|
+
const terminalEvents = /* @__PURE__ */ new Set(["complete", "failed", "error"]);
|
|
2435
|
+
(async () => {
|
|
2436
|
+
try {
|
|
2437
|
+
const headers = await getHeaders();
|
|
2438
|
+
callbacks.onConnectionChange?.(true);
|
|
2439
|
+
await fetchEventSource(url, {
|
|
2440
|
+
method: "GET",
|
|
2441
|
+
headers,
|
|
2442
|
+
signal: abortController.signal,
|
|
2443
|
+
openWhenHidden: true,
|
|
2444
|
+
// Keep connection open even when tab is hidden
|
|
2445
|
+
onopen: async (response) => {
|
|
2446
|
+
if (!response.ok) {
|
|
2447
|
+
const errorText = await response.text();
|
|
2448
|
+
throw new Error(`SSE connection failed: ${response.status} - ${errorText}`);
|
|
2449
|
+
}
|
|
2450
|
+
},
|
|
2451
|
+
onmessage: (ev) => {
|
|
2452
|
+
if (!ev.event || ev.event === "") {
|
|
2453
|
+
return;
|
|
2454
|
+
}
|
|
2455
|
+
let data;
|
|
2456
|
+
try {
|
|
2457
|
+
data = ev.data ? JSON.parse(ev.data) : {};
|
|
2458
|
+
} catch {
|
|
2459
|
+
data = ev.data;
|
|
2460
|
+
}
|
|
2461
|
+
const event = {
|
|
2462
|
+
type: ev.event,
|
|
2463
|
+
id: ev.id,
|
|
2464
|
+
data
|
|
2465
|
+
};
|
|
2466
|
+
callbacks.onEvent(event);
|
|
2467
|
+
if (terminalEvents.has(ev.event)) {
|
|
2468
|
+
callbacks.onComplete?.();
|
|
2469
|
+
abortController.abort();
|
|
2470
|
+
}
|
|
2471
|
+
},
|
|
2472
|
+
onerror: (err) => {
|
|
2473
|
+
callbacks.onConnectionChange?.(false);
|
|
2474
|
+
callbacks.onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
2475
|
+
throw err;
|
|
2476
|
+
},
|
|
2477
|
+
onclose: () => {
|
|
2478
|
+
callbacks.onConnectionChange?.(false);
|
|
2479
|
+
}
|
|
2480
|
+
});
|
|
2481
|
+
} catch (err) {
|
|
2482
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
2483
|
+
return;
|
|
2484
|
+
}
|
|
2485
|
+
callbacks.onConnectionChange?.(false);
|
|
2486
|
+
callbacks.onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
2487
|
+
}
|
|
2488
|
+
})();
|
|
2489
|
+
return () => {
|
|
2490
|
+
abortController.abort();
|
|
2491
|
+
callbacks.onConnectionChange?.(false);
|
|
2492
|
+
};
|
|
2394
2493
|
}
|
|
2395
2494
|
};
|
|
2396
2495
|
}
|
|
@@ -3214,8 +3313,9 @@ function useAgentChat(options) {
|
|
|
3214
3313
|
[adapter, handleExecutionLog, updateStreamingMessage, updateToolCall, handleClarification, handleComplete, handleError]
|
|
3215
3314
|
);
|
|
3216
3315
|
const sendMessage = useCallback(
|
|
3217
|
-
async (content) => {
|
|
3316
|
+
async (content, options2) => {
|
|
3218
3317
|
if (!content.trim()) return;
|
|
3318
|
+
const attachmentUuids = options2?.attachmentUuids;
|
|
3219
3319
|
lastMessageContentRef.current = content;
|
|
3220
3320
|
setIsLoading(true);
|
|
3221
3321
|
setError(null);
|
|
@@ -3246,7 +3346,8 @@ function useAgentChat(options) {
|
|
|
3246
3346
|
agentId,
|
|
3247
3347
|
teamId,
|
|
3248
3348
|
sessionId: currentSessionId ?? void 0,
|
|
3249
|
-
content: content.trim()
|
|
3349
|
+
content: content.trim(),
|
|
3350
|
+
attachmentUuids
|
|
3250
3351
|
});
|
|
3251
3352
|
console.log("[useAgentChat] Execution started:", result);
|
|
3252
3353
|
if (!currentSessionId && result.sessionId) {
|
|
@@ -3455,6 +3556,222 @@ function useAgentChat(options) {
|
|
|
3455
3556
|
loadSession
|
|
3456
3557
|
};
|
|
3457
3558
|
}
|
|
3559
|
+
|
|
3560
|
+
// src/chat/utils/compress-image.ts
|
|
3561
|
+
var DEFAULT_TARGET_SIZE_KB = 150;
|
|
3562
|
+
var MAX_DIMENSION = 1920;
|
|
3563
|
+
var QUALITY_START = 0.85;
|
|
3564
|
+
var QUALITY_STEP = 0.05;
|
|
3565
|
+
var QUALITY_MIN = 0.1;
|
|
3566
|
+
function loadImage(file) {
|
|
3567
|
+
return new Promise((resolve, reject) => {
|
|
3568
|
+
const img = new Image();
|
|
3569
|
+
const url = URL.createObjectURL(file);
|
|
3570
|
+
img.onload = () => {
|
|
3571
|
+
URL.revokeObjectURL(url);
|
|
3572
|
+
resolve(img);
|
|
3573
|
+
};
|
|
3574
|
+
img.onerror = () => {
|
|
3575
|
+
URL.revokeObjectURL(url);
|
|
3576
|
+
reject(new Error(`Failed to load image: ${file.name}`));
|
|
3577
|
+
};
|
|
3578
|
+
img.src = url;
|
|
3579
|
+
});
|
|
3580
|
+
}
|
|
3581
|
+
function getScaledDimensions(width, height, maxDim) {
|
|
3582
|
+
if (width <= maxDim && height <= maxDim) {
|
|
3583
|
+
return { width, height };
|
|
3584
|
+
}
|
|
3585
|
+
const ratio = Math.min(maxDim / width, maxDim / height);
|
|
3586
|
+
return {
|
|
3587
|
+
width: Math.round(width * ratio),
|
|
3588
|
+
height: Math.round(height * ratio)
|
|
3589
|
+
};
|
|
3590
|
+
}
|
|
3591
|
+
function canvasToBlob(img, width, height, format, quality) {
|
|
3592
|
+
return new Promise((resolve, reject) => {
|
|
3593
|
+
const canvas = document.createElement("canvas");
|
|
3594
|
+
canvas.width = width;
|
|
3595
|
+
canvas.height = height;
|
|
3596
|
+
const ctx = canvas.getContext("2d");
|
|
3597
|
+
if (!ctx) {
|
|
3598
|
+
reject(new Error("Failed to get canvas 2d context"));
|
|
3599
|
+
return;
|
|
3600
|
+
}
|
|
3601
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
3602
|
+
canvas.toBlob(
|
|
3603
|
+
(blob) => {
|
|
3604
|
+
if (!blob) {
|
|
3605
|
+
reject(new Error("Canvas toBlob returned null"));
|
|
3606
|
+
return;
|
|
3607
|
+
}
|
|
3608
|
+
resolve(blob);
|
|
3609
|
+
},
|
|
3610
|
+
format,
|
|
3611
|
+
quality
|
|
3612
|
+
);
|
|
3613
|
+
});
|
|
3614
|
+
}
|
|
3615
|
+
var webpSupported = null;
|
|
3616
|
+
function supportsWebP() {
|
|
3617
|
+
if (webpSupported !== null) return webpSupported;
|
|
3618
|
+
try {
|
|
3619
|
+
const canvas = document.createElement("canvas");
|
|
3620
|
+
canvas.width = 1;
|
|
3621
|
+
canvas.height = 1;
|
|
3622
|
+
webpSupported = canvas.toDataURL("image/webp").startsWith("data:image/webp");
|
|
3623
|
+
} catch {
|
|
3624
|
+
webpSupported = false;
|
|
3625
|
+
}
|
|
3626
|
+
return webpSupported;
|
|
3627
|
+
}
|
|
3628
|
+
function getOutputFormat(_originalType) {
|
|
3629
|
+
if (supportsWebP()) {
|
|
3630
|
+
return { mime: "image/webp", ext: ".webp" };
|
|
3631
|
+
}
|
|
3632
|
+
return { mime: "image/jpeg", ext: ".jpg" };
|
|
3633
|
+
}
|
|
3634
|
+
function replaceExtension(filename, newExt) {
|
|
3635
|
+
const dotIndex = filename.lastIndexOf(".");
|
|
3636
|
+
if (dotIndex === -1) return filename + newExt;
|
|
3637
|
+
return filename.substring(0, dotIndex) + newExt;
|
|
3638
|
+
}
|
|
3639
|
+
async function compressImage(file, targetSizeKB = DEFAULT_TARGET_SIZE_KB) {
|
|
3640
|
+
if (!file.type.startsWith("image/")) {
|
|
3641
|
+
return file;
|
|
3642
|
+
}
|
|
3643
|
+
if (file.type === "image/svg+xml") {
|
|
3644
|
+
return file;
|
|
3645
|
+
}
|
|
3646
|
+
if (file.type === "image/gif") {
|
|
3647
|
+
return file;
|
|
3648
|
+
}
|
|
3649
|
+
const targetBytes = targetSizeKB * 1024;
|
|
3650
|
+
if (file.size <= targetBytes) {
|
|
3651
|
+
return file;
|
|
3652
|
+
}
|
|
3653
|
+
const img = await loadImage(file);
|
|
3654
|
+
const { width, height } = getScaledDimensions(
|
|
3655
|
+
img.naturalWidth,
|
|
3656
|
+
img.naturalHeight,
|
|
3657
|
+
MAX_DIMENSION
|
|
3658
|
+
);
|
|
3659
|
+
const { mime, ext } = getOutputFormat(file.type);
|
|
3660
|
+
let quality = QUALITY_START;
|
|
3661
|
+
let blob = null;
|
|
3662
|
+
while (quality >= QUALITY_MIN) {
|
|
3663
|
+
blob = await canvasToBlob(img, width, height, mime, quality);
|
|
3664
|
+
if (blob.size <= targetBytes) {
|
|
3665
|
+
break;
|
|
3666
|
+
}
|
|
3667
|
+
quality -= QUALITY_STEP;
|
|
3668
|
+
}
|
|
3669
|
+
if (!blob) {
|
|
3670
|
+
blob = await canvasToBlob(img, width, height, mime, QUALITY_MIN);
|
|
3671
|
+
}
|
|
3672
|
+
if (blob.size >= file.size) {
|
|
3673
|
+
return file;
|
|
3674
|
+
}
|
|
3675
|
+
const newFilename = replaceExtension(file.name, ext);
|
|
3676
|
+
return new File([blob], newFilename, {
|
|
3677
|
+
type: mime,
|
|
3678
|
+
lastModified: Date.now()
|
|
3679
|
+
});
|
|
3680
|
+
}
|
|
3681
|
+
|
|
3682
|
+
// src/chat/hooks/useFileAttachments.ts
|
|
3683
|
+
function useFileAttachments(options) {
|
|
3684
|
+
const { onUpload, onConfirmUpload, onSubmitUpload, onError, maxFiles = 10, maxTotalSizeMB = 100 } = options;
|
|
3685
|
+
const [files, setFiles] = useState([]);
|
|
3686
|
+
const filesRef = useRef(files);
|
|
3687
|
+
filesRef.current = files;
|
|
3688
|
+
const onFilesChange = useCallback((newFiles) => {
|
|
3689
|
+
filesRef.current = newFiles;
|
|
3690
|
+
setFiles(newFiles);
|
|
3691
|
+
}, []);
|
|
3692
|
+
const removeFile = useCallback((uuid) => {
|
|
3693
|
+
setFiles((prev) => {
|
|
3694
|
+
const file = prev.find((f) => f.uuid === uuid);
|
|
3695
|
+
if (file?.previewUrl) {
|
|
3696
|
+
URL.revokeObjectURL(file.previewUrl);
|
|
3697
|
+
}
|
|
3698
|
+
return prev.filter((f) => f.uuid !== uuid);
|
|
3699
|
+
});
|
|
3700
|
+
}, []);
|
|
3701
|
+
const getReadyUuids = useCallback(() => {
|
|
3702
|
+
return filesRef.current.filter((f) => f.status === "ready").map((f) => f.uuid);
|
|
3703
|
+
}, []);
|
|
3704
|
+
const getReadyUrls = useCallback(() => {
|
|
3705
|
+
return filesRef.current.filter((f) => f.status === "ready").map((f) => f.uuid);
|
|
3706
|
+
}, []);
|
|
3707
|
+
const prepareForSubmit = useCallback(async () => {
|
|
3708
|
+
const readyFiles = filesRef.current.filter((f) => f.status === "ready");
|
|
3709
|
+
console.log("[useFileAttachments] prepareForSubmit - readyFiles:", readyFiles.length, "hasSubmitUpload:", !!onSubmitUpload);
|
|
3710
|
+
if (readyFiles.length === 0) return [];
|
|
3711
|
+
if (!onSubmitUpload) {
|
|
3712
|
+
return readyFiles.map((f) => f.uuid);
|
|
3713
|
+
}
|
|
3714
|
+
const aiUuids = [];
|
|
3715
|
+
for (const attachment of readyFiles) {
|
|
3716
|
+
try {
|
|
3717
|
+
let fileToSend = attachment.file;
|
|
3718
|
+
if (attachment.category === "image") {
|
|
3719
|
+
try {
|
|
3720
|
+
fileToSend = await compressImage(attachment.file);
|
|
3721
|
+
} catch {
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
console.log("[useFileAttachments] Uploading to AI service:", attachment.file.name, "localUuid:", attachment.uuid.substring(0, 40));
|
|
3725
|
+
const aiUuid = await onSubmitUpload(
|
|
3726
|
+
fileToSend,
|
|
3727
|
+
attachment.uuid,
|
|
3728
|
+
{ filename: fileToSend.name, mimeType: fileToSend.type, size: fileToSend.size }
|
|
3729
|
+
);
|
|
3730
|
+
console.log("[useFileAttachments] AI service returned UUID:", aiUuid);
|
|
3731
|
+
aiUuids.push(aiUuid);
|
|
3732
|
+
} catch (err) {
|
|
3733
|
+
const msg = err instanceof Error ? err.message : "Submit upload failed";
|
|
3734
|
+
console.error("[useFileAttachments] Submit upload failed for:", attachment.file.name, msg);
|
|
3735
|
+
onError?.(msg);
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
console.log("[useFileAttachments] prepareForSubmit result:", aiUuids);
|
|
3739
|
+
return aiUuids;
|
|
3740
|
+
}, [onSubmitUpload, onError]);
|
|
3741
|
+
const clear = useCallback(() => {
|
|
3742
|
+
for (const file of filesRef.current) {
|
|
3743
|
+
if (file.previewUrl) {
|
|
3744
|
+
URL.revokeObjectURL(file.previewUrl);
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
setFiles([]);
|
|
3748
|
+
}, []);
|
|
3749
|
+
const isUploading = files.some(
|
|
3750
|
+
(f) => f.status === "uploading" || f.status === "pending" || f.status === "processing"
|
|
3751
|
+
);
|
|
3752
|
+
const hasReadyFiles = files.some((f) => f.status === "ready");
|
|
3753
|
+
const dropZoneProps = {
|
|
3754
|
+
files,
|
|
3755
|
+
onFilesChange,
|
|
3756
|
+
onUpload,
|
|
3757
|
+
onConfirmUpload,
|
|
3758
|
+
onError,
|
|
3759
|
+
maxFiles,
|
|
3760
|
+
maxTotalSizeMB
|
|
3761
|
+
};
|
|
3762
|
+
return {
|
|
3763
|
+
files,
|
|
3764
|
+
dropZoneProps,
|
|
3765
|
+
removeFile,
|
|
3766
|
+
getReadyUuids,
|
|
3767
|
+
getReadyUrls,
|
|
3768
|
+
prepareForSubmit,
|
|
3769
|
+
clear,
|
|
3770
|
+
isUploading,
|
|
3771
|
+
hasReadyFiles,
|
|
3772
|
+
fileCount: files.length
|
|
3773
|
+
};
|
|
3774
|
+
}
|
|
3458
3775
|
function cn(...classes) {
|
|
3459
3776
|
return classes.filter(Boolean).join(" ");
|
|
3460
3777
|
}
|
|
@@ -4104,6 +4421,51 @@ function ChatContainer({
|
|
|
4104
4421
|
function cn3(...classes) {
|
|
4105
4422
|
return classes.filter(Boolean).join(" ");
|
|
4106
4423
|
}
|
|
4424
|
+
function formatFileSize(bytes) {
|
|
4425
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
4426
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
4427
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
4428
|
+
}
|
|
4429
|
+
function AttachmentChip({ attachment, onRemove }) {
|
|
4430
|
+
const isLoading = attachment.status === "uploading" || attachment.status === "pending" || attachment.status === "processing";
|
|
4431
|
+
const isError = attachment.status === "error" || attachment.status === "failed";
|
|
4432
|
+
return /* @__PURE__ */ jsxs(
|
|
4433
|
+
"div",
|
|
4434
|
+
{
|
|
4435
|
+
className: cn3(
|
|
4436
|
+
"inline-flex items-center gap-1.5 px-2 py-1 rounded-md text-xs max-w-[180px]",
|
|
4437
|
+
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"
|
|
4438
|
+
),
|
|
4439
|
+
children: [
|
|
4440
|
+
attachment.previewUrl && /* @__PURE__ */ jsx(
|
|
4441
|
+
"img",
|
|
4442
|
+
{
|
|
4443
|
+
src: attachment.previewUrl,
|
|
4444
|
+
alt: "",
|
|
4445
|
+
className: "w-5 h-5 rounded object-cover flex-shrink-0"
|
|
4446
|
+
}
|
|
4447
|
+
),
|
|
4448
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: attachment.file.name }),
|
|
4449
|
+
isLoading ? /* @__PURE__ */ jsx(LoadingSpinner, { className: "h-3 w-3 flex-shrink-0" }) : isError ? /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-red-500", title: attachment.error, children: "!" }) : /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-gray-400 dark:text-gray-500", children: formatFileSize(attachment.actualSize ?? attachment.file.size) }),
|
|
4450
|
+
/* @__PURE__ */ jsx(
|
|
4451
|
+
"button",
|
|
4452
|
+
{
|
|
4453
|
+
type: "button",
|
|
4454
|
+
onClick: (e) => {
|
|
4455
|
+
e.preventDefault();
|
|
4456
|
+
e.stopPropagation();
|
|
4457
|
+
onRemove(attachment.uuid);
|
|
4458
|
+
},
|
|
4459
|
+
className: "flex-shrink-0 p-0.5 rounded hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors",
|
|
4460
|
+
"aria-label": `Remove ${attachment.file.name}`,
|
|
4461
|
+
disabled: isLoading,
|
|
4462
|
+
children: /* @__PURE__ */ jsx(XIcon, { className: "h-3 w-3" })
|
|
4463
|
+
}
|
|
4464
|
+
)
|
|
4465
|
+
]
|
|
4466
|
+
}
|
|
4467
|
+
);
|
|
4468
|
+
}
|
|
4107
4469
|
function ChatInput({
|
|
4108
4470
|
onSend,
|
|
4109
4471
|
onStop,
|
|
@@ -4111,10 +4473,15 @@ function ChatInput({
|
|
|
4111
4473
|
disabled = false,
|
|
4112
4474
|
isLoading = false,
|
|
4113
4475
|
enableAttachments = false,
|
|
4476
|
+
attachments = [],
|
|
4477
|
+
onRemoveAttachment,
|
|
4478
|
+
dropZoneRef,
|
|
4479
|
+
isUploadingAttachments = false,
|
|
4114
4480
|
className
|
|
4115
4481
|
}) {
|
|
4116
4482
|
const [content, setContent] = useState("");
|
|
4117
4483
|
const textareaRef = useRef(null);
|
|
4484
|
+
const hasAttachments = enableAttachments && attachments.length > 0;
|
|
4118
4485
|
useEffect(() => {
|
|
4119
4486
|
const textarea = textareaRef.current;
|
|
4120
4487
|
if (textarea) {
|
|
@@ -4124,10 +4491,10 @@ function ChatInput({
|
|
|
4124
4491
|
}, [content]);
|
|
4125
4492
|
const handleSubmit = useCallback(async () => {
|
|
4126
4493
|
const trimmedContent = content.trim();
|
|
4127
|
-
if (!trimmedContent || disabled || isLoading) return;
|
|
4494
|
+
if (!trimmedContent && !hasAttachments || disabled || isLoading || isUploadingAttachments) return;
|
|
4128
4495
|
setContent("");
|
|
4129
4496
|
await onSend(trimmedContent);
|
|
4130
|
-
}, [content, disabled, isLoading, onSend]);
|
|
4497
|
+
}, [content, hasAttachments, disabled, isLoading, isUploadingAttachments, onSend]);
|
|
4131
4498
|
const handleKeyDown = useCallback(
|
|
4132
4499
|
(e) => {
|
|
4133
4500
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
@@ -4140,7 +4507,10 @@ function ChatInput({
|
|
|
4140
4507
|
const handleStopClick = useCallback(() => {
|
|
4141
4508
|
onStop?.();
|
|
4142
4509
|
}, [onStop]);
|
|
4143
|
-
const
|
|
4510
|
+
const handleAttachClick = useCallback(() => {
|
|
4511
|
+
dropZoneRef?.current?.openFileDialog();
|
|
4512
|
+
}, [dropZoneRef]);
|
|
4513
|
+
const canSend = (content.trim().length > 0 || hasAttachments) && !disabled && !isLoading && !isUploadingAttachments;
|
|
4144
4514
|
const showStopButton = isLoading && onStop;
|
|
4145
4515
|
return /* @__PURE__ */ jsxs(
|
|
4146
4516
|
"div",
|
|
@@ -4150,7 +4520,34 @@ function ChatInput({
|
|
|
4150
4520
|
className
|
|
4151
4521
|
),
|
|
4152
4522
|
children: [
|
|
4523
|
+
hasAttachments && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5 mb-2", children: attachments.map((attachment) => /* @__PURE__ */ jsx(
|
|
4524
|
+
AttachmentChip,
|
|
4525
|
+
{
|
|
4526
|
+
attachment,
|
|
4527
|
+
onRemove: onRemoveAttachment ?? (() => {
|
|
4528
|
+
})
|
|
4529
|
+
},
|
|
4530
|
+
attachment.uuid
|
|
4531
|
+
)) }),
|
|
4153
4532
|
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
|
|
4533
|
+
enableAttachments && /* @__PURE__ */ jsx(
|
|
4534
|
+
"button",
|
|
4535
|
+
{
|
|
4536
|
+
type: "button",
|
|
4537
|
+
onClick: handleAttachClick,
|
|
4538
|
+
disabled: disabled || isLoading,
|
|
4539
|
+
className: cn3(
|
|
4540
|
+
"flex-shrink-0 p-3 rounded-lg transition-colors",
|
|
4541
|
+
"text-gray-500 dark:text-gray-400",
|
|
4542
|
+
"hover:bg-gray-100 dark:hover:bg-gray-700",
|
|
4543
|
+
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
4544
|
+
"focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
|
4545
|
+
),
|
|
4546
|
+
"aria-label": "Attach files",
|
|
4547
|
+
title: "Attach files",
|
|
4548
|
+
children: /* @__PURE__ */ jsx(PaperclipIcon, { className: "h-5 w-5" })
|
|
4549
|
+
}
|
|
4550
|
+
),
|
|
4154
4551
|
/* @__PURE__ */ jsx("div", { className: "flex-1 relative", children: /* @__PURE__ */ jsx(
|
|
4155
4552
|
"textarea",
|
|
4156
4553
|
{
|
|
@@ -4158,7 +4555,7 @@ function ChatInput({
|
|
|
4158
4555
|
value: content,
|
|
4159
4556
|
onChange: (e) => setContent(e.target.value),
|
|
4160
4557
|
onKeyDown: handleKeyDown,
|
|
4161
|
-
placeholder,
|
|
4558
|
+
placeholder: hasAttachments && !content ? `${attachments.length} file${attachments.length > 1 ? "s" : ""} attached. Add a message or press Enter to send.` : placeholder,
|
|
4162
4559
|
disabled,
|
|
4163
4560
|
rows: 1,
|
|
4164
4561
|
className: cn3(
|
|
@@ -4208,9 +4605,664 @@ function ChatInput({
|
|
|
4208
4605
|
}
|
|
4209
4606
|
);
|
|
4210
4607
|
}
|
|
4608
|
+
function Upload({ className }) {
|
|
4609
|
+
return /* @__PURE__ */ 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: [
|
|
4610
|
+
/* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
4611
|
+
/* @__PURE__ */ jsx("polyline", { points: "17 8 12 3 7 8" }),
|
|
4612
|
+
/* @__PURE__ */ jsx("line", { x1: "12", x2: "12", y1: "3", y2: "15" })
|
|
4613
|
+
] });
|
|
4614
|
+
}
|
|
4615
|
+
function X({ className }) {
|
|
4616
|
+
return /* @__PURE__ */ 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: [
|
|
4617
|
+
/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
|
|
4618
|
+
/* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
|
|
4619
|
+
] });
|
|
4620
|
+
}
|
|
4621
|
+
function FileText({ className }) {
|
|
4622
|
+
return /* @__PURE__ */ 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: [
|
|
4623
|
+
/* @__PURE__ */ jsx("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
4624
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }),
|
|
4625
|
+
/* @__PURE__ */ jsx("line", { x1: "16", x2: "8", y1: "13", y2: "13" }),
|
|
4626
|
+
/* @__PURE__ */ jsx("line", { x1: "16", x2: "8", y1: "17", y2: "17" }),
|
|
4627
|
+
/* @__PURE__ */ jsx("line", { x1: "10", x2: "8", y1: "9", y2: "9" })
|
|
4628
|
+
] });
|
|
4629
|
+
}
|
|
4630
|
+
function ImageIcon2({ className }) {
|
|
4631
|
+
return /* @__PURE__ */ 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: [
|
|
4632
|
+
/* @__PURE__ */ jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
|
|
4633
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "2" }),
|
|
4634
|
+
/* @__PURE__ */ jsx("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
|
|
4635
|
+
] });
|
|
4636
|
+
}
|
|
4637
|
+
function Music({ className }) {
|
|
4638
|
+
return /* @__PURE__ */ 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: [
|
|
4639
|
+
/* @__PURE__ */ jsx("path", { d: "M9 18V5l12-2v13" }),
|
|
4640
|
+
/* @__PURE__ */ jsx("circle", { cx: "6", cy: "18", r: "3" }),
|
|
4641
|
+
/* @__PURE__ */ jsx("circle", { cx: "18", cy: "16", r: "3" })
|
|
4642
|
+
] });
|
|
4643
|
+
}
|
|
4644
|
+
function Loader2({ className }) {
|
|
4645
|
+
return /* @__PURE__ */ 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__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
|
|
4646
|
+
}
|
|
4647
|
+
function FileIcon2({ className }) {
|
|
4648
|
+
return /* @__PURE__ */ 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: [
|
|
4649
|
+
/* @__PURE__ */ jsx("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
4650
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
4651
|
+
] });
|
|
4652
|
+
}
|
|
4653
|
+
function RotateCw({ className }) {
|
|
4654
|
+
return /* @__PURE__ */ 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: [
|
|
4655
|
+
/* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" }),
|
|
4656
|
+
/* @__PURE__ */ jsx("path", { d: "M21 3v5h-5" })
|
|
4657
|
+
] });
|
|
4658
|
+
}
|
|
4211
4659
|
function cn4(...classes) {
|
|
4212
4660
|
return classes.filter(Boolean).join(" ");
|
|
4213
4661
|
}
|
|
4662
|
+
var SUPPORTED_TYPES = {
|
|
4663
|
+
document: {
|
|
4664
|
+
mimeTypes: [
|
|
4665
|
+
"application/pdf",
|
|
4666
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
4667
|
+
"application/msword",
|
|
4668
|
+
"text/plain",
|
|
4669
|
+
"text/markdown",
|
|
4670
|
+
"text/csv",
|
|
4671
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
4672
|
+
"application/vnd.ms-excel"
|
|
4673
|
+
],
|
|
4674
|
+
extensions: [
|
|
4675
|
+
".pdf",
|
|
4676
|
+
".docx",
|
|
4677
|
+
".doc",
|
|
4678
|
+
".txt",
|
|
4679
|
+
".md",
|
|
4680
|
+
".csv",
|
|
4681
|
+
".xlsx",
|
|
4682
|
+
".xls"
|
|
4683
|
+
],
|
|
4684
|
+
maxSizeMB: 25,
|
|
4685
|
+
label: "Documents"
|
|
4686
|
+
},
|
|
4687
|
+
image: {
|
|
4688
|
+
mimeTypes: [
|
|
4689
|
+
"image/png",
|
|
4690
|
+
"image/jpeg",
|
|
4691
|
+
"image/gif",
|
|
4692
|
+
"image/webp",
|
|
4693
|
+
"image/svg+xml"
|
|
4694
|
+
],
|
|
4695
|
+
extensions: [".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"],
|
|
4696
|
+
maxSizeMB: 10,
|
|
4697
|
+
label: "Images"
|
|
4698
|
+
},
|
|
4699
|
+
audio: {
|
|
4700
|
+
mimeTypes: [
|
|
4701
|
+
"audio/mpeg",
|
|
4702
|
+
"audio/wav",
|
|
4703
|
+
"audio/mp4",
|
|
4704
|
+
"audio/ogg",
|
|
4705
|
+
"audio/webm"
|
|
4706
|
+
],
|
|
4707
|
+
extensions: [".mp3", ".wav", ".m4a", ".ogg", ".webm"],
|
|
4708
|
+
maxSizeMB: 50,
|
|
4709
|
+
label: "Audio"
|
|
4710
|
+
}
|
|
4711
|
+
};
|
|
4712
|
+
var MAX_FILES_PER_DROP = 10;
|
|
4713
|
+
var MAX_TOTAL_SIZE_MB = 100;
|
|
4714
|
+
function categorizeFile(mimeType) {
|
|
4715
|
+
for (const [category, config] of Object.entries(SUPPORTED_TYPES)) {
|
|
4716
|
+
if (config.mimeTypes.includes(mimeType)) {
|
|
4717
|
+
return category;
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4720
|
+
return null;
|
|
4721
|
+
}
|
|
4722
|
+
function validateFile(file) {
|
|
4723
|
+
const category = categorizeFile(file.type);
|
|
4724
|
+
if (!category) {
|
|
4725
|
+
return {
|
|
4726
|
+
valid: false,
|
|
4727
|
+
error: `Unsupported file type: ${file.type || file.name.split(".").pop()}`
|
|
4728
|
+
};
|
|
4729
|
+
}
|
|
4730
|
+
const config = SUPPORTED_TYPES[category];
|
|
4731
|
+
const maxSizeBytes = config.maxSizeMB * 1024 * 1024;
|
|
4732
|
+
if (file.size > maxSizeBytes) {
|
|
4733
|
+
return {
|
|
4734
|
+
valid: false,
|
|
4735
|
+
error: `${file.name} exceeds ${config.maxSizeMB}MB limit for ${config.label.toLowerCase()}`
|
|
4736
|
+
};
|
|
4737
|
+
}
|
|
4738
|
+
return { valid: true, category };
|
|
4739
|
+
}
|
|
4740
|
+
function formatFileSize2(bytes) {
|
|
4741
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
4742
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
4743
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
4744
|
+
}
|
|
4745
|
+
function getCategoryIcon(category) {
|
|
4746
|
+
switch (category) {
|
|
4747
|
+
case "document":
|
|
4748
|
+
return FileText;
|
|
4749
|
+
case "image":
|
|
4750
|
+
return ImageIcon2;
|
|
4751
|
+
case "audio":
|
|
4752
|
+
return Music;
|
|
4753
|
+
default:
|
|
4754
|
+
return FileIcon2;
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
var FileDropZone = forwardRef(
|
|
4758
|
+
function FileDropZone2({
|
|
4759
|
+
onFilesChange,
|
|
4760
|
+
files,
|
|
4761
|
+
onUpload,
|
|
4762
|
+
onConfirmUpload,
|
|
4763
|
+
onError,
|
|
4764
|
+
maxFiles = MAX_FILES_PER_DROP,
|
|
4765
|
+
maxTotalSizeMB = MAX_TOTAL_SIZE_MB,
|
|
4766
|
+
disabled = false,
|
|
4767
|
+
className,
|
|
4768
|
+
accept,
|
|
4769
|
+
compact = false,
|
|
4770
|
+
children
|
|
4771
|
+
}, ref) {
|
|
4772
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
4773
|
+
const fileInputRef = useRef(null);
|
|
4774
|
+
const dropZoneRef = useRef(null);
|
|
4775
|
+
const dragCounter = useRef(0);
|
|
4776
|
+
useImperativeHandle(
|
|
4777
|
+
ref,
|
|
4778
|
+
() => ({
|
|
4779
|
+
openFileDialog: () => {
|
|
4780
|
+
if (!disabled) {
|
|
4781
|
+
fileInputRef.current?.click();
|
|
4782
|
+
}
|
|
4783
|
+
}
|
|
4784
|
+
}),
|
|
4785
|
+
[disabled]
|
|
4786
|
+
);
|
|
4787
|
+
const currentTotalSize = files.reduce((sum, f) => sum + f.file.size, 0);
|
|
4788
|
+
const uploadFile = useCallback(
|
|
4789
|
+
async (file, _tempId) => {
|
|
4790
|
+
const result = await onUpload(file, {
|
|
4791
|
+
filename: file.name,
|
|
4792
|
+
mimeType: file.type,
|
|
4793
|
+
size: file.size
|
|
4794
|
+
});
|
|
4795
|
+
if (result.uploadUrl) {
|
|
4796
|
+
const uploadResponse = await fetch(result.uploadUrl, {
|
|
4797
|
+
method: "PUT",
|
|
4798
|
+
body: file,
|
|
4799
|
+
headers: { "Content-Type": file.type }
|
|
4800
|
+
});
|
|
4801
|
+
if (!uploadResponse.ok) {
|
|
4802
|
+
throw new Error(`Upload failed: ${uploadResponse.statusText}`);
|
|
4803
|
+
}
|
|
4804
|
+
}
|
|
4805
|
+
if (onConfirmUpload) {
|
|
4806
|
+
await onConfirmUpload(result.uuid);
|
|
4807
|
+
}
|
|
4808
|
+
return result.uuid;
|
|
4809
|
+
},
|
|
4810
|
+
[onUpload, onConfirmUpload]
|
|
4811
|
+
);
|
|
4812
|
+
const handleFiles = useCallback(
|
|
4813
|
+
async (selectedFiles) => {
|
|
4814
|
+
if (disabled) return;
|
|
4815
|
+
if (files.length + selectedFiles.length > maxFiles) {
|
|
4816
|
+
onError?.(`Maximum ${maxFiles} files allowed`);
|
|
4817
|
+
return;
|
|
4818
|
+
}
|
|
4819
|
+
const validFiles = [];
|
|
4820
|
+
let newTotalSize = currentTotalSize;
|
|
4821
|
+
for (const file of selectedFiles) {
|
|
4822
|
+
const validation = validateFile(file);
|
|
4823
|
+
if (!validation.valid) {
|
|
4824
|
+
onError?.(validation.error);
|
|
4825
|
+
continue;
|
|
4826
|
+
}
|
|
4827
|
+
newTotalSize += file.size;
|
|
4828
|
+
if (newTotalSize > maxTotalSizeMB * 1024 * 1024) {
|
|
4829
|
+
onError?.(`Total upload size exceeds ${maxTotalSizeMB}MB`);
|
|
4830
|
+
break;
|
|
4831
|
+
}
|
|
4832
|
+
validFiles.push({ file, category: validation.category });
|
|
4833
|
+
}
|
|
4834
|
+
if (validFiles.length === 0) return;
|
|
4835
|
+
const processedFiles = [];
|
|
4836
|
+
for (const entry of validFiles) {
|
|
4837
|
+
if (entry.category === "image") {
|
|
4838
|
+
try {
|
|
4839
|
+
const compressed = await compressImage(entry.file);
|
|
4840
|
+
processedFiles.push({ file: compressed, category: entry.category });
|
|
4841
|
+
} catch (err) {
|
|
4842
|
+
console.warn("Image compression failed, using original:", err);
|
|
4843
|
+
processedFiles.push(entry);
|
|
4844
|
+
}
|
|
4845
|
+
} else {
|
|
4846
|
+
processedFiles.push(entry);
|
|
4847
|
+
}
|
|
4848
|
+
}
|
|
4849
|
+
const newAttachments = processedFiles.map(
|
|
4850
|
+
({ file, category }) => {
|
|
4851
|
+
const tempId = `temp-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
4852
|
+
return {
|
|
4853
|
+
uuid: tempId,
|
|
4854
|
+
file,
|
|
4855
|
+
status: "pending",
|
|
4856
|
+
progress: 0,
|
|
4857
|
+
category,
|
|
4858
|
+
previewUrl: category === "image" ? URL.createObjectURL(file) : void 0
|
|
4859
|
+
};
|
|
4860
|
+
}
|
|
4861
|
+
);
|
|
4862
|
+
let currentFiles = [...files, ...newAttachments];
|
|
4863
|
+
onFilesChange(currentFiles);
|
|
4864
|
+
for (const attachment of newAttachments) {
|
|
4865
|
+
try {
|
|
4866
|
+
currentFiles = currentFiles.map(
|
|
4867
|
+
(f) => f.uuid === attachment.uuid ? { ...f, status: "uploading", progress: 10 } : f
|
|
4868
|
+
);
|
|
4869
|
+
onFilesChange(currentFiles);
|
|
4870
|
+
const realUuid = await uploadFile(attachment.file, attachment.uuid);
|
|
4871
|
+
currentFiles = currentFiles.map(
|
|
4872
|
+
(f) => f.uuid === attachment.uuid ? {
|
|
4873
|
+
...f,
|
|
4874
|
+
uuid: realUuid,
|
|
4875
|
+
status: "ready",
|
|
4876
|
+
progress: 100,
|
|
4877
|
+
hasThumbnail: f.category === "image"
|
|
4878
|
+
} : f
|
|
4879
|
+
);
|
|
4880
|
+
onFilesChange(currentFiles);
|
|
4881
|
+
} catch (error) {
|
|
4882
|
+
console.error("Upload error:", error);
|
|
4883
|
+
currentFiles = currentFiles.map(
|
|
4884
|
+
(f) => f.uuid === attachment.uuid ? {
|
|
4885
|
+
...f,
|
|
4886
|
+
status: "error",
|
|
4887
|
+
error: error instanceof Error ? error.message : "Upload failed"
|
|
4888
|
+
} : f
|
|
4889
|
+
);
|
|
4890
|
+
onFilesChange(currentFiles);
|
|
4891
|
+
onError?.(`Failed to upload ${attachment.file.name}`);
|
|
4892
|
+
}
|
|
4893
|
+
}
|
|
4894
|
+
},
|
|
4895
|
+
[
|
|
4896
|
+
disabled,
|
|
4897
|
+
files,
|
|
4898
|
+
maxFiles,
|
|
4899
|
+
currentTotalSize,
|
|
4900
|
+
maxTotalSizeMB,
|
|
4901
|
+
onFilesChange,
|
|
4902
|
+
uploadFile,
|
|
4903
|
+
onError
|
|
4904
|
+
]
|
|
4905
|
+
);
|
|
4906
|
+
const handleRemove = useCallback(
|
|
4907
|
+
(uuid) => {
|
|
4908
|
+
const file = files.find((f) => f.uuid === uuid);
|
|
4909
|
+
if (file?.previewUrl) {
|
|
4910
|
+
URL.revokeObjectURL(file.previewUrl);
|
|
4911
|
+
}
|
|
4912
|
+
onFilesChange(files.filter((f) => f.uuid !== uuid));
|
|
4913
|
+
},
|
|
4914
|
+
[files, onFilesChange]
|
|
4915
|
+
);
|
|
4916
|
+
const handleRetry = useCallback(
|
|
4917
|
+
async (uuid) => {
|
|
4918
|
+
const attachment = files.find((f) => f.uuid === uuid);
|
|
4919
|
+
if (!attachment || attachment.status !== "error") return;
|
|
4920
|
+
let currentFiles = files.map(
|
|
4921
|
+
(f) => f.uuid === uuid ? { ...f, status: "pending", progress: 0, error: void 0 } : f
|
|
4922
|
+
);
|
|
4923
|
+
onFilesChange(currentFiles);
|
|
4924
|
+
try {
|
|
4925
|
+
currentFiles = currentFiles.map(
|
|
4926
|
+
(f) => f.uuid === uuid ? { ...f, status: "uploading", progress: 10 } : f
|
|
4927
|
+
);
|
|
4928
|
+
onFilesChange(currentFiles);
|
|
4929
|
+
const realUuid = await uploadFile(attachment.file, uuid);
|
|
4930
|
+
currentFiles = currentFiles.map(
|
|
4931
|
+
(f) => f.uuid === uuid ? {
|
|
4932
|
+
...f,
|
|
4933
|
+
uuid: realUuid,
|
|
4934
|
+
status: "ready",
|
|
4935
|
+
progress: 100,
|
|
4936
|
+
hasThumbnail: f.category === "image"
|
|
4937
|
+
} : f
|
|
4938
|
+
);
|
|
4939
|
+
onFilesChange(currentFiles);
|
|
4940
|
+
} catch (error) {
|
|
4941
|
+
console.error("Retry upload error:", error);
|
|
4942
|
+
currentFiles = currentFiles.map(
|
|
4943
|
+
(f) => f.uuid === uuid ? {
|
|
4944
|
+
...f,
|
|
4945
|
+
status: "error",
|
|
4946
|
+
error: error instanceof Error ? error.message : "Upload failed"
|
|
4947
|
+
} : f
|
|
4948
|
+
);
|
|
4949
|
+
onFilesChange(currentFiles);
|
|
4950
|
+
onError?.(`Failed to upload ${attachment.file.name}`);
|
|
4951
|
+
}
|
|
4952
|
+
},
|
|
4953
|
+
[files, onFilesChange, uploadFile, onError]
|
|
4954
|
+
);
|
|
4955
|
+
const hasDraggedFiles = useCallback((e) => {
|
|
4956
|
+
if (e.dataTransfer.types) {
|
|
4957
|
+
for (const type of e.dataTransfer.types) {
|
|
4958
|
+
if (type === "Files") return true;
|
|
4959
|
+
}
|
|
4960
|
+
}
|
|
4961
|
+
return false;
|
|
4962
|
+
}, []);
|
|
4963
|
+
const handleDragEnter = useCallback(
|
|
4964
|
+
(e) => {
|
|
4965
|
+
e.preventDefault();
|
|
4966
|
+
e.stopPropagation();
|
|
4967
|
+
if (!hasDraggedFiles(e)) return;
|
|
4968
|
+
dragCounter.current++;
|
|
4969
|
+
if (!disabled && dragCounter.current === 1) {
|
|
4970
|
+
setIsDragging(true);
|
|
4971
|
+
}
|
|
4972
|
+
},
|
|
4973
|
+
[disabled, hasDraggedFiles]
|
|
4974
|
+
);
|
|
4975
|
+
const handleDragOver = useCallback(
|
|
4976
|
+
(e) => {
|
|
4977
|
+
e.preventDefault();
|
|
4978
|
+
e.stopPropagation();
|
|
4979
|
+
if (hasDraggedFiles(e)) {
|
|
4980
|
+
e.dataTransfer.dropEffect = disabled ? "none" : "copy";
|
|
4981
|
+
}
|
|
4982
|
+
},
|
|
4983
|
+
[disabled, hasDraggedFiles]
|
|
4984
|
+
);
|
|
4985
|
+
const handleDragLeave = useCallback((e) => {
|
|
4986
|
+
e.preventDefault();
|
|
4987
|
+
e.stopPropagation();
|
|
4988
|
+
if (dragCounter.current > 0) {
|
|
4989
|
+
dragCounter.current--;
|
|
4990
|
+
if (dragCounter.current === 0) {
|
|
4991
|
+
setIsDragging(false);
|
|
4992
|
+
}
|
|
4993
|
+
}
|
|
4994
|
+
}, []);
|
|
4995
|
+
const handleDrop = useCallback(
|
|
4996
|
+
(e) => {
|
|
4997
|
+
e.preventDefault();
|
|
4998
|
+
e.stopPropagation();
|
|
4999
|
+
dragCounter.current = 0;
|
|
5000
|
+
setIsDragging(false);
|
|
5001
|
+
if (disabled) return;
|
|
5002
|
+
const droppedFiles = Array.from(e.dataTransfer.files);
|
|
5003
|
+
if (droppedFiles.length > 0) {
|
|
5004
|
+
handleFiles(droppedFiles);
|
|
5005
|
+
}
|
|
5006
|
+
},
|
|
5007
|
+
[handleFiles, disabled]
|
|
5008
|
+
);
|
|
5009
|
+
const handleFileSelect = useCallback(
|
|
5010
|
+
(e) => {
|
|
5011
|
+
const selectedFiles = e.target.files;
|
|
5012
|
+
if (selectedFiles) {
|
|
5013
|
+
handleFiles(Array.from(selectedFiles));
|
|
5014
|
+
}
|
|
5015
|
+
if (fileInputRef.current) {
|
|
5016
|
+
fileInputRef.current.value = "";
|
|
5017
|
+
}
|
|
5018
|
+
},
|
|
5019
|
+
[handleFiles]
|
|
5020
|
+
);
|
|
5021
|
+
const handleClick = useCallback(() => {
|
|
5022
|
+
if (!disabled) {
|
|
5023
|
+
fileInputRef.current?.click();
|
|
5024
|
+
}
|
|
5025
|
+
}, [disabled]);
|
|
5026
|
+
const checkDataTransferHasFiles = useCallback(
|
|
5027
|
+
(dataTransfer) => {
|
|
5028
|
+
if (!dataTransfer?.types) return false;
|
|
5029
|
+
for (let i = 0; i < dataTransfer.types.length; i++) {
|
|
5030
|
+
if (dataTransfer.types[i] === "Files") return true;
|
|
5031
|
+
}
|
|
5032
|
+
return false;
|
|
5033
|
+
},
|
|
5034
|
+
[]
|
|
5035
|
+
);
|
|
5036
|
+
useEffect(() => {
|
|
5037
|
+
if (!children || !dropZoneRef.current) return;
|
|
5038
|
+
const element = dropZoneRef.current;
|
|
5039
|
+
let enterCounter = 0;
|
|
5040
|
+
const handleDragEnterNative = (e) => {
|
|
5041
|
+
e.preventDefault();
|
|
5042
|
+
e.stopPropagation();
|
|
5043
|
+
if (!checkDataTransferHasFiles(e.dataTransfer)) return;
|
|
5044
|
+
enterCounter++;
|
|
5045
|
+
if (enterCounter === 1 && !disabled) {
|
|
5046
|
+
setIsDragging(true);
|
|
5047
|
+
}
|
|
5048
|
+
};
|
|
5049
|
+
const handleDragLeaveNative = (e) => {
|
|
5050
|
+
e.preventDefault();
|
|
5051
|
+
e.stopPropagation();
|
|
5052
|
+
if (!checkDataTransferHasFiles(e.dataTransfer)) return;
|
|
5053
|
+
enterCounter--;
|
|
5054
|
+
if (enterCounter <= 0) {
|
|
5055
|
+
enterCounter = 0;
|
|
5056
|
+
setIsDragging(false);
|
|
5057
|
+
}
|
|
5058
|
+
};
|
|
5059
|
+
const handleDragOverNative = (e) => {
|
|
5060
|
+
e.preventDefault();
|
|
5061
|
+
e.stopPropagation();
|
|
5062
|
+
if (checkDataTransferHasFiles(e.dataTransfer) && !disabled && e.dataTransfer) {
|
|
5063
|
+
e.dataTransfer.dropEffect = "copy";
|
|
5064
|
+
}
|
|
5065
|
+
};
|
|
5066
|
+
const handleDropNative = (e) => {
|
|
5067
|
+
e.preventDefault();
|
|
5068
|
+
e.stopPropagation();
|
|
5069
|
+
enterCounter = 0;
|
|
5070
|
+
setIsDragging(false);
|
|
5071
|
+
if (disabled) return;
|
|
5072
|
+
const droppedFiles = e.dataTransfer?.files;
|
|
5073
|
+
if (droppedFiles && droppedFiles.length > 0) {
|
|
5074
|
+
handleFiles(Array.from(droppedFiles));
|
|
5075
|
+
}
|
|
5076
|
+
};
|
|
5077
|
+
element.addEventListener("dragenter", handleDragEnterNative, true);
|
|
5078
|
+
element.addEventListener("dragleave", handleDragLeaveNative, true);
|
|
5079
|
+
element.addEventListener("dragover", handleDragOverNative, true);
|
|
5080
|
+
element.addEventListener("drop", handleDropNative, true);
|
|
5081
|
+
return () => {
|
|
5082
|
+
element.removeEventListener("dragenter", handleDragEnterNative, true);
|
|
5083
|
+
element.removeEventListener("dragleave", handleDragLeaveNative, true);
|
|
5084
|
+
element.removeEventListener("dragover", handleDragOverNative, true);
|
|
5085
|
+
element.removeEventListener("drop", handleDropNative, true);
|
|
5086
|
+
};
|
|
5087
|
+
}, [children, disabled, handleFiles, checkDataTransferHasFiles]);
|
|
5088
|
+
useEffect(() => {
|
|
5089
|
+
if (disabled) {
|
|
5090
|
+
dragCounter.current = 0;
|
|
5091
|
+
setIsDragging(false);
|
|
5092
|
+
}
|
|
5093
|
+
}, [disabled]);
|
|
5094
|
+
const acceptTypes = accept || Object.values(SUPPORTED_TYPES).flatMap((t) => [...t.mimeTypes, ...t.extensions]).join(",");
|
|
5095
|
+
const hasFiles = files.length > 0;
|
|
5096
|
+
const isUploading = files.some((f) => f.status === "uploading");
|
|
5097
|
+
if (children) {
|
|
5098
|
+
return /* @__PURE__ */ jsxs(
|
|
5099
|
+
"div",
|
|
5100
|
+
{
|
|
5101
|
+
ref: dropZoneRef,
|
|
5102
|
+
"data-dropzone": "wrapper",
|
|
5103
|
+
className: cn4("relative w-full h-full", className),
|
|
5104
|
+
style: { minHeight: "inherit" },
|
|
5105
|
+
children: [
|
|
5106
|
+
/* @__PURE__ */ jsx(
|
|
5107
|
+
"input",
|
|
5108
|
+
{
|
|
5109
|
+
ref: fileInputRef,
|
|
5110
|
+
type: "file",
|
|
5111
|
+
multiple: true,
|
|
5112
|
+
accept: acceptTypes,
|
|
5113
|
+
onChange: handleFileSelect,
|
|
5114
|
+
className: "hidden",
|
|
5115
|
+
disabled
|
|
5116
|
+
}
|
|
5117
|
+
),
|
|
5118
|
+
children,
|
|
5119
|
+
isDragging && !disabled && /* @__PURE__ */ jsx(
|
|
5120
|
+
"div",
|
|
5121
|
+
{
|
|
5122
|
+
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",
|
|
5123
|
+
style: { pointerEvents: "none" },
|
|
5124
|
+
children: /* @__PURE__ */ jsxs("div", { className: "bg-background/80 rounded-xl p-8 flex flex-col items-center shadow-lg", children: [
|
|
5125
|
+
/* @__PURE__ */ jsx(Upload, { className: "h-12 w-12 text-primary mb-4 animate-bounce" }),
|
|
5126
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-medium text-primary", children: "Drop files here" }),
|
|
5127
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-2", children: "Documents, images, or audio files" })
|
|
5128
|
+
] })
|
|
5129
|
+
}
|
|
5130
|
+
)
|
|
5131
|
+
]
|
|
5132
|
+
}
|
|
5133
|
+
);
|
|
5134
|
+
}
|
|
5135
|
+
return /* @__PURE__ */ jsxs("div", { className: cn4("w-full", className), children: [
|
|
5136
|
+
/* @__PURE__ */ jsx(
|
|
5137
|
+
"input",
|
|
5138
|
+
{
|
|
5139
|
+
ref: fileInputRef,
|
|
5140
|
+
type: "file",
|
|
5141
|
+
multiple: true,
|
|
5142
|
+
accept: acceptTypes,
|
|
5143
|
+
onChange: handleFileSelect,
|
|
5144
|
+
className: "hidden",
|
|
5145
|
+
disabled
|
|
5146
|
+
}
|
|
5147
|
+
),
|
|
5148
|
+
/* @__PURE__ */ jsxs(
|
|
5149
|
+
"div",
|
|
5150
|
+
{
|
|
5151
|
+
onDragEnter: handleDragEnter,
|
|
5152
|
+
onDragOver: handleDragOver,
|
|
5153
|
+
onDragLeave: handleDragLeave,
|
|
5154
|
+
onDrop: handleDrop,
|
|
5155
|
+
onClick: handleClick,
|
|
5156
|
+
className: cn4(
|
|
5157
|
+
"relative rounded-lg border-2 border-dashed transition-all cursor-pointer",
|
|
5158
|
+
"flex flex-col items-center justify-center",
|
|
5159
|
+
compact ? "p-2 min-h-[60px]" : "p-4 min-h-[100px]",
|
|
5160
|
+
isDragging && !disabled ? "border-primary bg-primary/5" : "border-muted-foreground/25",
|
|
5161
|
+
disabled ? "opacity-50 cursor-not-allowed" : "hover:border-primary/50 hover:bg-muted/25"
|
|
5162
|
+
),
|
|
5163
|
+
children: [
|
|
5164
|
+
isUploading ? /* @__PURE__ */ jsx(
|
|
5165
|
+
Loader2,
|
|
5166
|
+
{
|
|
5167
|
+
className: cn4(
|
|
5168
|
+
"animate-spin text-muted-foreground",
|
|
5169
|
+
compact ? "h-5 w-5 mb-1" : "h-8 w-8 mb-2"
|
|
5170
|
+
)
|
|
5171
|
+
}
|
|
5172
|
+
) : /* @__PURE__ */ jsx(
|
|
5173
|
+
Upload,
|
|
5174
|
+
{
|
|
5175
|
+
className: cn4(
|
|
5176
|
+
"text-muted-foreground",
|
|
5177
|
+
compact ? "h-5 w-5 mb-1" : "h-8 w-8 mb-2"
|
|
5178
|
+
)
|
|
5179
|
+
}
|
|
5180
|
+
),
|
|
5181
|
+
/* @__PURE__ */ jsx(
|
|
5182
|
+
"p",
|
|
5183
|
+
{
|
|
5184
|
+
className: cn4(
|
|
5185
|
+
"font-medium text-center text-muted-foreground",
|
|
5186
|
+
compact ? "text-xs" : "text-sm"
|
|
5187
|
+
),
|
|
5188
|
+
children: isDragging ? "Drop files here" : isUploading ? "Uploading..." : "Click to upload or drag and drop"
|
|
5189
|
+
}
|
|
5190
|
+
),
|
|
5191
|
+
!compact && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground/70 text-center mt-1", children: "Documents, images, or audio files" })
|
|
5192
|
+
]
|
|
5193
|
+
}
|
|
5194
|
+
),
|
|
5195
|
+
hasFiles && /* @__PURE__ */ jsx("div", { className: cn4("space-y-2", compact ? "mt-2" : "mt-3"), children: files.map((attachment) => {
|
|
5196
|
+
const Icon = getCategoryIcon(attachment.category);
|
|
5197
|
+
const isError = attachment.status === "error";
|
|
5198
|
+
const isLoading = attachment.status === "uploading" || attachment.status === "processing";
|
|
5199
|
+
return /* @__PURE__ */ jsxs(
|
|
5200
|
+
"div",
|
|
5201
|
+
{
|
|
5202
|
+
className: cn4(
|
|
5203
|
+
"flex items-center gap-3 p-2 rounded-lg border",
|
|
5204
|
+
isError ? "border-destructive/50 bg-destructive/5" : "border-border bg-muted/25"
|
|
5205
|
+
),
|
|
5206
|
+
children: [
|
|
5207
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex-shrink-0 w-10 h-10 rounded overflow-hidden bg-muted flex items-center justify-center", children: [
|
|
5208
|
+
attachment.previewUrl ? /* @__PURE__ */ jsx(
|
|
5209
|
+
"img",
|
|
5210
|
+
{
|
|
5211
|
+
src: attachment.previewUrl,
|
|
5212
|
+
alt: attachment.file.name,
|
|
5213
|
+
className: "w-full h-full object-cover"
|
|
5214
|
+
}
|
|
5215
|
+
) : /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5 text-muted-foreground" }),
|
|
5216
|
+
isLoading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-background/80 flex items-center justify-center", children: /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) })
|
|
5217
|
+
] }),
|
|
5218
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
5219
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: attachment.file.name }),
|
|
5220
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
5221
|
+
formatFileSize2(attachment.file.size),
|
|
5222
|
+
isError && attachment.error && /* @__PURE__ */ jsx("span", { className: "text-destructive ml-2", children: attachment.error })
|
|
5223
|
+
] })
|
|
5224
|
+
] }),
|
|
5225
|
+
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: isLoading ? /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
5226
|
+
attachment.progress,
|
|
5227
|
+
"%"
|
|
5228
|
+
] }) : attachment.status === "ready" ? /* @__PURE__ */ jsx("span", { className: "text-xs text-green-600", children: "Ready" }) : null }),
|
|
5229
|
+
isError && /* @__PURE__ */ jsx(
|
|
5230
|
+
"button",
|
|
5231
|
+
{
|
|
5232
|
+
type: "button",
|
|
5233
|
+
className: "flex-shrink-0 h-8 w-8 inline-flex items-center justify-center rounded-md text-primary hover:bg-muted transition-colors",
|
|
5234
|
+
onClick: (e) => {
|
|
5235
|
+
e.stopPropagation();
|
|
5236
|
+
handleRetry(attachment.uuid);
|
|
5237
|
+
},
|
|
5238
|
+
title: "Retry upload",
|
|
5239
|
+
children: /* @__PURE__ */ jsx(RotateCw, { className: "h-4 w-4" })
|
|
5240
|
+
}
|
|
5241
|
+
),
|
|
5242
|
+
/* @__PURE__ */ jsx(
|
|
5243
|
+
"button",
|
|
5244
|
+
{
|
|
5245
|
+
type: "button",
|
|
5246
|
+
className: "flex-shrink-0 h-8 w-8 inline-flex items-center justify-center rounded-md hover:bg-muted transition-colors disabled:opacity-50",
|
|
5247
|
+
onClick: (e) => {
|
|
5248
|
+
e.stopPropagation();
|
|
5249
|
+
handleRemove(attachment.uuid);
|
|
5250
|
+
},
|
|
5251
|
+
disabled: isLoading,
|
|
5252
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
|
|
5253
|
+
}
|
|
5254
|
+
)
|
|
5255
|
+
]
|
|
5256
|
+
},
|
|
5257
|
+
attachment.uuid
|
|
5258
|
+
);
|
|
5259
|
+
}) })
|
|
5260
|
+
] });
|
|
5261
|
+
}
|
|
5262
|
+
);
|
|
5263
|
+
function cn5(...classes) {
|
|
5264
|
+
return classes.filter(Boolean).join(" ");
|
|
5265
|
+
}
|
|
4214
5266
|
function AskUserPrompt({
|
|
4215
5267
|
question,
|
|
4216
5268
|
options,
|
|
@@ -4244,7 +5296,7 @@ function AskUserPrompt({
|
|
|
4244
5296
|
return /* @__PURE__ */ jsxs(
|
|
4245
5297
|
"div",
|
|
4246
5298
|
{
|
|
4247
|
-
className:
|
|
5299
|
+
className: cn5(
|
|
4248
5300
|
"rounded-lg border p-4 space-y-3",
|
|
4249
5301
|
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",
|
|
4250
5302
|
className
|
|
@@ -4256,7 +5308,7 @@ function AskUserPrompt({
|
|
|
4256
5308
|
/* @__PURE__ */ jsx(
|
|
4257
5309
|
"span",
|
|
4258
5310
|
{
|
|
4259
|
-
className:
|
|
5311
|
+
className: cn5(
|
|
4260
5312
|
"ml-auto text-xs px-2 py-1 rounded-full font-medium",
|
|
4261
5313
|
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"
|
|
4262
5314
|
),
|
|
@@ -4271,7 +5323,7 @@ function AskUserPrompt({
|
|
|
4271
5323
|
type: "button",
|
|
4272
5324
|
onClick: () => handleOptionSelect(option),
|
|
4273
5325
|
disabled,
|
|
4274
|
-
className:
|
|
5326
|
+
className: cn5(
|
|
4275
5327
|
"px-3 py-1.5 text-sm rounded-md border transition-all",
|
|
4276
5328
|
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",
|
|
4277
5329
|
disabled && "opacity-50 cursor-not-allowed"
|
|
@@ -4291,7 +5343,7 @@ function AskUserPrompt({
|
|
|
4291
5343
|
},
|
|
4292
5344
|
onKeyDown: handleKeyDown,
|
|
4293
5345
|
placeholder: options?.length ? "Or type a custom response..." : "Type your response...",
|
|
4294
|
-
className:
|
|
5346
|
+
className: cn5(
|
|
4295
5347
|
"flex-1 min-h-[60px] px-3 py-2 text-sm resize-none rounded-md border",
|
|
4296
5348
|
"border-gray-300 dark:border-gray-600",
|
|
4297
5349
|
"bg-white dark:bg-gray-800",
|
|
@@ -4309,7 +5361,7 @@ function AskUserPrompt({
|
|
|
4309
5361
|
type: "button",
|
|
4310
5362
|
onClick: handleSubmit,
|
|
4311
5363
|
disabled: isSubmitDisabled,
|
|
4312
|
-
className:
|
|
5364
|
+
className: cn5(
|
|
4313
5365
|
"self-end px-4 py-2 rounded-md text-white transition-colors",
|
|
4314
5366
|
"bg-blue-600 hover:bg-blue-700",
|
|
4315
5367
|
"disabled:opacity-50 disabled:cursor-not-allowed"
|
|
@@ -4330,7 +5382,7 @@ function AskUserPrompt({
|
|
|
4330
5382
|
}
|
|
4331
5383
|
);
|
|
4332
5384
|
}
|
|
4333
|
-
function
|
|
5385
|
+
function cn6(...classes) {
|
|
4334
5386
|
return classes.filter(Boolean).join(" ");
|
|
4335
5387
|
}
|
|
4336
5388
|
function MarkdownContent({
|
|
@@ -4391,7 +5443,7 @@ function MarkdownContent({
|
|
|
4391
5443
|
if (isCodeBlock) {
|
|
4392
5444
|
return /* @__PURE__ */ jsxs("div", { className: "relative my-2", children: [
|
|
4393
5445
|
language && /* @__PURE__ */ 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 }),
|
|
4394
|
-
/* @__PURE__ */ jsx("pre", { className: "bg-gray-100 dark:bg-gray-800 rounded-lg p-4 overflow-x-auto text-sm", children: /* @__PURE__ */ jsx("code", { className:
|
|
5446
|
+
/* @__PURE__ */ jsx("pre", { className: "bg-gray-100 dark:bg-gray-800 rounded-lg p-4 overflow-x-auto text-sm", children: /* @__PURE__ */ jsx("code", { className: cn6("font-mono", codeClassName), ...props, children }) })
|
|
4395
5447
|
] });
|
|
4396
5448
|
}
|
|
4397
5449
|
return /* @__PURE__ */ jsx(
|
|
@@ -4438,7 +5490,7 @@ function MarkdownContent({
|
|
|
4438
5490
|
() => customComponents ? { ...defaultComponents, ...customComponents } : defaultComponents,
|
|
4439
5491
|
[defaultComponents, customComponents]
|
|
4440
5492
|
);
|
|
4441
|
-
return /* @__PURE__ */ jsx("div", { className:
|
|
5493
|
+
return /* @__PURE__ */ jsx("div", { className: cn6("prose prose-sm dark:prose-invert max-w-none", className), children: /* @__PURE__ */ jsx(
|
|
4442
5494
|
ReactMarkdown,
|
|
4443
5495
|
{
|
|
4444
5496
|
remarkPlugins: [remarkGfm],
|
|
@@ -4448,7 +5500,7 @@ function MarkdownContent({
|
|
|
4448
5500
|
}
|
|
4449
5501
|
) });
|
|
4450
5502
|
}
|
|
4451
|
-
function
|
|
5503
|
+
function cn7(...classes) {
|
|
4452
5504
|
return classes.filter(Boolean).join(" ");
|
|
4453
5505
|
}
|
|
4454
5506
|
function formatToolName(toolName) {
|
|
@@ -4596,7 +5648,7 @@ function ToolCallEntry({
|
|
|
4596
5648
|
return /* @__PURE__ */ jsxs(
|
|
4597
5649
|
"div",
|
|
4598
5650
|
{
|
|
4599
|
-
className:
|
|
5651
|
+
className: cn7(
|
|
4600
5652
|
"rounded-lg border text-sm",
|
|
4601
5653
|
statusColor,
|
|
4602
5654
|
className
|
|
@@ -4617,7 +5669,7 @@ function ToolCallEntry({
|
|
|
4617
5669
|
]
|
|
4618
5670
|
}
|
|
4619
5671
|
),
|
|
4620
|
-
hasImages && /* @__PURE__ */ jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsx("div", { className:
|
|
5672
|
+
hasImages && /* @__PURE__ */ jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsx("div", { className: cn7(
|
|
4621
5673
|
"grid gap-2",
|
|
4622
5674
|
resultImages.length === 1 ? "grid-cols-1" : "grid-cols-2"
|
|
4623
5675
|
), children: resultImages.map((img, idx) => {
|
|
@@ -4676,7 +5728,7 @@ function ToolCallEntry({
|
|
|
4676
5728
|
}
|
|
4677
5729
|
);
|
|
4678
5730
|
}
|
|
4679
|
-
function
|
|
5731
|
+
function cn8(...classes) {
|
|
4680
5732
|
return classes.filter(Boolean).join(" ");
|
|
4681
5733
|
}
|
|
4682
5734
|
function ChatMessage({
|
|
@@ -4698,7 +5750,7 @@ function ChatMessage({
|
|
|
4698
5750
|
return /* @__PURE__ */ jsxs(
|
|
4699
5751
|
"div",
|
|
4700
5752
|
{
|
|
4701
|
-
className:
|
|
5753
|
+
className: cn8(
|
|
4702
5754
|
"flex gap-3 p-4",
|
|
4703
5755
|
isUser ? "flex-row-reverse" : "flex-row",
|
|
4704
5756
|
className
|
|
@@ -4707,7 +5759,7 @@ function ChatMessage({
|
|
|
4707
5759
|
/* @__PURE__ */ jsx(
|
|
4708
5760
|
"div",
|
|
4709
5761
|
{
|
|
4710
|
-
className:
|
|
5762
|
+
className: cn8(
|
|
4711
5763
|
"flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center",
|
|
4712
5764
|
isUser ? "bg-blue-500 text-white" : "bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300"
|
|
4713
5765
|
),
|
|
@@ -4717,7 +5769,7 @@ function ChatMessage({
|
|
|
4717
5769
|
/* @__PURE__ */ jsxs(
|
|
4718
5770
|
"div",
|
|
4719
5771
|
{
|
|
4720
|
-
className:
|
|
5772
|
+
className: cn8(
|
|
4721
5773
|
"flex-1 min-w-0 space-y-2",
|
|
4722
5774
|
isUser ? "text-right" : "text-left"
|
|
4723
5775
|
),
|
|
@@ -4725,7 +5777,7 @@ function ChatMessage({
|
|
|
4725
5777
|
/* @__PURE__ */ jsxs(
|
|
4726
5778
|
"div",
|
|
4727
5779
|
{
|
|
4728
|
-
className:
|
|
5780
|
+
className: cn8(
|
|
4729
5781
|
"flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400",
|
|
4730
5782
|
isUser ? "justify-end" : "justify-start"
|
|
4731
5783
|
),
|
|
@@ -4758,7 +5810,7 @@ function ChatMessage({
|
|
|
4758
5810
|
message.content && /* @__PURE__ */ jsx(
|
|
4759
5811
|
"div",
|
|
4760
5812
|
{
|
|
4761
|
-
className:
|
|
5813
|
+
className: cn8(
|
|
4762
5814
|
"rounded-lg p-3",
|
|
4763
5815
|
isUser ? "bg-blue-500 text-white" : "bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100"
|
|
4764
5816
|
),
|
|
@@ -4788,7 +5840,7 @@ function ChatMessage({
|
|
|
4788
5840
|
}
|
|
4789
5841
|
);
|
|
4790
5842
|
}
|
|
4791
|
-
function
|
|
5843
|
+
function cn9(...classes) {
|
|
4792
5844
|
return classes.filter(Boolean).join(" ");
|
|
4793
5845
|
}
|
|
4794
5846
|
function MessageList({
|
|
@@ -4818,7 +5870,7 @@ function MessageList({
|
|
|
4818
5870
|
"div",
|
|
4819
5871
|
{
|
|
4820
5872
|
ref: scrollRef,
|
|
4821
|
-
className:
|
|
5873
|
+
className: cn9(
|
|
4822
5874
|
"flex-1 overflow-y-auto",
|
|
4823
5875
|
className
|
|
4824
5876
|
),
|
|
@@ -4911,6 +5963,7 @@ function AgentChat({
|
|
|
4911
5963
|
isStreaming,
|
|
4912
5964
|
isLoadingHistory,
|
|
4913
5965
|
error,
|
|
5966
|
+
sessionId: currentSessionId,
|
|
4914
5967
|
clarification,
|
|
4915
5968
|
sendMessage,
|
|
4916
5969
|
respondToClarification,
|
|
@@ -4931,8 +5984,59 @@ function AgentChat({
|
|
|
4931
5984
|
onToolResult,
|
|
4932
5985
|
onClarification
|
|
4933
5986
|
});
|
|
5987
|
+
const dropZoneRef = useRef(null);
|
|
5988
|
+
const attachments = useFileAttachments({
|
|
5989
|
+
onUpload: enableAttachments && adapter ? async (_file, meta) => {
|
|
5990
|
+
const result = await adapter.getUploadUrl({
|
|
5991
|
+
sessionId: currentSessionId ?? "",
|
|
5992
|
+
...meta
|
|
5993
|
+
});
|
|
5994
|
+
return { uuid: result.attachmentUuid, uploadUrl: result.uploadUrl };
|
|
5995
|
+
} : async () => ({ uuid: "", uploadUrl: "" }),
|
|
5996
|
+
onConfirmUpload: enableAttachments && adapter ? (uuid) => adapter.confirmUpload(uuid) : void 0,
|
|
5997
|
+
onError: (msg) => {
|
|
5998
|
+
console.error("[AgentChat] Attachment error:", msg);
|
|
5999
|
+
}
|
|
6000
|
+
});
|
|
6001
|
+
const handleSend = useCallback(
|
|
6002
|
+
async (content) => {
|
|
6003
|
+
if (enableAttachments && attachments.hasReadyFiles) {
|
|
6004
|
+
const uuids = attachments.getReadyUuids();
|
|
6005
|
+
await sendMessage(content, { attachmentUuids: uuids });
|
|
6006
|
+
attachments.clear();
|
|
6007
|
+
} else {
|
|
6008
|
+
await sendMessage(content);
|
|
6009
|
+
}
|
|
6010
|
+
},
|
|
6011
|
+
[enableAttachments, attachments, sendMessage]
|
|
6012
|
+
);
|
|
4934
6013
|
return /* @__PURE__ */ jsxs(ChatContainer, { maxHeight, className, theme, children: [
|
|
4935
|
-
/* @__PURE__ */ jsx(
|
|
6014
|
+
enableAttachments ? /* @__PURE__ */ jsx(
|
|
6015
|
+
FileDropZone,
|
|
6016
|
+
{
|
|
6017
|
+
ref: dropZoneRef,
|
|
6018
|
+
...attachments.dropZoneProps,
|
|
6019
|
+
disabled: isLoading || !!clarification || isLoadingHistory,
|
|
6020
|
+
children: /* @__PURE__ */ jsx(
|
|
6021
|
+
MessageList,
|
|
6022
|
+
{
|
|
6023
|
+
messages,
|
|
6024
|
+
welcomeMessage,
|
|
6025
|
+
renderWelcome,
|
|
6026
|
+
isLoading,
|
|
6027
|
+
isStreaming,
|
|
6028
|
+
isLoadingHistory,
|
|
6029
|
+
enableMarkdown,
|
|
6030
|
+
showToolCalls,
|
|
6031
|
+
markdownComponents,
|
|
6032
|
+
markdownUrlTransform,
|
|
6033
|
+
markdownContentTransform,
|
|
6034
|
+
onImageClick,
|
|
6035
|
+
sendMessage: handleSend
|
|
6036
|
+
}
|
|
6037
|
+
)
|
|
6038
|
+
}
|
|
6039
|
+
) : /* @__PURE__ */ jsx(
|
|
4936
6040
|
MessageList,
|
|
4937
6041
|
{
|
|
4938
6042
|
messages,
|
|
@@ -4947,7 +6051,7 @@ function AgentChat({
|
|
|
4947
6051
|
markdownUrlTransform,
|
|
4948
6052
|
markdownContentTransform,
|
|
4949
6053
|
onImageClick,
|
|
4950
|
-
sendMessage
|
|
6054
|
+
sendMessage: handleSend
|
|
4951
6055
|
}
|
|
4952
6056
|
),
|
|
4953
6057
|
clarification && /* @__PURE__ */ jsx("div", { className: "border-t border-gray-200 dark:border-gray-700 p-4", children: /* @__PURE__ */ jsx(
|
|
@@ -4962,17 +6066,21 @@ function AgentChat({
|
|
|
4962
6066
|
/* @__PURE__ */ jsx(
|
|
4963
6067
|
ChatInput,
|
|
4964
6068
|
{
|
|
4965
|
-
onSend:
|
|
6069
|
+
onSend: handleSend,
|
|
4966
6070
|
onStop: isStreaming ? cancel : void 0,
|
|
4967
6071
|
placeholder,
|
|
4968
6072
|
disabled: !!clarification || isLoadingHistory,
|
|
4969
6073
|
isLoading,
|
|
4970
|
-
enableAttachments
|
|
6074
|
+
enableAttachments,
|
|
6075
|
+
attachments: enableAttachments ? attachments.files : void 0,
|
|
6076
|
+
onRemoveAttachment: enableAttachments ? attachments.removeFile : void 0,
|
|
6077
|
+
dropZoneRef: enableAttachments ? dropZoneRef : void 0,
|
|
6078
|
+
isUploadingAttachments: enableAttachments ? attachments.isUploading : void 0
|
|
4971
6079
|
}
|
|
4972
6080
|
)
|
|
4973
6081
|
] });
|
|
4974
6082
|
}
|
|
4975
|
-
function
|
|
6083
|
+
function cn10(...classes) {
|
|
4976
6084
|
return classes.filter(Boolean).join(" ");
|
|
4977
6085
|
}
|
|
4978
6086
|
function TodoList({
|
|
@@ -5001,7 +6109,7 @@ function TodoList({
|
|
|
5001
6109
|
return /* @__PURE__ */ jsxs(
|
|
5002
6110
|
"div",
|
|
5003
6111
|
{
|
|
5004
|
-
className:
|
|
6112
|
+
className: cn10(
|
|
5005
6113
|
"rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden",
|
|
5006
6114
|
className
|
|
5007
6115
|
),
|
|
@@ -5009,7 +6117,7 @@ function TodoList({
|
|
|
5009
6117
|
/* @__PURE__ */ jsxs(
|
|
5010
6118
|
"div",
|
|
5011
6119
|
{
|
|
5012
|
-
className:
|
|
6120
|
+
className: cn10(
|
|
5013
6121
|
"flex items-center gap-2 px-3 py-2 bg-gray-50 dark:bg-gray-800/50",
|
|
5014
6122
|
collapsible && "cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"
|
|
5015
6123
|
),
|
|
@@ -5040,7 +6148,7 @@ function TodoList({
|
|
|
5040
6148
|
!isCollapsed && /* @__PURE__ */ jsx("ul", { className: "divide-y divide-gray-100 dark:divide-gray-700", children: todos.map((todo, idx) => /* @__PURE__ */ jsxs(
|
|
5041
6149
|
"li",
|
|
5042
6150
|
{
|
|
5043
|
-
className:
|
|
6151
|
+
className: cn10(
|
|
5044
6152
|
"px-3 py-2 flex items-start gap-2 text-sm",
|
|
5045
6153
|
todo.status === "in_progress" && "bg-blue-50/50 dark:bg-blue-950/20",
|
|
5046
6154
|
todo.status === "completed" && "opacity-60"
|
|
@@ -5050,7 +6158,7 @@ function TodoList({
|
|
|
5050
6158
|
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(
|
|
5051
6159
|
"p",
|
|
5052
6160
|
{
|
|
5053
|
-
className:
|
|
6161
|
+
className: cn10(
|
|
5054
6162
|
"text-gray-700 dark:text-gray-300",
|
|
5055
6163
|
todo.status === "completed" && "line-through"
|
|
5056
6164
|
),
|
|
@@ -5067,6 +6175,39 @@ function TodoList({
|
|
|
5067
6175
|
);
|
|
5068
6176
|
}
|
|
5069
6177
|
|
|
5070
|
-
|
|
6178
|
+
// src/chat/index.ts
|
|
6179
|
+
var DEPRECATION_WARNING = `
|
|
6180
|
+
[@cadraos/sdk/chat] DEPRECATION WARNING
|
|
6181
|
+
|
|
6182
|
+
This module is deprecated and will be removed in a future version.
|
|
6183
|
+
Please migrate to @cadraos/react for:
|
|
6184
|
+
- Simpler setup with CadraCopilot
|
|
6185
|
+
- Better security (header-based SSE auth)
|
|
6186
|
+
- Scoped CSS that won't conflict with your app
|
|
6187
|
+
|
|
6188
|
+
Migration steps:
|
|
6189
|
+
1. npm install @cadraos/react
|
|
6190
|
+
2. import '@cadraos/react/styles.css'
|
|
6191
|
+
3. Replace <AgentChat> with <CadraCopilot> or <CadraChat>
|
|
6192
|
+
|
|
6193
|
+
See: https://github.com/cadraos/cadra-sdk/blob/main/packages/react/MIGRATION.md
|
|
6194
|
+
`;
|
|
6195
|
+
var hasWarned = false;
|
|
6196
|
+
function showDeprecationWarning() {
|
|
6197
|
+
if (hasWarned) return;
|
|
6198
|
+
hasWarned = true;
|
|
6199
|
+
if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") {
|
|
6200
|
+
return;
|
|
6201
|
+
}
|
|
6202
|
+
if (typeof window === "undefined") {
|
|
6203
|
+
return;
|
|
6204
|
+
}
|
|
6205
|
+
console.warn(DEPRECATION_WARNING);
|
|
6206
|
+
}
|
|
6207
|
+
if (typeof window !== "undefined") {
|
|
6208
|
+
setTimeout(showDeprecationWarning, 0);
|
|
6209
|
+
}
|
|
6210
|
+
|
|
6211
|
+
export { AgentChat, AlertCircleIcon, AskUserPrompt, BotIcon, ChatConfigProvider, ChatContainer, ChatInput, ChatMessage, CheckCircleIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, CircleIcon, ClockIcon, CodeIcon, CopyIcon, DatabaseIcon, DownloadIcon, ExternalLinkIcon, FileDropZone, FileIcon, FileTextIcon, HelpCircleIcon, ImageIcon, ImagesIcon, ListTodoIcon, LoadingSpinner, MarkdownContent, Maximize2Icon, MessageCircleIcon, MessageList, Minimize2Icon, PaperclipIcon, SendIcon, SparklesIcon, StopCircleIcon, TodoList, ToolCallEntry, UserIcon, XIcon, ZoomInIcon, ZoomOutIcon, compressImage, createDefaultAdapter, useAgentChat, useChatConfig, useChatConfigOptional, useExecutionStream, useFileAttachments };
|
|
5071
6212
|
//# sourceMappingURL=index.js.map
|
|
5072
6213
|
//# sourceMappingURL=index.js.map
|