#!/usr/bin/env node /* * inject-fonts.cjs — inline the @font-face blocks each HTML actually uses. * * node inject-fonts.cjs [file.html ...] * * hyperframes' renderer only auto-supplies its ~18 CANONICAL_FONTS. Every other * family (Anton, Bangers, VT323, Press Start 2P, …) falls back to a generic font * unless the HTML ships a local @font-face — even when it "looks fine" locally, * because that only works when the font happens to be installed as a system font. * On a clean/offline/CI machine it silently degrades. (hyperframes' own font * linter flags exactly this: font_family_without_font_face.) * * This step reads the bundled font library (modes/standard/fonts/fonts.css — * base64 woff2, no sub-resources), finds which of those families the HTML uses, * and inlines just those @font-face blocks into a `, "i"), ""); const used = usedFamilies(html); const already = declaredFamilies(html); const blocks = []; const inlined = []; for (const fam of used) { if (already.has(fam)) continue; const lb = lib.get(fam); if (!lb) continue; // canonical / system / unknown — not ours to supply blocks.push(...lb); inlined.push(fam); } if (!blocks.length) { fs.writeFileSync(file, html); return { file: path.basename(file), inlined: [] }; } const style = ``; // Prefer right after ; fall back to before , then , then prepend. if (/]*>/i.test(html)) html = html.replace(/]*>/i, (m0) => `${m0}\n${style}`); else if (/<\/head>/i.test(html)) html = html.replace(/<\/head>/i, `${style}\n`); else if (/]*>/i.test(html)) html = html.replace(/]*>/i, (m0) => `${m0}\n${style}`); else html = style + "\n" + html; fs.writeFileSync(file, html); return { file: path.basename(file), inlined }; } function main() { const project = path.resolve(process.argv[2] || ""); if (!process.argv[2]) { console.error("usage: inject-fonts.cjs [file.html ...]"); process.exit(1); } const lib = loadFontLibrary(); let files = process.argv.slice(3); if (!files.length) files = ["index.html", "rail.html", "index_fg.html"]; files = files.map((f) => (path.isAbsolute(f) ? f : path.join(project, f))); let touched = 0; for (const f of files) { const r = injectInto(f, lib); if (r == null) continue; if (r.inlined.length) { console.log(`[inject-fonts] ${r.file}: embedded ${r.inlined.join(", ")}`); touched++; } else console.log(`[inject-fonts] ${r.file}: no non-canonical fonts to embed`); } if (!touched) console.log(`[inject-fonts] nothing embedded (all fonts canonical/system or already declared)`); } main();