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"