/** * تولید تصویر پیش‌فرض مقاله‌ها — `public/assets/images/blog-default-cover.png`. * * یک‌بار اجرا می‌شود و خروجی PNG کامیت می‌گردد؛ اسکریپت می‌ماند تا اگر برند یا * ابعاد عوض شد، تصویر قابل بازتولید باشد: * * node scripts/make-blog-cover.mjs * * ابعاد ۱۲۰۰×۶۳۰ استاندارد Open Graph است و با `aspect-video` کارت‌ها هم می‌خواند. * رنگ‌ها و نشان از `public/nobat724.svg` می‌آیند تا با لوگوی سایت یکی بماند. */ import sharp from 'sharp'; import { readFileSync, writeFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname, join } from 'node:path'; const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); const W = 1200; const H = 630; const BRAND = '#5559CE'; // stroke نشان در public/nobat724.svg const ACCENT = '#FF8848'; // پلاس نارنجی همان نشان const OUT = join(ROOT, 'public/assets/images/blog-default-cover.png'); const mark = readFileSync(join(ROOT, 'public/nobat724.svg'), 'utf8') .replace(/<\?xml[^>]*\?>/, '') .replace(/width="\d+"\s+height="\d+"/, 'width="200" height="200"'); /** * چیدمان RTL: لوگو سمت راست، «اسکلتون» یک مقاله سمت چپ — کارت سفید با جای * تصویر شاخص، یک نوار عنوان و چند خط پاراگراف. همان زبان بصریِ حالت بارگذاری، * پس «مقالهٔ بدون کاور» به‌جای عکس بی‌ربط، قالبِ خالی مقاله را نشان می‌دهد. * * خط‌ها راست‌چین‌اند (RTL) و عرضشان نامنظم است تا شبیه متن واقعی باشد؛ خط آخر * کوتاه است، همان‌طور که آخرین سطر یک پاراگراف کوتاه می‌افتد. */ const CARD = { x: 132, y: 88, w: 628, h: 410, pad: 40 }; const INNER_RIGHT = CARD.x + CARD.w - CARD.pad; const INNER_W = CARD.w - CARD.pad * 2; // عرض هر سطر پاراگراف نسبت به عرض داخلی کارت const PARAGRAPH = [1, 0.94, 0.99, 0.88, 0.52]; const lines = PARAGRAPH.map((ratio, i) => { const w = Math.round(INNER_W * ratio); return { x: INNER_RIGHT - w, y: 344 + i * 26, w, h: 14 }; }); const svg = ` ${lines .map( (b) => `` ) .join('\n ')} ${mark} `; // density بالا برای متن تیز، سپس resize به ابعاد دقیق OG. const png = await sharp(Buffer.from(svg), { density: 144 }).resize(W, H).png().toBuffer(); writeFileSync(OUT, png); const meta = await sharp(png).metadata(); console.log(`✓ ${OUT} — ${meta.width}×${meta.height}, ${(png.length / 1024).toFixed(0)}KB`);