/** * Writes the light and foil versions of the motif SVGs from the dark originals * in src/assets/motifs, using the inks Greg picked on the light motif board * (2026-10-18), and the light GK monogram in src/assets/brand/light. Run after * a motif or the monogram changes: node packages/ui/scripts/light-motifs.mjs * * light/ deep gold line: the dark inks swapped for Starlight-page inks, glow removed * foil/ the dark seal with its gold as foil, glow kept * foil-light/ the light seal with its gold as foil */ import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; const dir = join(import.meta.dirname, "../src/assets/motifs"); // Dark colors as they appear in the motif files, and their light inks. const LIGHT = { "#C9A24A": "#8A6516", "#14A39A": "#0F8C84", "#0E1522": "#F4F2ED", "#101A2A": "#ECE8DF", "#F4F2ED": "#131C2B", "#F2D58A": "#A57C1F", "#F6E7B8": "#8A6516", "#2BC2B7": "#0F8C84", "#9A70F5": "#6A2FE0", "#FFF": "#F4F2ED", }; const GOLD = ["#C9A24A", "#F2D58A"]; const stops = (s) => s.map(([o, c]) => ``).join(""); const DARK_FOIL = stops([[0, "#8A6516"], [0.3, "#C9A24A"], [0.5, "#F6E7B8"], [0.68, "#C9A24A"], [1, "#8A6516"]]); const LIGHT_FOIL = stops([[0, "#6F5112"], [0.35, "#A57C1F"], [0.5, "#C9A24A"], [0.65, "#8A6516"], [1, "#6F5112"]]); const hex = (c) => new RegExp(c.replace("#", "#") + "(?![0-9A-Fa-f])", "gi"); function recolor(svg, map) { // One pass, so a swapped color is never swapped again. const keys = Object.keys(map).sort((a, b) => b.length - a.length); const any = new RegExp(`(${keys.join("|")})(?![0-9A-Fa-f])`, "gi"); return svg.replace(any, (m) => map[keys.find((k) => k.toLowerCase() === m.toLowerCase())]); } function foil(svg, gradStops, golds) { const [, , , w, h] = svg.match(/viewBox="([-\d.]+) ([-\d.]+) ([\d.]+) ([\d.]+)"/) ?? [0, 0, 0, 400, 400]; const grad = `${gradStops}`; for (const g of golds) svg = svg.replace(hex(g), "url(#gkfoil)"); return svg.includes("") ? svg.replace("", `${grad}`) : svg.replace(/(]*>)/, `$1${grad}`); } // Glow filters are for the night sky only. const unglow = (svg) => svg.replace(/\sfilter="url\(#[^)]*\)"/g, ""); for (const sub of ["light", "foil", "foil-light"]) mkdirSync(join(dir, sub), { recursive: true }); const files = readdirSync(dir).filter((f) => f.endsWith(".svg")); for (const f of files) { const dark = readFileSync(join(dir, f), "utf8"); const light = recolor(unglow(dark), LIGHT); writeFileSync(join(dir, "light", f), light); // Only the seals stand alone large enough for foil. if (/seal|astrolabe|transmutation/.test(f) && !f.startsWith("mini-") && !f.startsWith("filament-banner")) { writeFileSync(join(dir, "foil", f), foil(dark, DARK_FOIL, ["#C9A24A"])); writeFileSync(join(dir, "foil-light", f), foil(light, LIGHT_FOIL, ["#8A6516", "#A57C1F"])); } } console.log(`${files.length} motifs written`); // The GK monogram on Starlight: a pale disc, deep gold rings and ink letters. const MONOGRAM = { ...LIGHT, "#131C2B": "#FFFFFF", "#1F3B49": "#ECE6D8", "#15232F": "#FBF9F4", }; const brand = join(dir, "../brand"); mkdirSync(join(brand, "light"), { recursive: true }); writeFileSync(join(brand, "light", "gk-monogram.svg"), recolor(readFileSync(join(brand, "gk-monogram.svg"), "utf8"), MONOGRAM)); console.log("monogram written");