The favicon service was wrong as often as it was right: a sign-in page's icon for anything behind a login, nothing at all for a private host, and both answers cached past any way of asking again. Refreshing could not fix it, because the staleness was not local. Now every mark is. Each app is its brand glyph in white on a round tile, coloured from Tailwind's 500s by hashing the host - you find things by their colour, so one that moved every launch would be worse than none. A host with no glyph gets its initial in the same tile. Matching tries the registrable name first, since a self-hosted tool is nearly always on a subdomain of its vendor - aputure.odoo.com is Odoo, not Aputure. A short table covers what a domain cannot answer, which is most of Google. The build reduces Simple Icons' 15MB of SVGs to one 4.5MB map in public/, fetched once rather than parsed into the bundle at every start; the bundle stays at 233KB. It is generated on every build, so never committed and never stale. Traffic lights are offset to sit inside the window margin rather than crowding its edge.
32 lines
1.2 KiB
JavaScript
32 lines
1.2 KiB
JavaScript
/**
|
|
* Turns Simple Icons' 3,400 SVG files into one map of slug → path data.
|
|
*
|
|
* The package ships a file per icon and 15MB of SVG wrapper around what is
|
|
* really a single `d` attribute each. This keeps the attribute and throws the
|
|
* rest away, so the app carries about a megabyte instead of fifteen.
|
|
*
|
|
* The output goes to public/ rather than src/: it is fetched once at runtime
|
|
* instead of being parsed into the JS bundle at every startup.
|
|
*
|
|
* Run `npm run icons` after upgrading simple-icons. The output is committed,
|
|
* so a build never depends on this having been run.
|
|
*/
|
|
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
|
|
const SRC = "node_modules/simple-icons/icons";
|
|
const OUT = "public/brand-icons.json";
|
|
|
|
const icons = {};
|
|
for (const file of readdirSync(SRC)) {
|
|
if (!file.endsWith(".svg")) continue;
|
|
const svg = readFileSync(join(SRC, file), "utf8");
|
|
const match = /\sd="([^"]+)"/.exec(svg);
|
|
if (!match) continue;
|
|
icons[file.slice(0, -4)] = match[1];
|
|
}
|
|
|
|
writeFileSync(OUT, JSON.stringify(icons));
|
|
const kb = Math.round(readFileSync(OUT).length / 1024);
|
|
console.log(`${Object.keys(icons).length} icons → ${OUT} (${kb} KB)`);
|