fix: harden psd template dialog against malformed payload
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-01 17:44:03 +08:00
parent 49b54c2387
commit 046fff7f2e

View File

@ -42,6 +42,7 @@ import {
import {
isAgentPsdImageLayerSlot,
isAgentPsdTextLayerSlot,
parseAgentPsdTemplates,
type AgentPsdTemplate,
type AgentPsdTemplateImageLayerSlot,
type AgentPsdTemplateLayerSlot,
@ -51,7 +52,33 @@ import {
} from "@/lib/agents/psd-template-config";
import { resolveTextLayout } from "@/lib/agents/psd-text-layout";
const fetcher = (url: string) => fetch(url, { cache: "no-store" }).then((res) => res.json());
const fetcher = async (url: string): Promise<AgentPsdTemplateListResponse> => {
try {
const response = await fetch(url, { cache: "no-store" });
const payload = (await response.json().catch(() => null)) as
| Partial<AgentPsdTemplateListResponse>
| null;
if (!response.ok || !payload || payload.success !== true || !Array.isArray(payload.templates)) {
return {
success: false,
templates: [],
error: typeof payload?.error === "string" ? payload.error : "PSD 模板加载失败",
};
}
return {
success: true,
templates: parseAgentPsdTemplates(payload.templates),
};
} catch {
return {
success: false,
templates: [],
error: "PSD 模板加载失败",
};
}
};
const PREVIEW_VIEWPORT_PADDING = 56;
const PREVIEW_STAGE_MAX_SCALE = 0.82;
const PREVIEW_STAGE_TARGET_WIDTH = 760;
@ -302,30 +329,36 @@ export function AgentPsdTemplateEditorDialog({
() => templates.find((template) => template.id === selectedTemplateId) || null,
[selectedTemplateId, templates]
);
const selectedTemplateLayers = useMemo(() => {
if (!selectedTemplate || !Array.isArray(selectedTemplate.layers)) {
return [] as AgentPsdTemplateLayerSlot[];
}
return selectedTemplate.layers;
}, [selectedTemplate]);
const editableImageLayers = useMemo(
() =>
(selectedTemplate?.layers || []).filter(
selectedTemplateLayers.filter(
(layer): layer is AgentPsdTemplateImageLayerSlot =>
layer.isEditable && isAgentPsdImageLayerSlot(layer)
),
[selectedTemplate]
[selectedTemplateLayers]
);
const editableTextLayers = useMemo(
() =>
(selectedTemplate?.layers || []).filter(
selectedTemplateLayers.filter(
(layer): layer is AgentPsdTemplateTextLayerSlot =>
layer.isEditable && isAgentPsdTextLayerSlot(layer)
),
[selectedTemplate]
[selectedTemplateLayers]
);
const fixedLayerCount = useMemo(() => {
if (!selectedTemplate) return 0;
return selectedTemplate.layers.filter((layer) => !layer.isEditable).length;
}, [selectedTemplate]);
return selectedTemplateLayers.filter((layer) => !layer.isEditable).length;
}, [selectedTemplate, selectedTemplateLayers]);
const orderedPreviewLayers = useMemo(
() => (selectedTemplate ? [...selectedTemplate.layers].reverse() : []),
[selectedTemplate]
() => [...selectedTemplateLayers].reverse(),
[selectedTemplateLayers]
);
const orderedEditableLayers = useMemo(
() => orderedPreviewLayers.filter((layer) => layer.isEditable),
@ -403,8 +436,8 @@ export function AgentPsdTemplateEditorDialog({
}, [editableImageLayers, effectiveImageAssignments, imagePlacements, selectedTemplate]);
const selectedLayer = useMemo(
() => selectedTemplate?.layers.find((layer) => layer.id === selectedLayerId) || null,
[selectedLayerId, selectedTemplate]
() => selectedTemplateLayers.find((layer) => layer.id === selectedLayerId) || null,
[selectedLayerId, selectedTemplateLayers]
);
const selectedImageLayer = selectedLayer && isAgentPsdImageLayerSlot(selectedLayer) ? selectedLayer : null;
const selectedTextLayer = selectedLayer && isAgentPsdTextLayerSlot(selectedLayer) ? selectedLayer : null;
@ -562,7 +595,11 @@ export function AgentPsdTemplateEditorDialog({
const focusTextarea = () => {
textarea.focus();
const valueLength = textarea.value.length;
textarea.setSelectionRange(valueLength, valueLength);
try {
textarea.setSelectionRange(valueLength, valueLength);
} catch (error) {
console.warn("[AgentPsdTemplateEditorDialog] setSelectionRange failed:", error);
}
};
if (typeof window !== "undefined" && typeof window.requestAnimationFrame === "function") {
@ -664,7 +701,11 @@ export function AgentPsdTemplateEditorDialog({
};
const nextTarget = event.currentTarget;
nextTarget.setPointerCapture?.(event.pointerId);
try {
nextTarget.setPointerCapture?.(event.pointerId);
} catch (error) {
console.warn("[AgentPsdTemplateEditorDialog] setPointerCapture failed:", error);
}
const handlePointerMove = (moveEvent: PointerEvent) => {
const dragState = imageDragStateRef.current;