feat: add download-all-fonts ZIP button, proxy fonts locally via /api/font

This commit is contained in:
openhands committed 2026-07-03 17:25:55 +02:00
1 parent d5ec7ba0f8
commit a5110a62dc
4 files changed
+132 -2

No files matched your search

+51 -2
View File
@@ -52,6 +52,47 @@ export default function LogoGenerator() {
});
}, [gifUrl, startTransition]);
const [zipping, setZipping] = useState(false);
const allFonts = useMemo(
() => HABBO_FONT_GROUPS.flatMap((g) => g.fonts),
[],
);
const handleDownloadAll = useCallback(async () => {
if (zipping) return;
setZipping(true);
try {
const JSZip = (await import("jszip")).default;
const zip = new JSZip();
const folder = zip.folder(safeText.replace(/[^a-z0-9]+/gi, "_") || "logo")!;
const results = await Promise.allSettled(
allFonts.map(async (font) => {
const url = buildFontUrl(font.styleName, safeText);
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const blob = await res.blob();
folder.file(`${font.styleName}.gif`, blob);
}),
);
const failed = results.filter((r) => r.status === "rejected").length;
const content = await zip.generateAsync({ type: "blob" });
const a = document.createElement("a");
a.href = URL.createObjectURL(content);
a.download = `${safeText.replace(/[^a-z0-9]+/gi, "_") || "logo"}-all-fonts.zip`;
a.click();
URL.revokeObjectURL(a.href);
if (failed > 0) {
alert(`${allFonts.length - failed}/${allFonts.length} fonts downloaded. ${failed} failed.`);
}
} catch {
alert("Failed to create ZIP.");
} finally {
setZipping(false);
}
}, [safeText, allFonts, zipping]);
return (
<div className="card-grid md-2" style={{ alignItems: "start" }}>
<ContentCard icon="⚙️" title="Design" subtitle="Pick a Habbo font and type your text.">
@@ -122,6 +163,14 @@ export default function LogoGenerator() {
? "❌ Error"
: "💾 Save as site logo"}
</button>
<button
type="button"
className="btn btn-secondary"
onClick={handleDownloadAll}
disabled={zipping}
>
{zipping ? "⏳ Creating ZIP..." : "📦 Download all fonts"}
</button>
</div>
</ContentCard>
@@ -146,11 +195,11 @@ export default function LogoGenerator() {
/>
</div>
<p className="muted" style={{ marginBottom: 0, marginTop: "0.85rem", wordBreak: "break-all" }}>
Generated via{" "}
Fonts rendered via{" "}
<a href="https://habbofont.net" target="_blank" rel="noopener noreferrer">
habbofont.net
</a>
. The GIF is loaded from their server.
{" "}and served locally.
</p>
</ContentCard>
</div>