Fix PSD template preview fallback
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-04-16 10:32:52 +08:00
parent 2a793dcbba
commit 169bf2fe4c

View File

@ -271,6 +271,111 @@ function buildTemplatePreviewThumbUrl(
return `/api/image-proxy?${searchParams.toString()}`;
}
function buildTemplatePreviewFallbackUrls(
template: Pick<AgentPsdTemplate, "previewUrl" | "renderBaseUrl">,
params?: {
width?: number;
quality?: number;
preferRenderBase?: boolean;
}
) {
const sourceUrls = (
params?.preferRenderBase
? [template.renderBaseUrl, template.previewUrl]
: [template.previewUrl, template.renderBaseUrl]
)
.map((url) => String(url || "").trim())
.filter(Boolean);
const candidates = [
...sourceUrls.map((url) =>
buildTemplatePreviewThumbUrl(url, {
width: params?.width,
quality: params?.quality,
})
),
...sourceUrls,
];
const seen = new Set<string>();
return candidates.filter((url) => {
if (!url || seen.has(url)) {
return false;
}
seen.add(url);
return true;
});
}
function TemplatePreviewImage({
template,
alt,
className,
width,
quality,
preferRenderBase,
decoding = "async",
fetchPriority,
loading = "lazy",
}: {
template: Pick<AgentPsdTemplate, "name" | "previewUrl" | "renderBaseUrl">;
alt?: string;
className?: string;
width?: number;
quality?: number;
preferRenderBase?: boolean;
decoding?: "async" | "auto" | "sync";
fetchPriority?: "auto" | "high" | "low";
loading?: "eager" | "lazy";
}) {
const fallbackUrls = useMemo(
() =>
buildTemplatePreviewFallbackUrls(template, {
width,
quality,
preferRenderBase,
}),
[preferRenderBase, quality, template.previewUrl, template.renderBaseUrl, width]
);
const fallbackUrlKey = fallbackUrls.join("\n");
const [activeUrlIndex, setActiveUrlIndex] = useState(0);
useEffect(() => {
setActiveUrlIndex(0);
}, [fallbackUrlKey]);
const src = fallbackUrls[activeUrlIndex] || "";
if (!src) {
return (
<div
aria-label={alt || template.name}
className={className}
role="img"
/>
);
}
const intrinsicSize = Math.max(1, Math.round(width || 1));
return (
// biome-ignore lint/a11y/noNoninteractiveElementInteractions: the image swaps to the next remote fallback when a thumbnail URL fails.
// biome-ignore lint/performance/noImgElement: these dynamic PSD previews already use the app image proxy and need browser-level error fallback.
<img
alt={alt || template.name}
className={className}
decoding={decoding}
fetchPriority={fetchPriority}
height={intrinsicSize}
loading={loading}
onError={() =>
setActiveUrlIndex((current) =>
current + 1 < fallbackUrls.length ? current + 1 : current
)
}
src={src}
width={intrinsicSize}
/>
);
}
function getScrollAreaViewportElement(root: HTMLDivElement | null) {
return root?.querySelector(
"[data-radix-scroll-area-viewport]"
@ -521,14 +626,11 @@ function renderTemplateSelectContent(
: "flex h-[46px] w-[46px] shrink-0 overflow-hidden rounded-lg border border-white/10 bg-white/5"
}
>
<img
alt={template.name}
<TemplatePreviewImage
className="h-full w-full object-cover"
decoding="async"
loading="lazy"
src={buildTemplatePreviewThumbUrl(template.previewUrl, {
width: compact ? 96 : 92,
})}
template={template}
width={compact ? 96 : 92}
/>
</div>
<div className="min-w-0 text-left">
@ -580,15 +682,12 @@ function renderTemplatePickerCardContent(
return (
<div className="flex min-w-0 items-center gap-3.5">
<div className="flex h-14 w-14 shrink-0 overflow-hidden rounded-xl border border-white/10 bg-white/5 shadow-inner">
<img
alt={template.name}
<TemplatePreviewImage
className="h-full w-full object-cover"
decoding="async"
fetchPriority={eager ? "high" : "low"}
loading={eager ? "eager" : "lazy"}
src={buildTemplatePreviewThumbUrl(template.previewUrl, {
width: 112,
})}
template={template}
width={112}
/>
</div>
<div className="min-w-0">
@ -2876,17 +2975,12 @@ export function AgentPsdTemplateEditorDialog({
aspectRatio: `${Math.max(1, selectedTemplate.width)} / ${Math.max(1, selectedTemplate.height)}`,
}}
>
<img
alt={selectedTemplate.name}
<TemplatePreviewImage
className="h-full w-full object-contain"
decoding="async"
src={buildTemplatePreviewThumbUrl(
selectedTemplate.previewUrl,
{
width: 440,
quality: 78,
}
)}
loading="eager"
quality={78}
template={selectedTemplate}
width={440}
/>
</div>
</div>
@ -2966,18 +3060,14 @@ export function AgentPsdTemplateEditorDialog({
>
{shouldShowCompositeFallback &&
orderedVisiblePreviewLayers.length > 0 ? (
<img
<TemplatePreviewImage
alt={`${selectedTemplate.name} 编辑底图`}
className="absolute inset-0 h-full w-full object-cover"
decoding="async"
src={buildTemplatePreviewThumbUrl(
selectedTemplate.renderBaseUrl ||
selectedTemplate.previewUrl,
{
width: 1280,
quality: 80,
}
)}
loading="eager"
preferRenderBase
quality={80}
template={selectedTemplate}
width={1280}
/>
) : null}