From 649825072b4cd8d1e1da376982de27b329ac4238 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 10 Apr 2026 23:28:17 +0800 Subject: [PATCH] feat: add async PSD render task pipeline --- .../agents/AgentPsdTemplateEditorDialog.tsx | 444 ++++++++++++------ lib/agents/psd-render-service.ts | 15 +- lib/agents/psd-render-task-contract.ts | 1 + lib/agents/psd-render-task-queue.ts | 17 + lib/agents/psd-template-renderer.ts | 9 + lib/client/long-task.ts | 89 +++- 6 files changed, 424 insertions(+), 151 deletions(-) diff --git a/components/agents/AgentPsdTemplateEditorDialog.tsx b/components/agents/AgentPsdTemplateEditorDialog.tsx index 3c489c48e..b92584225 100644 --- a/components/agents/AgentPsdTemplateEditorDialog.tsx +++ b/components/agents/AgentPsdTemplateEditorDialog.tsx @@ -1,6 +1,8 @@ "use client"; import { + Eye, + EyeOff, Expand, Image as ImageIcon, Loader2, @@ -1229,6 +1231,7 @@ export function AgentPsdTemplateEditorDialog({ const [textColorAssignments, setTextColorAssignments] = useState< Record >({}); + const [hiddenLayerIds, setHiddenLayerIds] = useState([]); const [uploadingSlotId, setUploadingSlotId] = useState(null); const [extendingSlotId, setExtendingSlotId] = useState(null); const [isRendering, setIsRendering] = useState(false); @@ -1400,6 +1403,15 @@ export function AgentPsdTemplateEditorDialog({ () => [...selectedTemplateLayers].reverse(), [selectedTemplateLayers] ); + const hiddenLayerIdSet = useMemo( + () => + new Set(hiddenLayerIds.map((layerId) => String(layerId || "").trim()).filter(Boolean)), + [hiddenLayerIds] + ); + const orderedVisiblePreviewLayers = useMemo( + () => orderedPreviewLayers.filter((layer) => !hiddenLayerIdSet.has(layer.id)), + [hiddenLayerIdSet, orderedPreviewLayers] + ); const orderedEditableLayers = useMemo( () => orderedPreviewLayers.filter((layer) => layer.isEditable), [orderedPreviewLayers] @@ -1422,11 +1434,11 @@ export function AgentPsdTemplateEditorDialog({ ); const shouldShowCompositeFallback = useMemo( () => - orderedPreviewLayers.length === 0 || - orderedPreviewLayers.some( + orderedVisiblePreviewLayers.length === 0 || + orderedVisiblePreviewLayers.some( (layer) => !String(layer.sourceUrl || "").trim() ), - [orderedPreviewLayers] + [orderedVisiblePreviewLayers] ); const effectiveImageAssignments = useMemo(() => { @@ -1511,6 +1523,9 @@ export function AgentPsdTemplateEditorDialog({ null, [selectedLayerId, selectedTemplateLayers] ); + const isSelectedLayerHidden = Boolean( + selectedLayer && hiddenLayerIdSet.has(selectedLayer.id) + ); const selectedImageLayer = selectedLayer && isAgentPsdImageLayerSlot(selectedLayer) ? selectedLayer @@ -1534,6 +1549,26 @@ export function AgentPsdTemplateEditorDialog({ const isSelectedImageExtending = Boolean( selectedImageLayer && extendingSlotId === selectedImageLayer.id ); + + function hideLayerTemporarily(layerId: string) { + const normalizedLayerId = String(layerId || "").trim(); + if (!normalizedLayerId) { + return; + } + setHiddenLayerIds((current) => + current.includes(normalizedLayerId) ? current : [...current, normalizedLayerId] + ); + } + + function restoreHiddenLayer(layerId: string) { + const normalizedLayerId = String(layerId || "").trim(); + if (!normalizedLayerId) { + return; + } + setHiddenLayerIds((current) => + current.filter((currentLayerId) => currentLayerId !== normalizedLayerId) + ); + } const selectedImageAdjustmentPanel = selectedImageLayer ? (
@@ -1766,6 +1801,7 @@ export function AgentPsdTemplateEditorDialog({ setImageSizes({}); setTextAssignments({}); setTextColorAssignments({}); + setHiddenLayerIds([]); setRenderedResultUrl(""); setRightPanelView("editor"); previousSelectedTemplateRef.current = null; @@ -1804,6 +1840,7 @@ export function AgentPsdTemplateEditorDialog({ setImagePlacements(initialState.imagePlacements); setTextAssignments(initialState.textAssignments); setTextColorAssignments(initialState.textColorAssignments); + setHiddenLayerIds([]); setSelectedLayerId( resolveTransferredSelectedLayerId({ nextTemplate: selectedTemplate, @@ -1838,6 +1875,15 @@ export function AgentPsdTemplateEditorDialog({ }; }, [open, selectedTemplate?.id]); + useEffect(() => { + if (hiddenLayerIds.length === 0) { + return; + } + + const validLayerIds = new Set(selectedTemplateLayers.map((layer) => layer.id)); + setHiddenLayerIds((current) => current.filter((layerId) => validLayerIds.has(layerId))); + }, [hiddenLayerIds.length, selectedTemplateLayers]); + useEffect(() => { previousSelectedTemplateRef.current = selectedTemplate; }, [selectedTemplate]); @@ -2208,9 +2254,11 @@ export function AgentPsdTemplateEditorDialog({ const payload = (await requestLongTaskResult({ endpoint: "/api/psd-templates/render", timeoutMs: 8 * 60 * 1000, + maxPollFailures: 8, payload: { templateId: selectedTemplate.id, sourceImageUrl: sourceImageUrl || "", + hiddenLayerIds, imageAssignments: effectiveImageAssignments, imagePlacements: effectiveImagePlacements, textAssignments: effectiveTextAssignments, @@ -2631,7 +2679,8 @@ export function AgentPsdTemplateEditorDialog({ transformOrigin: "top left", }} > - {shouldShowCompositeFallback ? ( + {shouldShowCompositeFallback && + orderedVisiblePreviewLayers.length > 0 ? ( {`${selectedTemplate.name} - {orderedPreviewLayers.map((layer) => { + {orderedVisiblePreviewLayers.map((layer) => { const layerBoxStyle = { left: layer.x, top: layer.y, @@ -2773,6 +2822,9 @@ export function AgentPsdTemplateEditorDialog({
{orderedEditableImageLayers.map((layer) => { + if (hiddenLayerIdSet.has(layer.id)) { + return null; + } const isSelected = selectedLayerId === layer.id; const frameStyle = getLayerFrameStyle( layer, @@ -2846,6 +2898,9 @@ export function AgentPsdTemplateEditorDialog({ })} {orderedEditableImageLayers.map((layer) => { + if (hiddenLayerIdSet.has(layer.id)) { + return null; + } const isSelected = selectedLayerId === layer.id; const hasAssignedImage = Boolean( String( @@ -2899,6 +2954,9 @@ export function AgentPsdTemplateEditorDialog({ })} {orderedEditableTextLayers.map((layer) => { + if (hiddenLayerIdSet.has(layer.id)) { + return null; + } const isSelected = selectedLayerId === layer.id; const frameStyle = getLayerFrameStyle( layer, @@ -3119,155 +3177,257 @@ export function AgentPsdTemplateEditorDialog({ key={`${selectedTemplate?.id || "template"}-${renderedResultUrl}`} sizeInfoText={sizeInfoText} /> - ) : selectedImageLayer ? ( -
- {selectedImageAdjustmentPanel} -
- ) : selectedTextLayer ? ( -
-
-