diff --git a/components/agents/AgentPsdTemplateEditorDialog.tsx b/components/agents/AgentPsdTemplateEditorDialog.tsx
index ec40d7f9e..112577785 100644
--- a/components/agents/AgentPsdTemplateEditorDialog.tsx
+++ b/components/agents/AgentPsdTemplateEditorDialog.tsx
@@ -231,10 +231,10 @@ function getResolvedTextPreview(layer: AgentPsdTemplateTextLayerSlot, value: str
return value || layer.placeholder || layer.layerName;
}
-function getTextLayerContentStyle(
+function getTextLayerRenderFrame(
layer: AgentPsdTemplateTextLayerSlot,
value: string
-): CSSProperties {
+) {
const resolvedLayout = resolveTextLayout({
text: getResolvedTextPreview(layer, value),
width: layer.width,
@@ -246,11 +246,35 @@ function getTextLayerContentStyle(
paddingY: layer.paddingY,
maxLines: layer.maxLines,
autoFit: false,
+ preferNoWrap: true,
});
+ const width = Math.max(layer.width, resolvedLayout.contentWidth);
+ const height = Math.max(layer.height, resolvedLayout.contentHeight);
+ const offsetX =
+ layer.textAlign === "right"
+ ? layer.width - width
+ : layer.textAlign === "center"
+ ? (layer.width - width) / 2
+ : 0;
return {
- width: "100%",
- height: "100%",
+ ...resolvedLayout,
+ width,
+ height,
+ offsetX,
+ offsetY: 0,
+ };
+}
+
+function getTextLayerContentStyle(
+ layer: AgentPsdTemplateTextLayerSlot,
+ value: string
+): CSSProperties {
+ const resolvedLayout = getTextLayerRenderFrame(layer, value);
+
+ return {
+ width: resolvedLayout.width,
+ height: resolvedLayout.height,
fontFamily: buildTextLayerFontFamily(layer),
fontSize: resolvedLayout.fontSize,
lineHeight: layer.lineHeight,
@@ -262,6 +286,9 @@ function getTextLayerContentStyle(
paddingRight: layer.paddingX,
paddingTop: layer.paddingY,
paddingBottom: layer.paddingY,
+ whiteSpace: "pre",
+ wordBreak: "keep-all",
+ overflowWrap: "normal",
};
}
@@ -1108,14 +1135,24 @@ export function AgentPsdTemplateEditorDialog({
return null;
}
+ const textFrame = getTextLayerRenderFrame(
+ layer,
+ value || layer.placeholder || layer.layerName
+ );
+
return (
{value || layer.placeholder || layer.layerName}
@@ -1242,6 +1279,7 @@ export function AgentPsdTemplateEditorDialog({
const currentValue = effectiveTextAssignments[layer.id] || "";
const preserveSourceAppearance =
!!layer.sourceUrl && !hasEditedTextValue(layer, currentValue);
+ const editedTextFrame = getTextLayerRenderFrame(layer, currentValue);
if (isSelected) {
return (
@@ -1263,18 +1301,33 @@ export function AgentPsdTemplateEditorDialog({
className="pointer-events-none absolute inset-0 h-full w-full object-fill"
/>
) : null}
-
{layer.layerName}
diff --git a/lib/agents/psd-template-renderer.ts b/lib/agents/psd-template-renderer.ts
index 0a7e60d14..2e11a5861 100644
--- a/lib/agents/psd-template-renderer.ts
+++ b/lib/agents/psd-template-renderer.ts
@@ -119,6 +119,30 @@ function sanitizeFileSegment(value: string, fallback: string) {
return normalized || fallback;
}
+function resolveTextOverlayFrame(params: {
+ layerWidth: number;
+ layerHeight: number;
+ contentWidth: number;
+ contentHeight: number;
+ textAlign: "left" | "center" | "right";
+}) {
+ const width = Math.max(params.layerWidth, params.contentWidth);
+ const height = Math.max(params.layerHeight, params.contentHeight);
+ const leftOffset =
+ params.textAlign === "right"
+ ? params.layerWidth - width
+ : params.textAlign === "center"
+ ? (params.layerWidth - width) / 2
+ : 0;
+
+ return {
+ width,
+ height,
+ leftOffset,
+ topOffset: 0,
+ };
+}
+
function escapeXml(value: string) {
return value
.replace(/&/g, "&")
@@ -234,6 +258,7 @@ async function createTextOverlayBuffer(params: {
paddingY,
maxLines,
autoFit: false,
+ preferNoWrap: true,
});
const adjustedFontSize = resolvedLayout.fontSize;
const lineHeightPx = resolvedLayout.lineHeightPx;
@@ -263,28 +288,27 @@ async function createTextOverlayBuffer(params: {
const anchor = textAlign === "center" ? "middle" : textAlign === "right" ? "end" : "start";
const x =
textAlign === "center"
- ? width / 2
+ ? resolvedLayout.contentWidth / 2
: textAlign === "right"
- ? width - paddingX
+ ? resolvedLayout.contentWidth - paddingX
: paddingX;
const tspans = lines
.map((line, index) => {
- const y = paddingY + fontSize + index * lineHeightPx;
const baselineY = paddingY + adjustedFontSize + index * lineHeightPx;
return `
${escapeXml(line)}`;
})
.join("");
const svg = `
-