npm run art renders the standalone SVGs and the 1200 by 630 card through Sharp, a development dependency only.
57 lines
3.8 KiB
JavaScript
57 lines
3.8 KiB
JavaScript
// Run with npm run art. Both illustrations originate in the live board component.
|
||
import { readFile, writeFile, unlink } from 'node:fs/promises';
|
||
import { compile } from 'svelte/compiler';
|
||
import { render } from 'svelte/server';
|
||
import sharp from 'sharp';
|
||
import { createGame } from '../src/lib/game/index.ts';
|
||
const root = new URL('../', import.meta.url);
|
||
const source = await readFile(new URL('src/lib/components/BoardArtwork.svelte', root), 'utf8');
|
||
const compiled = compile(source.replace("'$lib/game'", "'../src/lib/game/index.ts'"), { generate: 'server', css: 'injected', filename: 'BoardArtwork.svelte' });
|
||
const temporary = new URL(`.board-art-${process.pid}.mjs`, import.meta.url);
|
||
try {
|
||
await writeFile(temporary, compiled.js.code);
|
||
const { default: BoardArtwork } = await import(temporary.href);
|
||
for (const set of ['copenhagen', 'tablut']) {
|
||
const result = render(BoardArtwork, { props: { g: createGame({ a: 'Defenders', b: 'Attackers' }, 0, undefined, { set }), decorative: true } });
|
||
let svg = result.body.replace(/<!--[\s\S]*?-->/g, '');
|
||
const css = [...result.head.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/g)].map(m => m[1]).join('\n');
|
||
svg = svg.replace(/(<svg[^>]*>)/, '$1<style>' + css + '</style>');
|
||
// Resolve custom properties for standalone SVG rasterizers, which do not
|
||
// consistently implement CSS variable inheritance. Browsers use the component.
|
||
const paletteSource = source.slice(source.indexOf('/* Copenhagen:'));
|
||
const base = paletteSource.slice(0, paletteSource.indexOf('/* Tablut:'));
|
||
const overrides = set === 'tablut' ? paletteSource.slice(paletteSource.indexOf('/* Tablut:'), paletteSource.indexOf('\n\t.artwork {')) : '';
|
||
const values = Object.fromEntries([...`${base}\n${overrides}`.matchAll(/(--[\w-]+):\s*([^;]+);/g)].map(m => [m[1], m[2]]));
|
||
values['--piece-rim'] = values['--defender-rim'];
|
||
values['--font'] = 'Georgia, serif';
|
||
values['--frost'] = '#9ccbdd';
|
||
svg = svg.replace(/:where\((\.[\w-]+)\)/g, '$1');
|
||
svg = svg.replace(/var\((--[\w-]+)\)/g, (_, key) => values[key] ?? 'inherit');
|
||
svg = svg.replace('</style>', ['attacker', 'defender', 'king'].map(kind => `.piece.${kind} .base{fill:${values[`--${kind}-rim`]}}.piece.${kind} .body,.piece.${kind} .turned{stroke:${values[`--${kind}-rim`]}}`).join('') + '</style>');
|
||
await writeFile(new URL(`static/board-${set}.svg`, root), svg);
|
||
}
|
||
const board = await readFile(new URL('static/board-copenhagen.svg', root), 'utf8');
|
||
const image = Buffer.from(board).toString('base64');
|
||
const share = `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630">
|
||
<rect width="1200" height="630" fill="#14181a"/>
|
||
<image x="62" y="80" width="470" height="470" href="data:image/svg+xml;base64,${image}"/>
|
||
<g font-family="Georgia, serif">
|
||
<text x="590" y="180" font-size="82" fill="#e6dfcf">Hnefatafl</text>
|
||
<text x="594" y="246" font-size="30" fill="#e6dfcf">The Viking board game.</text>
|
||
<text x="594" y="294" font-size="27" fill="#aeb3ab">
|
||
<tspan x="594">One king, one throne, four corners,</tspan>
|
||
<tspan x="594" dy="38">and a ring of attackers closing in.</tspan>
|
||
<tspan x="594" dy="50">Play a housecarl or a friend.</tspan>
|
||
<tspan x="594" dy="38">Copenhagen or Linnaeus’s Tablut.</tspan>
|
||
</text>
|
||
<text x="594" y="491" font-size="24" fill="#c5ae7b">tafl.kestrelsnest.social</text>
|
||
<text x="594" y="526" font-size="22" fill="#9ea59e">Free · no accounts</text>
|
||
</g>
|
||
</svg>`;
|
||
await writeFile(new URL('docs/og-card.svg', root), share);
|
||
await sharp(Buffer.from(share)).png().toFile(new URL('static/og.png', root).pathname);
|
||
console.log('Generated both board illustrations and static/og.png (1200 × 630).');
|
||
} finally {
|
||
await unlink(temporary).catch(() => {});
|
||
}
|