9f761a3e3b
🤖 Agent: merged after verify passed; site built with every docs page and no broken links (platform-2), website tests pass.
154 lines
11 KiB
Markdown
154 lines
11 KiB
Markdown
# Horizon's website
|
||
|
||
Live at **https://playhorizon.dev**.
|
||
|
||
A landing page, a Releases page, a Benchmarks page and a documentation section in Horizon's design language (`docs/design-system.md`), with the tokens of
|
||
`crates/horizon-theme` (generated into `src/tokens.css` by `cargo run -p xtask -- tokens`):
|
||
the Blocks look: Pixelify Sans headings over Geist text, Horizon's purple in a light and a dark
|
||
appearance (the system's, or the toggle's), square panels with an outline, a bevel and a lip, a
|
||
stone texture and a pixel landscape drawn in code (`scripts/art.mjs`), and short, eased motion. A headline and the download, then a scroll through what it is, the screenshots, the parity grid, the launcher, the newest
|
||
releases, the community links and the contributors. Plain HTML, CSS and JavaScript; no framework and
|
||
no runtime dependencies.
|
||
|
||
Home, Releases, Benchmarks and every Docs page share the header and phone menu rendered by
|
||
`build.mjs`, with the same links, theme switch and Download button.
|
||
|
||
## Layout
|
||
|
||
```
|
||
build.mjs renders dist/ (no dependencies)
|
||
docs/*.md the documentation, one Markdown file per page: "# Title", a lede paragraph, then sections
|
||
docs/nav.json the order of the pages and the groups of the left navigation
|
||
serve.mjs a tiny static server for local checks
|
||
src/index.html the page, with {{placeholders}} build.mjs fills
|
||
src/404.html the not-found page
|
||
src/privacy.html what the launcher can send when players allow it, retention, deletion (/privacy)
|
||
src/releases.html every release, newest first, with what it adds and fixes, its notes and downloads (/releases)
|
||
src/benchmarks.html Horizon against vanilla 26.3 on our machines, the hardware picker, players' results (/benchmarks)
|
||
src/hub.html the hub (/hub, /roadmap redirects to its Roadmap tab): Overview, Roadmap, Kanban, Journal, Contribute, a Simple / Technical switch
|
||
src/mods.html the registry's list (/mods) and a page per project (/mods/<id>), one template
|
||
src/docs.html the template of every documentation page (/docs/...), with its left navigation
|
||
src/tokens.css the design tokens, generated from crates/horizon-theme (do not edit)
|
||
src/style.css every style, on top of the tokens
|
||
src/main.js the home page: download buttons, recent releases, links, contributors, nav, tabs, lightbox, motion
|
||
src/chrome.js the light/dark switch, the phone menu and GitHub fetching, shared by the pages
|
||
src/releases-page.js the Releases page: the channel filter and the refresh from GitHub
|
||
src/mods.js the registry's markup (list, project page, submit form), shared by build and page; mods-page.js: the filter, the search, the refresh; mods-submit.js: the form's sign-in and sending
|
||
src/benchmarks-page.js the Benchmarks page: players' results from the telemetry server, the picker
|
||
src/docs-page.js the Docs pages: the shared theme switch and phone menu
|
||
src/bench.js the Benchmarks tables, shared by build and page
|
||
src/hardware.js the spec table estimates use (GPU TFLOPS and bandwidth, CPU single-thread score)
|
||
src/estimate.js estimates for unmeasured hardware from the nearest measured machine
|
||
src/markdown.js a small Markdown renderer (escapes everything), shared by build and page
|
||
src/releases.js the home page's "Recently added" and the Releases page's markup, shared by build and page
|
||
src/contributors.js the contributors' filtering and markup, shared by build and page
|
||
src/hub.js the hub's roadmap, board and journal from data/roadmap.json, GitHub's issues and the releases, shared by build and page; /data/journal.json
|
||
src/hub-page.js the hub page: tabs by #hash, the Simple / Technical switch (localStorage), the area filter, live states
|
||
data/roadmap.json the roadmap, generated by tools/roadmap.py from roadmap/roadmap.toml (do not edit); served at /data/roadmap.json
|
||
public/ fonts, screenshots, icons, og.png, robots.txt, benchmarks.json: copied as is (sitemap.xml is written by the build)
|
||
data/benchmarks/ our raw benchmark reports (tools/perf_bench.py) and fleet.json, the list scripts/benchmarks.mjs reads
|
||
scripts/benchmarks.mjs benchmark reports -> public/benchmarks.json (ours, and players' groups of 3+ installs)
|
||
scripts/assets.mjs rebuilds public/ from the repo (fonts to WOFF2, images to WebP, the pixel art)
|
||
scripts/art.mjs draws the stone tile and the hero's landscape (PNG, one pixel per texel)
|
||
scripts/shots.mjs Playwright screenshots at every width, and og.png
|
||
Dockerfile, Caddyfile, railway.json the Railway service
|
||
LEGAL.md every asset, its source and licence
|
||
shots/ (gitignored) full-page screenshots at 360–1920 px, a 1250×700 window, the open phone menu
|
||
```
|
||
|
||
## What comes from where
|
||
|
||
- **Built in** (`build.mjs`): Horizon's registry (`/mods`, one page per project) from the index ludovic111/horizon-mods
|
||
publishes (`HORIZON_MOD_INDEX=<file>` builds from a test index; docs/api/mod-index.md); the parity table, its score, harness count and audit date,
|
||
all from `README.md`; the documentation pages (`/docs`) from `website/docs/`; the Discord and Sponsor links from
|
||
`launcher/links.json`; the latest releases and the contributors (`/repos/ludovic111/horizon/contributors`,
|
||
bots left out, the owner first), when GitHub answers; without an answer the build still succeeds and
|
||
the Contributors block and its menu entries stay hidden until the page loads them. Locally these come from
|
||
the checkout; on Railway, which builds only this folder, from `main` on GitHub. The build
|
||
fails if the README has no parity table, so a broken README never deploys.
|
||
- **Fetched by the page**: the registry's index (a project merged after the build shows at once), the releases (the download button picks the newest
|
||
`launcher-v*` release's asset for the visitor's OS, the home page lists the newest game releases, and
|
||
the Releases page reloads its list when GitHub has a newer release), players' benchmark results
|
||
(`GET https://telemetry.playhorizon.dev/v1/benchmarks/summary`, 3-second timeout), the contributors (avatars come from `avatars.githubusercontent.com`),
|
||
and `links.json` from `main`. Each
|
||
falls back to what was built in, and the download button to the releases page.
|
||
|
||
## The documentation
|
||
|
||
`docs/<slug>.md` becomes `/docs/<slug>` (`index.md` is `/docs/`). Write for players first, then hosters,
|
||
then contributors; plain words, short paragraphs; claim only what players can download today, and
|
||
mark merged-but-unreleased things "next release". `{{HARNESSES}}` and `{{PERCENT}}` are filled from
|
||
the README. A line holding only `` becomes a figure (the file must be in
|
||
`public/media/`); links to other pages are `/docs/<slug>#heading`. The build checks every link
|
||
between pages and every image, and fails with the list when one is broken. `/server`
|
||
redirects to `/docs/hosting`.
|
||
|
||
The API portal is the repository's `docs/api/`, not a copy: `nav.json` lists its README as `api` (`/docs/api`) and
|
||
its pages as `api/<page>` (`/docs/api/<page>`), read from the checkout or from `main` on GitHub like the README.
|
||
Links between its pages become the site's, every other relative link goes to the file on GitHub, and the same link
|
||
check applies. Markdown tables render as tables. The other developer docs in `docs/` stay on GitHub and are linked
|
||
from the Contributing page.
|
||
|
||
## Run it locally
|
||
|
||
```sh
|
||
cd website
|
||
node build.mjs && node serve.mjs # http://localhost:4173
|
||
```
|
||
|
||
Asset and screenshot scripts need the dev dependencies (sharp, wawoff2, Playwright):
|
||
|
||
```sh
|
||
npm install
|
||
GITHUB_TOKEN=$(gh auth token) node build.mjs # optional token: bakes the releases past the API rate limit
|
||
npm run assets # after a font, screenshot or logo changes in the repo
|
||
npm run shots # shots/*.png (gitignored; SHOTS=dir to write elsewhere) and public/og.png
|
||
```
|
||
|
||
The screenshots are not committed: full-page PNGs at a dozen widths weigh about 17 MB.
|
||
Running text stays under 80 characters a line; on 360–430 px phones the narrowest paragraphs
|
||
fall to about 26–35, which is what that width allows. `npm run shots` also reports horizontal overflow, tap targets under 44 px on touch widths
|
||
and characters per line, for 360 to 1920 px wide and a 1250×700 window.
|
||
|
||
## Railway
|
||
|
||
The service deploys `main` (Railway's GitHub integration; there is no `website` branch any more) when `/website/**`, `README.md` or `launcher/links.json` change, and answers on https://playhorizon.dev. Its
|
||
`*.up.railway.app` address redirects there with a 301 (the Caddyfile), so search engines index
|
||
one URL; the page also names it as canonical, in its Open Graph tags and in `sitemap.xml`.
|
||
Railway's healthcheck reaches `$PORT` under its own host name and is served normally.
|
||
|
||
Create a service from the GitHub repo with **root directory `website`**. `railway.json`
|
||
selects the Dockerfile: a Node stage runs `build.mjs` (reading the README from `main`), and
|
||
a Caddy stage serves `dist/` on `$PORT` with compression, cache headers, a strict
|
||
Content-Security-Policy and the 404 page. Its watch patterns also redeploy when the README
|
||
or `launcher/links.json` change on main. No variables are needed; an optional
|
||
`GITHUB_TOKEN` (read-only, public repos) lets the build bake the releases even when
|
||
GitHub's anonymous rate limit is hit. `REGISTRY_UPSTREAM`
|
||
(`horizon-mod-registry.railway.internal:8080`) sends `/api/mods/*` to the registry's submission
|
||
service (`crates/horizon-mod-registry`); unset, `/mods/submit` says the form isn't open.
|
||
|
||
## Rules the site keeps
|
||
|
||
- The disclaimer, "Not an official Minecraft product. Not approved by or associated with
|
||
Mojang or Microsoft.", is under the headline, in the phone menu, the footer and the 404 page.
|
||
- No Minecraft logo or wordmark; "Minecraft" appears only descriptively ("for Minecraft:
|
||
Java Edition 26.3"). No game files are offered or linked.
|
||
- Parity is worded as the README words it: 100% of what the harnesses compare.
|
||
- No cookies, analytics, trackers or third-party fonts and scripts.
|
||
- Motion stops under `prefers-reduced-motion`; nothing hijacks the scroll.
|
||
|
||
## Benchmarks
|
||
|
||
`public/benchmarks.json` is what the Benchmarks page shows: `{ format: "horizon-benchmark-summary", version: 1,
|
||
generated, min_installs, groups }`, one group per GPU, CPU, render distance and game (`horizon`, `vanilla`) with
|
||
`medians` (`avg_fps`, `low_1pct_fps`, `p95_ms`, `world_ms`, `chunk_load_ms`, `rss_peak_mb`, `rss_steady_mb`).
|
||
Groups `"source": "us"` are our runs (with the machine, date, version and the four scenes); groups `"source":
|
||
"players"` come from players' `benchmark` uploads (docs/telemetry.md) and need at least `min_installs` (3)
|
||
different installs. The telemetry server's `GET /v1/benchmarks/summary` answers in the same shape; the page asks it
|
||
for players' groups and keeps the baked ones when it doesn't answer. The rules are in `scripts/benchmarks.mjs`, with
|
||
a fixture set (`scripts/fixtures/benchmarks/`) the server's test shares.
|
||
|
||
To publish new measurements: run `tools/perf_bench.py` for Horizon and vanilla on a machine (docs/benchmark.md), copy
|
||
the reports into `data/benchmarks/`, list them in `data/benchmarks/fleet.json`, then
|
||
`node scripts/benchmarks.mjs --ours data/benchmarks/fleet.json`.
|