Something went wrong. Try again.
Browser extension: detect and subscribe to standard.site publications on ATProto
Something went wrong. Try again.
3.2 kB · 78 lines
JavaScript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879// Copies the public popup captures (scripts/shots.mjs) into the frontpage's// static assets and writes the manifest the homepage renders from.//// The site serves the captures themselves rather than the composed store// tiles: the tilt, the ink border and the offset shadow are the site's own// CSS (.shot in web/src/styles/global.css), so the gallery follows the theme// into dark mode instead of carrying a baked-in paper background, and the// hero's lockup is not repeated a few hundred pixels below itself.//// The manifest carries each capture's real pixel size, read here rather than// guessed, so the page can reserve the right box before the image lands and// a re-capture at a different popup width cannot silently reflow the page.//// Usage: node scripts/sync-shots.mjs (also: npm run shots)
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs'import { join, resolve } from 'node:path'import { pathToFileURL } from 'node:url'
import { pngSize } from './render-icons.mjs'import { MANIFEST, SHOTS, SITE_DIR, SITE_URL_PREFIX, capturePath } from './shots.mjs'
const REPO_ROOT = resolve(import.meta.dirname, '..')
/** * Sync the captures into the site and rewrite the manifest. * * @param {object} [options] * @param {string} [options.root] repo root to work against (tests use a copy) * @param {Shot[]} [options.shots] * @returns {{ manifest: object[], removed: string[] }} */export function syncShots({ root = REPO_ROOT, shots = SHOTS } = {}) { const siteDir = join(root, SITE_DIR) mkdirSync(siteDir, { recursive: true })
const manifest = shots.map((shot) => { const capture = join(root, capturePath(shot)) if (!existsSync(capture)) { throw new Error( `${capturePath(shot)} does not exist — run \`node scripts/capture-status-docs.mjs\``, ) } const png = readFileSync(capture) const { width, height } = pngSize(png) writeFileSync(join(siteDir, `${shot.name}.png`), png) return { name: shot.name, src: `${SITE_URL_PREFIX}/${shot.name}.png`, width, height, alt: shot.alt, caption: shot.caption, } })
// A shot dropped from the list would otherwise stay on the site forever: // the page would stop rendering it, but the deploy would keep uploading it. const kept = new Set(manifest.map((entry) => `${entry.name}.png`)) const removed = readdirSync(siteDir).filter((file) => !kept.has(file)) for (const file of removed) rmSync(join(siteDir, file), { recursive: true, force: true })
writeFileSync(join(root, MANIFEST), `${JSON.stringify(manifest, null, 2)}\n`) return { manifest, removed }}
function main() { const { manifest, removed } = syncShots() for (const entry of manifest) { console.log(`shots: ${entry.name}.png ${entry.width}x${entry.height} -> ${SITE_DIR}/`) } for (const file of removed) console.log(`shots: removed stale ${SITE_DIR}/${file}`) console.log(`shots: wrote ${manifest.length} entr${manifest.length === 1 ? 'y' : 'ies'} to ${MANIFEST}`)}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main()}