import { readFile, writeFile, mkdir } from 'node:fs/promises'; import { fileURLToPath } from 'node:url'; const root = new URL('./', import.meta.url); const icons = JSON.parse(await readFile(new URL('icons.json', root), 'utf8')); const bodies = new Map(); for (const item of icons) { const source = await readFile(new URL(`upstream/${item.icon}.svg`, root), 'utf8'); bodies.set(item.id, source.match(/]*>([\s\S]*?)<\/svg>/)[1].trim()); } const glyph = (item, x, y, size) => `${bodies.get(item.id)}`; // All seven use the same 24-unit Lucide canvas, occupying 70% of the tile. const tile = (item, x, y, size, radius = 0) => `${glyph(item, x + size * .15, y + size * .15, size * .7)}`; const svg = (width, height, content, title) => `${title}${content}\n`; await mkdir(new URL('svg/', root), { recursive: true }); for (const item of icons) { await writeFile(new URL(`svg/${item.id}.svg`, root), svg(1024, 1024, tile(item, 0, 0, 1024), item.name)); } let sheet = `OficinaOne family. Seven marks. White Lucide symbols on solid colour.`; for (let i = 0; i < icons.length; i++) { const item = icons[i]; const x = 64 + i * 184; sheet += tile(item, x, 165, 144, 30); sheet += `${item.name}${item.icon}${item.colour}`; for (const [size, y] of [[48, 450], [32, 525], [16, 588]]) sheet += tile(item, x, y, size, size * .21); } sheet += `Actual sizes above: 48, 32 and 16 px. Rounded corners are a preview mask only.Square, opaque masters · Shared padding · 2-unit strokes · No gradients or shadows`; await writeFile(new URL('preview.svg', root), svg(1400, 790, sheet, 'Oficina app icon set')); console.log('Generated seven SVG masters and preview.svg'); if (process.argv.includes('--render')) { const { chromium } = await import('playwright'); const browser = await chromium.launch({ headless: true }); try { const page = await browser.newPage({ viewport: { width: 1400, height: 790 }, deviceScaleFactor: 1 }); await page.goto(new URL('preview.svg', root).href); await page.screenshot({ path: fileURLToPath(new URL('preview.png', root)) }); await mkdir(new URL('png/', root), { recursive: true }); for (const item of icons) { const source = await readFile(new URL(`svg/${item.id}.svg`, root), 'utf8'); for (const size of [16, 32, 48, 180, 192, 512, 1024]) { const png = await page.evaluate(async ({ source, size }) => { const img = new Image(); img.src = 'data:image/svg+xml;base64,' + btoa(source); await img.decode(); const canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas'); canvas.width = canvas.height = size; canvas.getContext('2d', { alpha: false }).drawImage(img, 0, 0, size, size); return canvas.toDataURL('image/png').split(',')[1]; }, { source, size }); await writeFile(new URL(`png/${item.id}-${size}.png`, root), Buffer.from(png, 'base64')); } } console.log('Rendered preview.png and seven PNG sizes per app'); } finally { await browser.close(); } }