fix: show claw image upload progress
This commit is contained in:
parent
36d575b825
commit
d37cb6cffc
@ -1116,6 +1116,14 @@ type CanvasTypewriterTextProps = {
|
|||||||
onComplete?: (typingKey: string) => void;
|
onComplete?: (typingKey: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type CanvasComposerUploadProgress = {
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
current: number;
|
||||||
|
total: number;
|
||||||
|
fileName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
type MarkdownTableBlockRange = {
|
type MarkdownTableBlockRange = {
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
@ -1353,6 +1361,60 @@ function CanvasAssistantTypingPlaceholder({ hint }: { hint?: string | null }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CanvasComposerUploadProgressDialog({
|
||||||
|
progress,
|
||||||
|
}: {
|
||||||
|
progress: CanvasComposerUploadProgress | null;
|
||||||
|
}) {
|
||||||
|
if (!progress) return null;
|
||||||
|
|
||||||
|
const percent =
|
||||||
|
progress.total > 1
|
||||||
|
? Math.max(
|
||||||
|
8,
|
||||||
|
Math.min(100, Math.round((progress.current / progress.total) * 100))
|
||||||
|
)
|
||||||
|
: 100;
|
||||||
|
const isSingleFile = progress.total <= 1;
|
||||||
|
|
||||||
|
return createPortal(
|
||||||
|
<div className="fixed inset-0 z-[1000] flex items-center justify-center bg-black/35 px-4 backdrop-blur-[2px]">
|
||||||
|
<div className="w-full max-w-[360px] rounded-[22px] border border-white/12 bg-[#111215]/95 p-5 text-white shadow-2xl">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-white/10 text-[#e0ff3a]">
|
||||||
|
<Loader2 className="h-5 w-5 animate-spin" />
|
||||||
|
</div>
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<div className="text-[15px] font-semibold leading-5">
|
||||||
|
{progress.title}
|
||||||
|
</div>
|
||||||
|
{progress.fileName ? (
|
||||||
|
<div className="mt-1 truncate text-[12px] leading-5 text-zinc-400">
|
||||||
|
{progress.fileName}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4 h-1.5 overflow-hidden rounded-full bg-white/10">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"h-full rounded-full bg-[#e0ff3a] transition-all duration-300",
|
||||||
|
isSingleFile && "animate-pulse"
|
||||||
|
)}
|
||||||
|
style={{ width: `${percent}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-3 text-[12px] leading-5 text-zinc-400">
|
||||||
|
{progress.message}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeCanvasAgentText(value: string): string {
|
function normalizeCanvasAgentText(value: string): string {
|
||||||
return String(value || "")
|
return String(value || "")
|
||||||
.replace(/\r\n/g, "\n")
|
.replace(/\r\n/g, "\n")
|
||||||
@ -1941,6 +2003,8 @@ export function CanvasChatPanel({
|
|||||||
} | null>(null);
|
} | null>(null);
|
||||||
const [downloadProgress, setDownloadProgress] =
|
const [downloadProgress, setDownloadProgress] =
|
||||||
useState<CanvasImageDownloadProgress | null>(null);
|
useState<CanvasImageDownloadProgress | null>(null);
|
||||||
|
const [composerUploadProgress, setComposerUploadProgress] =
|
||||||
|
useState<CanvasComposerUploadProgress | null>(null);
|
||||||
const [downloadingOriginalUrl, setDownloadingOriginalUrl] = useState("");
|
const [downloadingOriginalUrl, setDownloadingOriginalUrl] = useState("");
|
||||||
const [addingClawdImageUrl, setAddingClawdImageUrl] = useState("");
|
const [addingClawdImageUrl, setAddingClawdImageUrl] = useState("");
|
||||||
const downloadAbortControllerRef = useRef<AbortController | null>(null);
|
const downloadAbortControllerRef = useRef<AbortController | null>(null);
|
||||||
@ -10641,30 +10705,45 @@ export function CanvasChatPanel({
|
|||||||
|
|
||||||
if (isClawdMode) {
|
if (isClawdMode) {
|
||||||
let handledCount = 0;
|
let handledCount = 0;
|
||||||
for (const file of files) {
|
try {
|
||||||
const uploaded = await uploadImageFileForComposerAttachment(file);
|
for (const [index, file] of files.entries()) {
|
||||||
if (!uploaded) continue;
|
setComposerUploadProgress({
|
||||||
|
title:
|
||||||
|
files.length === 1
|
||||||
|
? "正在上传图片"
|
||||||
|
: `正在上传 ${files.length} 张图片`,
|
||||||
|
message: `正在上传第 ${index + 1}/${files.length} 张,图片较大时可能需要一点时间。`,
|
||||||
|
current: index + 1,
|
||||||
|
total: files.length,
|
||||||
|
fileName: file.name,
|
||||||
|
});
|
||||||
|
|
||||||
handledCount += 1;
|
const uploaded = await uploadImageFileForComposerAttachment(file);
|
||||||
setAttachments((prev) => {
|
if (!uploaded) continue;
|
||||||
const newIndex = prev.length;
|
|
||||||
setTimeout(() => {
|
|
||||||
insertImageTagInEditor(uploaded.imageUrl, uploaded.name, newIndex);
|
|
||||||
(textareaRef.current as HTMLDivElement | null)?.focus();
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
return [
|
handledCount += 1;
|
||||||
...prev,
|
setAttachments((prev) => {
|
||||||
{
|
const newIndex = prev.length;
|
||||||
url: uploaded.imageUrl,
|
setTimeout(() => {
|
||||||
name: uploaded.name,
|
insertImageTagInEditor(uploaded.imageUrl, uploaded.name, newIndex);
|
||||||
contentType: uploaded.contentType,
|
(textareaRef.current as HTMLDivElement | null)?.focus();
|
||||||
assetId: uploaded.assetId,
|
}, 0);
|
||||||
width: uploaded.width,
|
|
||||||
height: uploaded.height,
|
return [
|
||||||
},
|
...prev,
|
||||||
];
|
{
|
||||||
});
|
url: uploaded.imageUrl,
|
||||||
|
name: uploaded.name,
|
||||||
|
contentType: uploaded.contentType,
|
||||||
|
assetId: uploaded.assetId,
|
||||||
|
width: uploaded.width,
|
||||||
|
height: uploaded.height,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setComposerUploadProgress(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handledCount > 0) {
|
if (handledCount > 0) {
|
||||||
@ -10887,30 +10966,47 @@ export function CanvasChatPanel({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let handledImageCount = 0;
|
let handledImageCount = 0;
|
||||||
for (const file of imageFiles) {
|
if (imageFiles.length > 0) {
|
||||||
const uploaded = await uploadImageFileForComposerAttachment(file);
|
try {
|
||||||
if (!uploaded) continue;
|
for (const [index, file] of imageFiles.entries()) {
|
||||||
|
setComposerUploadProgress({
|
||||||
|
title:
|
||||||
|
imageFiles.length === 1
|
||||||
|
? "正在上传图片"
|
||||||
|
: `正在上传 ${imageFiles.length} 张图片`,
|
||||||
|
message: `正在上传第 ${index + 1}/${imageFiles.length} 张,图片较大时可能需要一点时间。`,
|
||||||
|
current: index + 1,
|
||||||
|
total: imageFiles.length,
|
||||||
|
fileName: file.name,
|
||||||
|
});
|
||||||
|
|
||||||
handledImageCount += 1;
|
const uploaded = await uploadImageFileForComposerAttachment(file);
|
||||||
setAttachments((prev) => {
|
if (!uploaded) continue;
|
||||||
const newIndex = prev.length;
|
|
||||||
setTimeout(() => {
|
|
||||||
insertImageTagInEditor(uploaded.imageUrl, uploaded.name, newIndex);
|
|
||||||
(textareaRef.current as HTMLDivElement | null)?.focus();
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
return [
|
handledImageCount += 1;
|
||||||
...prev,
|
setAttachments((prev) => {
|
||||||
{
|
const newIndex = prev.length;
|
||||||
url: uploaded.imageUrl,
|
setTimeout(() => {
|
||||||
name: uploaded.name,
|
insertImageTagInEditor(uploaded.imageUrl, uploaded.name, newIndex);
|
||||||
contentType: uploaded.contentType,
|
(textareaRef.current as HTMLDivElement | null)?.focus();
|
||||||
assetId: uploaded.assetId,
|
}, 0);
|
||||||
width: uploaded.width,
|
|
||||||
height: uploaded.height,
|
return [
|
||||||
},
|
...prev,
|
||||||
];
|
{
|
||||||
});
|
url: uploaded.imageUrl,
|
||||||
|
name: uploaded.name,
|
||||||
|
contentType: uploaded.contentType,
|
||||||
|
assetId: uploaded.assetId,
|
||||||
|
width: uploaded.width,
|
||||||
|
height: uploaded.height,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setComposerUploadProgress(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handledImageCount > 0) {
|
if (handledImageCount > 0) {
|
||||||
@ -11200,7 +11296,9 @@ export function CanvasChatPanel({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const isLoading = status === 'streaming' || status === 'submitted';
|
const isLoading = status === 'streaming' || status === 'submitted';
|
||||||
const isSubmitting = isLoading || isDetailGenerating || isClawdSubmitting;
|
const isComposerUploading = Boolean(composerUploadProgress);
|
||||||
|
const isSubmitting =
|
||||||
|
isLoading || isDetailGenerating || isClawdSubmitting || isComposerUploading;
|
||||||
const hasInputOrAttachments = Boolean(
|
const hasInputOrAttachments = Boolean(
|
||||||
input.trim() || attachments.length > 0 || (!isDetailGenerationMode && selectedSkillTag)
|
input.trim() || attachments.length > 0 || (!isDetailGenerationMode && selectedSkillTag)
|
||||||
);
|
);
|
||||||
@ -12851,6 +12949,7 @@ export function CanvasChatPanel({
|
|||||||
onCancel={handleCancelOriginalImageDownload}
|
onCancel={handleCancelOriginalImageDownload}
|
||||||
progress={downloadProgress}
|
progress={downloadProgress}
|
||||||
/>
|
/>
|
||||||
|
<CanvasComposerUploadProgressDialog progress={composerUploadProgress} />
|
||||||
|
|
||||||
{!isWorkspacePresentation && shouldShowCanvasBanner ? (
|
{!isWorkspacePresentation && shouldShowCanvasBanner ? (
|
||||||
<div className="px-[10px] pb-0">
|
<div className="px-[10px] pb-0">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user