189119a861
The launcher's palette, violet accent, Instrument Serif and Geist, without its sidebar and settings rows: a full-viewport hero over Horizon's own sunset (slow drift, parallax, grain), then sections for what it is (a statement that lights up as it scrolls), a screenshot mosaic, the parity count and a grid of the 14 subsystems, the launcher beside Horizon's own menus, the latest patch notes, the roadmap, community and the footer. Reveals, staggered letters and magnetic buttons stop under prefers-reduced-motion; a translucent top bar and a full-screen phone menu replace the sidebar. playhorizon.dev: canonical, og:url, absolute og/twitter images, sitemap.xml and its robots.txt line, and a 301 from *.up.railway.app (Railway's healthcheck host is still served). The full-page screenshots are no longer committed (about 17 MB): `npm run shots` writes them to the gitignored shots/. The unused blurred background is gone; LEGAL.md lists the hero and the settings screenshot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68 lines
3.2 KiB
JavaScript
68 lines
3.2 KiB
JavaScript
// Copies the launcher's fonts and the repo's screenshots into public/,
|
|
// converted for the web (woff2, webp in two widths). Run it after any of them change:
|
|
// `npm run assets`. Its output is committed, because Railway builds only website/.
|
|
import { mkdir, readFile, writeFile, copyFile } from "node:fs/promises";
|
|
import { join, dirname } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import sharp from "sharp";
|
|
import wawoff2 from "wawoff2";
|
|
|
|
const site = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
const repo = join(site, "..");
|
|
const pub = join(site, "public");
|
|
|
|
const fonts = [
|
|
"Geist-Regular.ttf",
|
|
"Geist-Medium.ttf",
|
|
"Geist-SemiBold.ttf",
|
|
"GeistMono-Regular.ttf",
|
|
"InstrumentSerif-Regular.ttf",
|
|
];
|
|
|
|
// The launcher's screenshots are left out: they show its background, whose licence is
|
|
// unconfirmed (see LEGAL.md). [source, name]; every image becomes media/<name>.webp and media/<name>-800.webp.
|
|
export const images = [
|
|
["docs/media/sunset.jpg", "sunset"],
|
|
["docs/media/village.jpg", "village"],
|
|
["docs/media/survival.jpg", "survival"],
|
|
["docs/media/nether.jpg", "nether"],
|
|
["docs/media/night.jpg", "night"],
|
|
["docs/media/underwater.jpg", "underwater"],
|
|
["docs/media/menu-title.png", "menu-title"],
|
|
["docs/media/menu-worlds.png", "menu-worlds"],
|
|
["docs/media/menu-options.png", "menu-options"],
|
|
["docs/media/menu-pause.png", "menu-pause"],
|
|
["docs/media/settings-simple.png", "settings"],
|
|
];
|
|
|
|
await mkdir(join(pub, "fonts"), { recursive: true });
|
|
await mkdir(join(pub, "media"), { recursive: true });
|
|
|
|
for (const font of fonts) {
|
|
const ttf = await readFile(join(repo, "crates/horizon-theme/fonts", font));
|
|
await writeFile(join(pub, "fonts", font.replace(".ttf", ".woff2")), await wawoff2.compress(ttf));
|
|
}
|
|
for (const licence of ["OFL-Geist.txt", "OFL-InstrumentSerif.txt"]) {
|
|
await copyFile(join(repo, "crates/horizon-theme/fonts", licence), join(pub, "fonts", licence));
|
|
}
|
|
|
|
for (const [source, name] of images) {
|
|
const input = join(repo, source);
|
|
await sharp(input).resize({ width: 1600, withoutEnlargement: true }).webp({ quality: 82 }).toFile(join(pub, "media", `${name}.webp`));
|
|
await sharp(input).resize({ width: 800 }).webp({ quality: 78 }).toFile(join(pub, "media", `${name}-800.webp`));
|
|
}
|
|
|
|
// The hero: Horizon's sunset over a savanna river valley, darkened and cooled a little so the
|
|
// wordmark and the violet accent sit on it, in a landscape and a portrait crop.
|
|
const hero = () => sharp(join(repo, "docs/media/sunset.jpg")).modulate({ brightness: 0.82, saturation: 0.86 }).linear(1.04, -6);
|
|
await hero().webp({ quality: 74 }).toFile(join(pub, "media/hero.webp"));
|
|
await hero().resize({ width: 1200 }).webp({ quality: 72 }).toFile(join(pub, "media/hero-1200.webp"));
|
|
await hero().extract({ left: 470, top: 0, width: 840, height: 1080 }).resize({ width: 720 }).webp({ quality: 72 }).toFile(join(pub, "media/hero-portrait.webp"));
|
|
|
|
// Icons from the project's logo.
|
|
await copyFile(join(repo, "docs/media/logo.svg"), join(pub, "favicon.svg"));
|
|
await sharp(join(repo, "docs/media/logo.svg")).resize(180).png().toFile(join(pub, "apple-touch-icon.png"));
|
|
await sharp(join(repo, "docs/media/logo.svg")).resize(32).png().toFile(join(pub, "favicon-32.png"));
|
|
|
|
console.log("assets written to public/");
|