Absolutely—let’s auto-trim transparent edges so the icon is tightly cropped before you combine it with text. We’ll do it server-side right after background removal.

Below is a drop-in update to your /api/generate-image that:

Generates PNG (requests transparency)

Runs rembg fallback if needed

Trims all-transparent margins (with optional padding)

No client changes needed.

Update pages/api/generate-image.ts
// pages/api/generate-image.ts
import type { NextApiRequest, NextApiResponse } from "next";
import OpenAI from "openai";
import { Rembg } from "rembg-node"; // npm i rembg-node openai pngjs
import { PNG } from "pngjs";

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY! });
const rembg = new Rembg({ logging: false });

function pngHasTransparency(buf: Buffer): boolean {
  try {
    const png = PNG.sync.read(buf);
    if (!png.alpha) return false;
    const data = png.data;
    for (let i = 3; i < data.length; i += 4) {
      if (data[i] < 255) return true;
    }
    return false;
  } catch {
    return false;
  }
}

/** Crop to the smallest rect that contains any pixel with alpha > 0.
 *  Adds optional padding (px) inside the original image bounds.
 */
function trimTransparentEdges(buf: Buffer, padding = 8): Buffer {
  const src = PNG.sync.read(buf);
  const { width, height, data } = src;

  let minX = width, minY = height, maxX = -1, maxY = -1;

  for (let y = 0; y < height; y++) {
    const row = y * width * 4;
    for (let x = 0; x < width; x++) {
      const a = data[row + x * 4 + 3];
      if (a > 0) {
        if (x < minX) minX = x;
        if (x > maxX) maxX = x;
        if (y < minY) minY = y;
        if (y > maxY) maxY = y;
      }
    }
  }

  // If nothing found (fully transparent), just return original
  if (maxX < 0 || maxY < 0) return buf;

  // Apply padding but clamp to image bounds
  minX = Math.max(0, minX - padding);
  minY = Math.max(0, minY - padding);
  maxX = Math.min(width - 1, maxX + padding);
  maxY = Math.min(height - 1, maxY + padding);

  const cropW = maxX - minX + 1;
  const cropH = maxY - minY + 1;

  const out = new PNG({ width: cropW, height: cropH });
  for (let y = 0; y < cropH; y++) {
    const srcStart = ((minY + y) * width + minX) * 4;
    const srcEnd = srcStart + cropW * 4;
    const dstStart = y * cropW * 4;
    data.copy(out.data, dstStart, srcStart, srcEnd);
  }

  return PNG.sync.write(out);
}

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  try {
    if (req.method !== "POST") return res.status(405).end();

    const { prompt, size = 1024, style = "photo", trimPadding = 8 } = req.body || {};
    if (!prompt || typeof prompt !== "string") {
      return res.status(400).json({ error: "Missing prompt" });
    }

    // 1) Generate with transparent request
    const positive =
      style === "photo"
        ? `${prompt}, subject isolated, product photo, center composition, high detail, no background, studio lighting`
        : `${prompt}, minimal flat icon, clean silhouette, no background`;

    const out = await openai.images.generate({
      model: "gpt-image-1",
      prompt: positive,
      size: `${size}x${size}`,
      background: "transparent",
    });

    const b64 = out.data?.[0]?.b64_json;
    if (!b64) return res.status(500).json({ error: "Image generation failed" });

    let pngBuf = Buffer.from(b64, "base64");

    // 2) Fallback: ensure actual transparency
    if (!pngHasTransparency(pngBuf)) {
      pngBuf = await rembg.remove(pngBuf);
    }

    // 3) Trim transparent edges (with padding)
    pngBuf = trimTransparentEdges(pngBuf, Number(trimPadding) || 0);

    const dataUrl = `data:image/png;base64,${pngBuf.toString("base64")}`;
    return res.status(200).json({ dataUrl });
  } catch (err: any) {
    console.error(err);
    return res.status(500).json({ error: err?.message ?? "Internal error" });
  }
}

Install / env
npm i openai rembg-node pngjs
# Replit Secret:
#   OPENAI_API_KEY = sk-...

Optional: control trimming from the client

If you want to adjust padding from the UI, include trimPadding in your fetch body:

await fetch("/api/generate-image", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    prompt: iconPrompt,
    size: 1024,
    style: "photo",
    trimPadding: 12, // px around subject
  }),
});


With this, every generated icon will come back transparent and tightly cropped, so your “Combine” step looks crisp and centered.