feat(urd): mærket bliver ð, og frontend-bygget bliver deterministisk
## Mærket Urds oplagte mærke var ᚢ uruz — bogstavet navnet begynder med i den ældre futhark, præcis som Verdandes er ᚹ wunjo. Det blev tegnet sådan først. Men uruz er allerede et af husets egne produkter, og dets egen `scripts/gen-icons.ts` siger i sin kommentar at den "draws the Uruz rune (ᚢ) in rune-gold" med "a tall left vertical, a shoulder across the top, and a shorter" — altså samme glyf, samme konstruktion, kun en anden farve. To af samme persons apps side om side på en hjemmeskærm, hvilket er nøjagtig det sted mærket skal kunne skelnes. ᚦ thurisaz blev forkastet et niveau nede af samme grund: en stav med en trekant til højre, og det er ᚹ også. Ved 16px er de én form. Så bogstavet frem for runen: ð, ethet i Urðr. Centreret ved at tælle malede pixels frem for ud fra path-tallene, fordi en centreret kasse ikke er en centreret glyf: verdande ᚹ blæk x 53–108 midte 80.5 · y 18–131 midte 74.5 urd ð blæk x 39–110 midte 74.5 · y 15–134 midte 74.5 Verdandes +5.5 i x er dens egen bevidste forskydning — en tung stav ved siden af et let flag skal sidde til højre for midten for at se centreret ud. ð er symmetrisk nok til at undvære den, og de to er enige i y. Læsbarhed, målt: ð læses fra 32px og op. Ved 16px er det en klat, og det er ᚹ også — en favicon er ikke det, der skiller to installerede apps i en dock. `productRune` hedder nu `productMark`, fordi ð ikke er en rune. ## Determinisme SvelteKits `kit.version.name` er som standard `Date.now()`. Millisekundet landede i version.json, i index.html og i service-workeren, ændrede indholdshashen på hver chunk der refererer dem, og dermed deres filnavne. To bygninger af samme commit havde forskellige filer i sig. Dockerfilen har i et år sagt at `-trimpath` og en tom buildid "keep the output reproducible". Flagene gjorde deres arbejde; frontenden i binæren bar et tidsstempel. Sætningen var falsk, og ingen læste den. Fastgjort til VERSION, som udgivelsen allerede sender ind, og `dev` når intet sætter den. Målt ved at bygge to gange og diffe træet, før og efter. Prisen er at et lokalt `npm run build` ikke længere ligner en ny version for service-workeren — hvilket kun betyder noget på en udrullet instans, hvor udgivelsen sender det rigtige mærkat. Det gør også CI's nye påstand mulig: de to images deler alle lag, så urd bærer samme binær som verdande frem for en genbygning der tilfældigvis matcher. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b451fd585d
commit
ff988d26bb
@@ -1,40 +1,71 @@
|
||||
/**
|
||||
* Renders static/icon.svg to the PNGs the manifest and iOS need.
|
||||
* Renders each edition's icon SVG to the PNGs the manifest and iOS need.
|
||||
*
|
||||
* node scripts/generate-icons.js
|
||||
*
|
||||
* A script rather than a test, so CI never runs it and never rewrites a tracked
|
||||
* binary. Run it when the icon changes; the smoke test fetches every icon the
|
||||
* binary. Run it when an icon changes; the smoke test fetches every icon the
|
||||
* manifest names, so a reference with no file behind it fails there.
|
||||
*
|
||||
* It drives Chromium because there is no SVG renderer on this machine — which is
|
||||
* why these references were once removed rather than left pointing at files that
|
||||
* did not exist. Playwright is a renderer, and it is already a dependency.
|
||||
*
|
||||
* Two sets, because there are two editions. Both ship in the one binary and the
|
||||
* server decides which to serve at /icon.svg and friends — see
|
||||
* internal/httpapi/shell.go. The alternative, rewriting the manifest to point at
|
||||
* urd-icon-192.png, would have left `apple-touch-icon.png` behind: Safari looks
|
||||
* for that one BY NAME and ignores the manifest entirely, so a renamed path there
|
||||
* is a home-screen icon that is a screenshot of the page.
|
||||
*/
|
||||
import { chromium } from '@playwright/test';
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
|
||||
// The three sizes, and why each is here.
|
||||
const SIZES = [
|
||||
// The two the manifest asks for: 192 for a launcher, 512 for a splash screen.
|
||||
{ file: 'static/icon-192.png', size: 192 },
|
||||
{ file: 'static/icon-512.png', size: 512 },
|
||||
// And the one Safari wants by name. It ignores the manifest's icons entirely
|
||||
// and looks for this, which is why an installed PWA on iOS otherwise gets a
|
||||
// screenshot of the page as its home-screen icon.
|
||||
{ file: 'static/apple-touch-icon.png', size: 180 }
|
||||
{ suffix: '-192.png', size: 192 },
|
||||
{ suffix: '-512.png', size: 512 },
|
||||
// And the one Safari wants by name.
|
||||
{ suffix: '-apple-touch.png', size: 180 }
|
||||
];
|
||||
|
||||
// `out` is spelled out per set rather than built from the prefix, because
|
||||
// verdande's three were named before there was a second set and renaming them
|
||||
// would move the paths the manifest and app.html already point at.
|
||||
const SETS = [
|
||||
{
|
||||
source: 'static/icon.svg',
|
||||
out: {
|
||||
'-192.png': 'static/icon-192.png',
|
||||
'-512.png': 'static/icon-512.png',
|
||||
'-apple-touch.png': 'static/apple-touch-icon.png'
|
||||
}
|
||||
},
|
||||
{
|
||||
source: 'static/urd-icon.svg',
|
||||
out: {
|
||||
'-192.png': 'static/urd-icon-192.png',
|
||||
'-512.png': 'static/urd-icon-512.png',
|
||||
'-apple-touch.png': 'static/urd-apple-touch-icon.png'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const svg = readFileSync('static/icon.svg', 'utf8');
|
||||
const browser = await chromium.launch();
|
||||
const page = await browser.newPage();
|
||||
|
||||
for (const { file, size } of SIZES) {
|
||||
await page.setViewportSize({ width: size, height: size });
|
||||
await page.setContent(
|
||||
`<style>html,body{margin:0;padding:0}svg{display:block;width:${size}px;height:${size}px}</style>${svg}`
|
||||
);
|
||||
writeFileSync(file, await page.locator('svg').screenshot());
|
||||
console.log(`${file} ${size}×${size}`);
|
||||
for (const set of SETS) {
|
||||
const svg = readFileSync(set.source, 'utf8');
|
||||
for (const { suffix, size } of SIZES) {
|
||||
const file = set.out[suffix];
|
||||
await page.setViewportSize({ width: size, height: size });
|
||||
await page.setContent(
|
||||
`<style>html,body{margin:0;padding:0}svg{display:block;width:${size}px;height:${size}px}</style>${svg}`
|
||||
);
|
||||
writeFileSync(file, await page.locator('svg').screenshot());
|
||||
console.log(`${file} ${size}×${size}`);
|
||||
}
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
|
||||
Reference in New Issue
Block a user