mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix(meme-generator): cap auto font size, center text vertically, fix sidebar truncation
- Auto font size now capped at min(200, boxHeight/2, boxWidth/3) to prevent oversized text on server render - Text vertically centered within each text box - Sidebar color labels shortened (Text/Stroke) with min-w-0 to prevent overflow
This commit is contained in:
@@ -143,8 +143,9 @@ export function autoSizeFontToFit(
|
|||||||
boxHeight: number,
|
boxHeight: number,
|
||||||
maxFontSize = DEFAULT_MAX_FONT_SIZE,
|
maxFontSize = DEFAULT_MAX_FONT_SIZE,
|
||||||
): number {
|
): number {
|
||||||
|
const effectiveMax = Math.min(maxFontSize, Math.floor(boxHeight / 2), Math.floor(boxWidth / 3));
|
||||||
let lo = MIN_FONT_SIZE;
|
let lo = MIN_FONT_SIZE;
|
||||||
let hi = maxFontSize;
|
let hi = Math.max(MIN_FONT_SIZE, effectiveMax);
|
||||||
let best = MIN_FONT_SIZE;
|
let best = MIN_FONT_SIZE;
|
||||||
|
|
||||||
while (lo <= hi) {
|
while (lo <= hi) {
|
||||||
@@ -216,6 +217,9 @@ export function renderMemeTextSvg(opts: MemeTextOptions): Buffer {
|
|||||||
const fillAttr = escapeXmlAttr(textColor);
|
const fillAttr = escapeXmlAttr(textColor);
|
||||||
const strokeAttr = escapeXmlAttr(strokeColor);
|
const strokeAttr = escapeXmlAttr(strokeColor);
|
||||||
|
|
||||||
|
const totalTextHeight = lines.length * lineHeight;
|
||||||
|
const yOffset = by + (bh - totalTextHeight) / 2 + fontSize * 0.85;
|
||||||
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i];
|
const line = lines[i];
|
||||||
if (!line) continue;
|
if (!line) continue;
|
||||||
@@ -228,12 +232,10 @@ export function renderMemeTextSvg(opts: MemeTextOptions): Buffer {
|
|||||||
} else if (textAlign === "right") {
|
} else if (textAlign === "right") {
|
||||||
x = bx + bw - lineWidth;
|
x = bx + bw - lineWidth;
|
||||||
} else {
|
} else {
|
||||||
// center
|
|
||||||
x = bx + (bw - lineWidth) / 2;
|
x = bx + (bw - lineWidth) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Baseline y: top of box + line offset + ascent approximation
|
const y = yOffset + i * lineHeight;
|
||||||
const y = by + (i + 1) * lineHeight;
|
|
||||||
|
|
||||||
let d: string;
|
let d: string;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -163,46 +163,46 @@ function EditorSettings() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Colors */}
|
{/* Colors */}
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-2">
|
||||||
<div className="flex-1">
|
<div className="flex-1 min-w-0">
|
||||||
<label htmlFor="text-color" className="text-xs text-muted-foreground block mb-0.5">
|
<label htmlFor="text-color" className="text-xs text-muted-foreground block mb-0.5">
|
||||||
Text Color
|
Text
|
||||||
</label>
|
</label>
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1">
|
||||||
<input
|
<input
|
||||||
id="text-color"
|
id="text-color"
|
||||||
data-testid="text-color"
|
data-testid="text-color"
|
||||||
type="color"
|
type="color"
|
||||||
value={textColor}
|
value={textColor}
|
||||||
onChange={(e) => setTextColor(e.target.value)}
|
onChange={(e) => setTextColor(e.target.value)}
|
||||||
className="w-8 h-8 rounded border border-border shrink-0 cursor-pointer"
|
className="w-7 h-7 rounded border border-border shrink-0 cursor-pointer"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={textColor}
|
value={textColor}
|
||||||
onChange={(e) => setTextColor(e.target.value)}
|
onChange={(e) => setTextColor(e.target.value)}
|
||||||
className="flex-1 px-1.5 py-1 rounded border border-border bg-background text-xs text-foreground font-mono"
|
className="flex-1 min-w-0 px-1 py-1 rounded border border-border bg-background text-[11px] text-foreground font-mono"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1 min-w-0">
|
||||||
<label htmlFor="stroke-color" className="text-xs text-muted-foreground block mb-0.5">
|
<label htmlFor="stroke-color" className="text-xs text-muted-foreground block mb-0.5">
|
||||||
Stroke Color
|
Stroke
|
||||||
</label>
|
</label>
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1">
|
||||||
<input
|
<input
|
||||||
id="stroke-color"
|
id="stroke-color"
|
||||||
data-testid="stroke-color"
|
data-testid="stroke-color"
|
||||||
type="color"
|
type="color"
|
||||||
value={strokeColor}
|
value={strokeColor}
|
||||||
onChange={(e) => setStrokeColor(e.target.value)}
|
onChange={(e) => setStrokeColor(e.target.value)}
|
||||||
className="w-8 h-8 rounded border border-border shrink-0 cursor-pointer"
|
className="w-7 h-7 rounded border border-border shrink-0 cursor-pointer"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={strokeColor}
|
value={strokeColor}
|
||||||
onChange={(e) => setStrokeColor(e.target.value)}
|
onChange={(e) => setStrokeColor(e.target.value)}
|
||||||
className="flex-1 px-1.5 py-1 rounded border border-border bg-background text-xs text-foreground font-mono"
|
className="flex-1 min-w-0 px-1 py-1 rounded border border-border bg-background text-[11px] text-foreground font-mono"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user