fix: preserve psd clip mask layer stacking

This commit is contained in:
Your Name 2026-06-04 13:52:01 +08:00
parent 74f2d052ac
commit 6674c22cdd
2 changed files with 85 additions and 3 deletions

View File

@ -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<string, string> = {};
@ -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<AgentPsdTemplate["renderSegments"]>[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 ? (
<TemplatePreviewImage
alt={`${selectedTemplate.name} 编辑底图`}
@ -4393,6 +4429,27 @@ export function AgentPsdTemplateEditorDialog({
width={960}
/>
) : null}
{shouldUseRenderSegmentPreviewFallback
? renderPreviewSegments.map((segment) => (
<img
alt={`${selectedTemplate.name} 分层底图`}
className="pointer-events-none absolute inset-0 h-full w-full object-cover"
decoding="async"
key={`render-segment-preview-${segment.id}`}
loading="eager"
src={buildTemplatePreviewThumbUrl(
segment.url,
{
width: 960,
quality: 74,
}
)}
style={{
zIndex: getRenderSegmentPreviewZIndex(segment),
}}
/>
))
: null}
<div className="pointer-events-none absolute inset-0">
{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];

View File

@ -845,6 +845,18 @@ export async function preparePsdTemplateRenderAssets(params: {
.filter(isAgentPsdTemplateLayerUserEditable)
.map((layer) => layer.layerId)
);
const clipMaskBoundaryByTemplateLayerId = new Map<string, string>();
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,
};
}