diff --git a/components/agents/AgentPsdTemplateEditorDialog.tsx b/components/agents/AgentPsdTemplateEditorDialog.tsx index 1656850ff..04baf125e 100644 --- a/components/agents/AgentPsdTemplateEditorDialog.tsx +++ b/components/agents/AgentPsdTemplateEditorDialog.tsx @@ -70,6 +70,7 @@ import { isAgentPsdImageLayerSlot, isAgentPsdTextLayerSlot, parseAgentPsdTemplates, + resolveAgentPsdTextLayerEffectiveAlign, } from "@/lib/agents/psd-template-config"; import { defaultAgentPsdTemplateSecondaryCategoryConfig, @@ -992,9 +993,11 @@ function getLayerFrameStyle( function getResolvedTextPreview( layer: AgentPsdTemplateTextLayerSlot, - value: string + value?: string ) { - return value || layer.placeholder || layer.layerName; + return typeof value === "string" + ? value + : layer.placeholder || layer.layerName; } function getResolvedTextLayerColor( @@ -1023,10 +1026,11 @@ function getTextLayerRenderFrame( }); const width = Math.max(layer.width, resolvedLayout.contentWidth); const height = Math.max(layer.height, resolvedLayout.contentHeight); + const textAlign = resolveAgentPsdTextLayerEffectiveAlign(layer); const offsetX = - layer.textAlign === "right" + textAlign === "right" ? layer.width - width - : layer.textAlign === "center" + : textAlign === "center" ? (layer.width - width) / 2 : 0; @@ -1046,16 +1050,17 @@ function renderTextLayerPreviewContent( ) { const resolvedText = getResolvedTextPreview(layer, value); const resolvedLayout = getTextLayerRenderFrame(layer, resolvedText); + const textAlign = resolveAgentPsdTextLayerEffectiveAlign(layer); const textAnchor = - layer.textAlign === "center" + textAlign === "center" ? "middle" - : layer.textAlign === "right" + : textAlign === "right" ? "end" : "start"; const textX = - layer.textAlign === "center" + textAlign === "center" ? resolvedLayout.width / 2 - : layer.textAlign === "right" + : textAlign === "right" ? resolvedLayout.width - layer.paddingX : layer.paddingX; @@ -3043,7 +3048,7 @@ export function AgentPsdTemplateEditorDialog({ } const value = - effectiveTextAssignments[layer.id] || ""; + effectiveTextAssignments[layer.id] ?? ""; const resolvedTextColor = effectiveTextColorAssignments[layer.id] || layer.textColor; @@ -3076,7 +3081,7 @@ export function AgentPsdTemplateEditorDialog({ const textFrame = getTextLayerRenderFrame( layer, - value || layer.placeholder || layer.layerName + value ); return ( @@ -3092,7 +3097,7 @@ export function AgentPsdTemplateEditorDialog({ > {renderTextLayerPreviewContent( layer, - value || layer.placeholder || layer.layerName, + value, resolvedTextColor )} @@ -3243,7 +3248,7 @@ export function AgentPsdTemplateEditorDialog({ isSelected ); const currentValue = - effectiveTextAssignments[layer.id] || ""; + effectiveTextAssignments[layer.id] ?? ""; const resolvedTextColor = effectiveTextColorAssignments[layer.id] || layer.textColor; @@ -3496,7 +3501,7 @@ export function AgentPsdTemplateEditorDialog({ } rows={4} value={ - effectiveTextAssignments[selectedTextLayer.id] || "" + effectiveTextAssignments[selectedTextLayer.id] ?? "" } />
diff --git a/components/agents/AgentWorkflowMaterialPicker.tsx b/components/agents/AgentWorkflowMaterialPicker.tsx index 6f2673f81..22133f965 100644 --- a/components/agents/AgentWorkflowMaterialPicker.tsx +++ b/components/agents/AgentWorkflowMaterialPicker.tsx @@ -171,8 +171,7 @@ export function AgentWorkflowMaterialPicker({ recommendedImageIds.every((id) => selectedSet.has(id)); const isAutoSelectedResult = Boolean(hasSystemRecommendedSelection) && !hasManualSelectionInteraction; - const canTogglePreviewMode = - totalImageCount > 0 && selectedGalleryCount > 0 && selectedGalleryCount < totalImageCount; + const canTogglePreviewMode = totalImageCount > 0; const visibleImages = useMemo(() => { if (!galleryImages.length) { return []; @@ -201,8 +200,8 @@ export function AgentWorkflowMaterialPicker({ if (hasManualSelectionInteraction) { return; } - setPreviewDisplayMode(hasSystemRecommendedSelection ? "selected" : "all"); - }, [hasManualSelectionInteraction, hasSystemRecommendedSelection, previewResetKey]); + setPreviewDisplayMode("all"); + }, [hasManualSelectionInteraction, previewResetKey]); const hasFetchSuccess = !disabled && !isLoading && !errorText && Boolean(result); diff --git a/lib/agents/psd-render-service.ts b/lib/agents/psd-render-service.ts index 159db8515..174527417 100644 --- a/lib/agents/psd-render-service.ts +++ b/lib/agents/psd-render-service.ts @@ -95,9 +95,12 @@ export async function executeAgentPsdTemplateRenderRequest( } if (isAgentPsdTextLayerSlot(layer)) { - const nextText = String(body.textAssignments[layer.id] || layer.defaultText || "").trim(); - if (nextText) { - textAssignments[layer.id] = nextText; + if ( + Object.prototype.hasOwnProperty.call(body.textAssignments, layer.id) + ) { + textAssignments[layer.id] = String( + body.textAssignments[layer.id] ?? "" + ).trim(); } textColorAssignments[layer.id] = normalizeHexColor( body.textColorAssignments[layer.id] || layer.textColor, @@ -112,7 +115,8 @@ export async function executeAgentPsdTemplateRenderRequest( layer.isRequired && (isAgentPsdImageLayerSlot(layer) ? !imageAssignments[layer.id] - : !textAssignments[layer.id]) + : !Object.prototype.hasOwnProperty.call(textAssignments, layer.id) && + !String(layer.defaultText || "").trim()) ); if (missingRequiredLayer) { throw new Error(`缺少必传素材:${missingRequiredLayer.layerName}`); diff --git a/lib/agents/psd-template-config.ts b/lib/agents/psd-template-config.ts index 0a590b9d8..c111ad73d 100644 --- a/lib/agents/psd-template-config.ts +++ b/lib/agents/psd-template-config.ts @@ -289,6 +289,27 @@ export function normalizeAgentPsdTextAlign(value: unknown): AgentPsdTextAlign { return "left"; } +export function resolveAgentPsdTextLayerEffectiveAlign( + layer: Pick< + AgentPsdTemplateTextLayerSlot, + "paddingX" | "styleSource" | "textAlign" | "width" + > +): AgentPsdTextAlign { + const textAlign = normalizeAgentPsdTextAlign(layer.textAlign); + const paddingX = normalizePositiveInteger(layer.paddingX); + const width = normalizePositiveInteger(layer.width); + + if ( + layer.styleSource !== "custom" && + textAlign === "center" && + paddingX <= Math.max(2, Math.round(width * 0.02)) + ) { + return "left"; + } + + return textAlign; +} + export function normalizeAgentPsdTextStyleSource(value: unknown): AgentPsdTextStyleSource { return normalizeText(value) === "custom" ? "custom" : "auto"; } diff --git a/lib/agents/psd-template-renderer.ts b/lib/agents/psd-template-renderer.ts index e509bdd56..639f4371f 100644 --- a/lib/agents/psd-template-renderer.ts +++ b/lib/agents/psd-template-renderer.ts @@ -6,6 +6,7 @@ import { hasAgentPsdTemplateStoredLayerSource, isAgentPsdImageLayerSlot, isAgentPsdTextLayerSlot, + resolveAgentPsdTextLayerEffectiveAlign, type AgentPsdTemplate, } from "@/lib/agents/psd-template-config"; import type { AgentPsdFont } from "@/lib/agents/psd-font-config"; @@ -527,17 +528,30 @@ export async function renderAgentPsdTemplate(params: { } if (isAgentPsdTextLayerSlot(layer)) { - const text = String(textAssignments[layer.id] || layer.defaultText || "").trim(); + const hasTextAssignment = Object.prototype.hasOwnProperty.call( + textAssignments, + layer.id + ); const defaultText = String(layer.defaultText || "").trim(); + const text = String( + hasTextAssignment ? textAssignments[layer.id] ?? "" : defaultText + ).trim(); const textColor = normalizeHexColor( textColorAssignments[layer.id] || layer.textColor, layer.textColor ); const hasColorChange = normalizeHexColor(textColor, layer.textColor) !== normalizeHexColor(layer.textColor); - if (!text || (text === defaultText && !hasColorChange)) { + if (!text) { + if (!hasTextAssignment) { + await appendStoredLayer(layer); + } + continue; + } + if (text === defaultText && !hasColorChange) { await appendStoredLayer(layer); continue; } + const textAlign = resolveAgentPsdTextLayerEffectiveAlign(layer); const font = layer.fontId ? fontMap.get(layer.fontId) : undefined; const fontBuffer = font?.fileUrl @@ -587,7 +601,7 @@ export async function renderAgentPsdTemplate(params: { lineHeight: layer.lineHeight, letterSpacing: layer.letterSpacing, textColor, - textAlign: layer.textAlign, + textAlign, paddingX: layer.paddingX, paddingY: layer.paddingY, maxLines: layer.maxLines, @@ -612,7 +626,7 @@ export async function renderAgentPsdTemplate(params: { layerHeight: layer.height, contentWidth: overlayLayout.contentWidth, contentHeight: overlayLayout.contentHeight, - textAlign: layer.textAlign, + textAlign, topOffset: -overlayLayout.visualOffsetY, }); diff --git a/lib/agents/psd-text-layout.ts b/lib/agents/psd-text-layout.ts index 10e0786af..00e81a852 100644 --- a/lib/agents/psd-text-layout.ts +++ b/lib/agents/psd-text-layout.ts @@ -272,9 +272,12 @@ export function analyzeTextLayerPixels(params: { const rightGap = Math.max(0, width - maxX - 1); const centerTolerance = Math.max(4, width * 0.06); const edgeTolerance = Math.max(6, width * 0.08); + const hasReliableCenterPadding = + leftGap >= edgeTolerance && rightGap >= edgeTolerance; const contentCenterX = (minX + maxX) / 2; const stageCenterX = (width - 1) / 2; const inferredTextAlign: AgentPsdTextAlign = + hasReliableCenterPadding && Math.abs(contentCenterX - stageCenterX) <= centerTolerance && Math.abs(leftGap - rightGap) <= edgeTolerance ? "center"