From 6674c22cdd031098aa2e434dfb0ddb7a5df6145d Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 4 Jun 2026 13:52:01 +0800 Subject: [PATCH] fix: preserve psd clip mask layer stacking --- .../agents/AgentPsdTemplateEditorDialog.tsx | 66 ++++++++++++++++++- lib/agents/psd-template-parser.ts | 22 ++++++- 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/components/agents/AgentPsdTemplateEditorDialog.tsx b/components/agents/AgentPsdTemplateEditorDialog.tsx index 76c171d42..d76febf9c 100644 --- a/components/agents/AgentPsdTemplateEditorDialog.tsx +++ b/components/agents/AgentPsdTemplateEditorDialog.tsx @@ -2290,6 +2290,13 @@ export function AgentPsdTemplateEditorDialog({ ), [orderedVisiblePreviewLayers] ); + const renderPreviewSegments = useMemo( + () => + (selectedTemplate?.renderSegments || []).filter((segment) => + Boolean(String(segment.url || "").trim()) + ), + [selectedTemplate?.renderSegments] + ); const effectiveImageAssignments = useMemo(() => { const nextAssignments: Record = {}; @@ -2315,6 +2322,35 @@ export function AgentPsdTemplateEditorDialog({ return nextAssignments; }, [editableImageLayers, imageAssignments, selectedTemplate, sourceImageUrl]); + const hasVisibleAssignedPreviewImageLayer = useMemo( + () => + orderedVisiblePreviewLayers.some( + (layer) => + isAgentPsdImageLayerSlot(layer) && + Boolean(String(effectiveImageAssignments[layer.id] || "").trim()) + ), + [effectiveImageAssignments, orderedVisiblePreviewLayers] + ); + const shouldUseRenderSegmentPreviewFallback = + shouldShowCompositeFallback && + hasVisibleAssignedPreviewImageLayer && + hiddenLayerIds.length === 0 && + renderPreviewSegments.length > 0; + const shouldShowRenderBasePreviewFallback = + shouldShowCompositeFallback && !shouldUseRenderSegmentPreviewFallback; + const getRenderSegmentPreviewZIndex = ( + segment: NonNullable[number] + ) => { + const beforeLayerId = String(segment.beforeLayerId || "").trim(); + if (beforeLayerId) { + return Math.max( + 1, + (layerStackZIndexById.get(beforeLayerId) || PSD_LAYER_Z_INDEX_STEP) - 1 + ); + } + + return (orderedPreviewLayers.length + 1) * PSD_LAYER_Z_INDEX_STEP; + }; const pendingAssignedPreviewLayerIds = useMemo( () => orderedVisiblePreviewLayers @@ -4379,7 +4415,7 @@ export function AgentPsdTemplateEditorDialog({ transformOrigin: "top left", }} > - {shouldShowCompositeFallback && + {shouldShowRenderBasePreviewFallback && orderedVisiblePreviewLayers.length > 0 ? ( ) : null} + {shouldUseRenderSegmentPreviewFallback + ? renderPreviewSegments.map((segment) => ( + {`${selectedTemplate.name} + )) + : null}
{orderedVisiblePreviewLayers.map((layer) => { @@ -4410,6 +4467,13 @@ export function AgentPsdTemplateEditorDialog({ height: layer.height, } satisfies CSSProperties; + if ( + shouldUseRenderSegmentPreviewFallback && + !layer.isEditable + ) { + return null; + } + if (isAgentPsdImageLayerSlot(layer)) { const assignedUrl = effectiveImageAssignments[layer.id]; diff --git a/lib/agents/psd-template-parser.ts b/lib/agents/psd-template-parser.ts index a9e546efb..f7eb679d7 100644 --- a/lib/agents/psd-template-parser.ts +++ b/lib/agents/psd-template-parser.ts @@ -845,6 +845,18 @@ export async function preparePsdTemplateRenderAssets(params: { .filter(isAgentPsdTemplateLayerUserEditable) .map((layer) => layer.layerId) ); + const clipMaskBoundaryByTemplateLayerId = new Map(); + for (const layer of params.layers) { + if ( + isAgentPsdImageLayerSlot(layer) && + isAgentPsdTemplateLayerUserEditable(layer) && + layer.forceClipMask && + layer.clipMaskLayerId && + layer.clipMaskLayerId !== layer.id + ) { + clipMaskBoundaryByTemplateLayerId.set(layer.clipMaskLayerId, layer.id); + } + } const nextLayers = [...params.layers]; @@ -1041,15 +1053,21 @@ export async function preparePsdTemplateRenderAssets(params: { assetConcurrency, async (entry, index) => { const layerSlot = layerSlotByPsdLayerId.get(entry.layer.layerId); + const clipMaskBoundaryLayerId = + clipMaskBoundaryByTemplateLayerId.get(layerSlot?.id || "") || + clipMaskBoundaryByTemplateLayerId.get(entry.layer.id); const isEditableBoundary = layerSlot ? isAgentPsdTemplateLayerUserEditable(layerSlot) : editableLayerIdSet.has(entry.layer.layerId); + const isClipMaskBoundary = Boolean(clipMaskBoundaryLayerId); - if (isEditableBoundary) { + if (isEditableBoundary || isClipMaskBoundary) { return { index, input: null, - beforeLayerId: layerSlot?.id || "", + beforeLayerId: isClipMaskBoundary + ? clipMaskBoundaryLayerId || "" + : layerSlot?.id || "", editableBoundary: true, }; }