import fs from 'fs'; import path from 'path'; import puppeteer from 'puppeteer'; /** * Gera todos os ativos de marca a partir da geometria do logo: * favicons (transparentes), ícones de app (tile obsidiana), site.webmanifest * e a imagem de compartilhamento (Open Graph, 1200x630). * * Cores = tokens de app/globals.css. Uso: npm run favicons * (usa o Chrome do Puppeteer; defina CHROME_PATH para usar outro binário) */ const COR = { obsidiana: "#07090d", tealEscuro: "#2dd4bf", cobreEscuro: "#ffb39b", tealMedio: "#1a8f83", // legível em aba clara e escura cobreMedio: "#c8694e", texto: "#f4f6f8", suave: "#a6afbd", }; const marca = (teal, cobre) => ` `; function createIco(pngBuffers) { // pngBuffers: array of { width, height, buffer } const count = pngBuffers.length; const headerSize = 6; const dirEntrySize = 16; let offset = headerSize + dirEntrySize * count; const header = Buffer.alloc(headerSize); header.writeUInt16LE(0, 0); // reserved header.writeUInt16LE(1, 2); // ICO type header.writeUInt16LE(count, 4); // count const entries = []; for (const img of pngBuffers) { const entry = Buffer.alloc(dirEntrySize); entry.writeUInt8(img.width >= 256 ? 0 : img.width, 0); entry.writeUInt8(img.height >= 256 ? 0 : img.height, 1); entry.writeUInt8(0, 2); // colors entry.writeUInt8(0, 3); // reserved entry.writeUInt16LE(1, 4); // planes entry.writeUInt16LE(32, 6); // bpp entry.writeUInt32LE(img.buffer.length, 8); // size entry.writeUInt32LE(offset, 12); // offset entries.push(entry); offset += img.buffer.length; } return Buffer.concat([header, ...entries, ...pngBuffers.map(b => b.buffer)]); } const pagina = (corpo, css = "") => `
${corpo}`; async function run() { const browser = await puppeteer.launch({ executablePath: process.env.CHROME_PATH || undefined, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); // 1. Favicons: marca sozinha, fundo transparente. O logo ocupa x 2..55; centraliza. const favicon = pagina(`