feat: add download-all-fonts ZIP button, proxy fonts locally via /api/font
This commit is contained in:
1 parent
d5ec7ba0f8
commit
a5110a62dc
4 files changed
+132
-2
No files matched your search
@@ -23,6 +23,7 @@
|
|||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"croner": "^10.0.1",
|
"croner": "^10.0.1",
|
||||||
"hash-wasm": "^4.12.0",
|
"hash-wasm": "^4.12.0",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
"lucide-react": "^1.23.0",
|
"lucide-react": "^1.23.0",
|
||||||
"next": "^16.2.10",
|
"next": "^16.2.10",
|
||||||
"next-auth": "5.0.0-beta.31",
|
"next-auth": "5.0.0-beta.31",
|
||||||
|
|||||||
Generated
+80
File diff suppressed because it is too large.
Load diff
BIN
Binary file not shown.
|
After Width: | Height: | Size: 225 B |
@@ -52,6 +52,47 @@ export default function LogoGenerator() {
|
|||||||
});
|
});
|
||||||
}, [gifUrl, startTransition]);
|
}, [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 (
|
return (
|
||||||
<div className="card-grid md-2" style={{ alignItems: "start" }}>
|
<div className="card-grid md-2" style={{ alignItems: "start" }}>
|
||||||
<ContentCard icon="⚙️" title="Design" subtitle="Pick a Habbo font and type your text.">
|
<ContentCard icon="⚙️" title="Design" subtitle="Pick a Habbo font and type your text.">
|
||||||
@@ -122,6 +163,14 @@ export default function LogoGenerator() {
|
|||||||
? "❌ Error"
|
? "❌ Error"
|
||||||
: "💾 Save as site logo"}
|
: "💾 Save as site logo"}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary"
|
||||||
|
onClick={handleDownloadAll}
|
||||||
|
disabled={zipping}
|
||||||
|
>
|
||||||
|
{zipping ? "⏳ Creating ZIP..." : "📦 Download all fonts"}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</ContentCard>
|
</ContentCard>
|
||||||
|
|
||||||
@@ -146,11 +195,11 @@ export default function LogoGenerator() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p className="muted" style={{ marginBottom: 0, marginTop: "0.85rem", wordBreak: "break-all" }}>
|
<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">
|
<a href="https://habbofont.net" target="_blank" rel="noopener noreferrer">
|
||||||
habbofont.net
|
habbofont.net
|
||||||
</a>
|
</a>
|
||||||
. The GIF is loaded from their server.
|
{" "}and served locally.
|
||||||
</p>
|
</p>
|
||||||
</ContentCard>
|
</ContentCard>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user