fix: fallback to original agent preview images
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-25 13:23:25 +08:00
parent bbde945bb8
commit 50d487dbcb

View File

@ -2,7 +2,16 @@
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
import { Fragment, useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
import {
Fragment,
useCallback,
useEffect,
useMemo,
useRef,
useState,
type ReactNode,
type SyntheticEvent,
} from "react";
import useSWR from "swr";
import { motion, AnimatePresence } from "framer-motion";
import JSZip from "jszip";
@ -285,6 +294,24 @@ function buildPreviewImageUrl(url: string, width: number) {
return buildImageProxyUrl(url, { width, quality: 86, format: "webp" });
}
function handlePreviewImageLoadError(
event: SyntheticEvent<HTMLImageElement, Event>,
originalUrl: string
) {
const fallbackUrl = String(originalUrl || "").trim();
if (!fallbackUrl) {
return;
}
const image = event.currentTarget;
if (image.dataset.previewFallbackApplied === "1") {
return;
}
image.dataset.previewFallbackApplied = "1";
image.src = fallbackUrl;
}
function buildDownloadFilename(url: string): string {
const fallback = `canvas-image-${Date.now()}.png`;
const raw = String(url || "").trim();
@ -3431,6 +3458,7 @@ export function AgentGenerationWorkspaceDarkV2({
src={buildPreviewImageUrl(attachment.url, 360)}
alt={attachment.name || slot.label}
className="h-full w-full object-cover"
onError={(event) => handlePreviewImageLoadError(event, attachment.url)}
/>
<div className="absolute inset-x-0 bottom-0 bg-black/70 px-2 py-1.5 text-[11px] text-white">
{slot.label}
@ -3579,6 +3607,9 @@ export function AgentGenerationWorkspaceDarkV2({
src={buildPreviewImageUrl(attachments[index]!.url, 256)}
alt={attachments[index]!.name || slot.label}
className="h-full w-full object-cover"
onError={(event) =>
handlePreviewImageLoadError(event, attachments[index]!.url)
}
/>
<div className="absolute inset-x-0 bottom-0 bg-black/70 px-3 py-2 text-xs font-medium text-white">
{slot.label}
@ -4139,6 +4170,9 @@ export function AgentGenerationWorkspaceDarkV2({
src={buildPreviewImageUrl(primaryPreviewUrl, 360)}
alt={candidate.sceneName || candidate.taskNo || candidate.productModel || "候选整套"}
className="h-full w-full object-contain object-top transition-transform duration-500 group-hover:scale-[1.02]"
onError={(event) =>
handlePreviewImageLoadError(event, primaryPreviewUrl)
}
/>
) : (
<div className="h-full w-full bg-[linear-gradient(180deg,rgba(255,255,255,0.03),rgba(255,255,255,0.01))]" />
@ -4378,6 +4412,9 @@ export function AgentGenerationWorkspaceDarkV2({
src={buildPreviewImageUrl(attachment!.url, 400)}
alt={attachment!.name || `输入图 ${index + 1}`}
className="h-full w-full object-cover"
onError={(event) =>
handlePreviewImageLoadError(event, attachment!.url)
}
/>
</div>
<div className="border-t border-white/10 px-3 py-2">
@ -4403,6 +4440,9 @@ export function AgentGenerationWorkspaceDarkV2({
src={buildPreviewImageUrl(selectedModelAttachment.url, 720)}
alt={selectedModelAttachment.name || "模特图"}
className="h-full w-full object-cover"
onError={(event) =>
handlePreviewImageLoadError(event, selectedModelAttachment.url)
}
/>
<div className="absolute left-3 top-3 rounded-full border border-emerald-300/25 bg-black/45 px-2.5 py-1 text-[11px] text-emerald-100">
模特图
@ -4471,6 +4511,9 @@ export function AgentGenerationWorkspaceDarkV2({
src={buildPreviewImageUrl(card.imageUrl, 720)}
alt={card.label}
className="block h-auto w-full"
onError={(event) =>
handlePreviewImageLoadError(event, card.imageUrl)
}
/>
) : null}
<div className="absolute left-3 top-3 rounded-full border border-white/10 bg-black/45 px-2.5 py-1 text-[11px] text-white/78">
@ -4542,6 +4585,9 @@ export function AgentGenerationWorkspaceDarkV2({
src={buildPreviewImageUrl(card.resultUrl, 720)}
alt={`结果图 ${card.index + 1}`}
className="block h-auto w-full cursor-zoom-in"
onError={(event) =>
handlePreviewImageLoadError(event, card.resultUrl!)
}
onClick={() => {
setActiveDetailReferenceCardId(card.id);
setFullscreenPreview(card.resultUrl!);
@ -4658,6 +4704,9 @@ export function AgentGenerationWorkspaceDarkV2({
src={buildPreviewImageUrl(attachment!.url, 400)}
alt={attachment!.name || `输入图 ${index + 1}`}
className="h-full w-full object-cover"
onError={(event) =>
handlePreviewImageLoadError(event, attachment!.url)
}
/>
</div>
<div className="border-t border-white/10 px-3 py-2">
@ -4683,6 +4732,9 @@ export function AgentGenerationWorkspaceDarkV2({
src={buildPreviewImageUrl(selectedModelAttachment.url, 720)}
alt={selectedModelAttachment.name || "模特图"}
className="h-full w-full object-cover"
onError={(event) =>
handlePreviewImageLoadError(event, selectedModelAttachment.url)
}
/>
<div className="absolute left-3 top-3 rounded-full border border-emerald-300/25 bg-black/45 px-2.5 py-1 text-[11px] text-emerald-100">
模特图
@ -4825,6 +4877,9 @@ export function AgentGenerationWorkspaceDarkV2({
src={buildPreviewImageUrl(previewUrl, 640)}
alt={candidate.sceneName || candidate.taskNo || candidate.productModel || "候选整套"}
className="h-full w-full object-cover"
onError={(event) =>
handlePreviewImageLoadError(event, previewUrl)
}
/>
) : null}
<div className="absolute left-3 top-3 rounded-full border border-white/10 bg-black/45 px-2.5 py-1 text-[11px] text-white/78">
@ -4901,6 +4956,9 @@ export function AgentGenerationWorkspaceDarkV2({
src={buildPreviewImageUrl(card.imageUrl, 720)}
alt={card.label}
className="h-full w-full object-cover"
onError={(event) =>
handlePreviewImageLoadError(event, card.imageUrl)
}
/>
) : null}
<div className="absolute left-3 top-3 rounded-full border border-white/10 bg-black/45 px-2.5 py-1 text-[11px] text-white/78">
@ -5002,6 +5060,9 @@ export function AgentGenerationWorkspaceDarkV2({
src={buildPreviewImageUrl(card.resultUrl, 720)}
alt={`结果图 ${card.index + 1}`}
className="h-full w-full cursor-zoom-in object-cover"
onError={(event) =>
handlePreviewImageLoadError(event, card.resultUrl!)
}
onClick={() => {
setActiveSkuReferenceCardId(card.id);
setFullscreenPreview(card.resultUrl!);