From 046fff7f2e9134db89063edecc7df7a0501ca793 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 1 Apr 2026 17:44:03 +0800 Subject: [PATCH] fix: harden psd template dialog against malformed payload --- .../agents/AgentPsdTemplateEditorDialog.tsx | 67 +++++++++++++++---- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/components/agents/AgentPsdTemplateEditorDialog.tsx b/components/agents/AgentPsdTemplateEditorDialog.tsx index c24196c54..92513ab95 100644 --- a/components/agents/AgentPsdTemplateEditorDialog.tsx +++ b/components/agents/AgentPsdTemplateEditorDialog.tsx @@ -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 => { + try { + const response = await fetch(url, { cache: "no-store" }); + const payload = (await response.json().catch(() => null)) as + | Partial + | 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;