// Build-time OG images: 1200x630 dark cards matching the site design, // one per note (plus /og/index.png for the homepage). satori renders an // element tree to SVG, resvg rasterizes to PNG. Base.astro points // og:image here via the ogSlug prop. import fs from 'node:fs'; import type { APIRoute } from 'astro'; import { getCollection } from 'astro:content'; import satori from 'satori'; import { Resvg } from '@resvg/resvg-js'; import { slugForId } from '../../lib/quartz-compat.mjs'; const FONT_DIR = 'node_modules/@fontsource/jetbrains-mono/files'; const mono400 = fs.readFileSync(`${FONT_DIR}/jetbrains-mono-latin-400-normal.woff`); const mono800 = fs.readFileSync(`${FONT_DIR}/jetbrains-mono-latin-800-normal.woff`); export async function getStaticPaths() { const entries = await getCollection('garden', (e: any) => !e.data.draft); return entries.map((e: any) => { const isIndex = e.id === 'index'; const title = isIndex ? 'Wesley Ray' : (e.data.title ?? e.id.split('/').pop()); const dir = e.id.includes('/') ? e.id.slice(0, e.id.lastIndexOf('/')) : ''; return { params: { slug: isIndex ? 'index' : slugForId(e.id) }, props: { title, dir, subtitle: isIndex ? 'projects, notes, and experiments' : dir, }, }; }); } export const GET: APIRoute = async ({ props }) => { const { title, subtitle } = props as { title: string; subtitle: string }; const size = title.length > 55 ? 44 : title.length > 32 ? 56 : 68; const svg = await satori( { type: 'div', props: { style: { width: '100%', height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', backgroundColor: '#0b0d12', borderLeft: '14px solid #218107', padding: '64px 72px', fontFamily: 'JetBrains Mono', color: '#ffffff', }, children: [ { type: 'div', props: { style: { display: 'flex', fontSize: '26px', color: 'rgba(255,255,255,0.64)', letterSpacing: '2px', }, children: 'the garden // Wesley Ray', }, }, { type: 'div', props: { style: { display: 'flex', fontSize: `${size}px`, fontWeight: 800, lineHeight: 1.25, letterSpacing: '-1px', }, children: title, }, }, { type: 'div', props: { style: { display: 'flex', justifyContent: 'space-between', fontSize: '26px', }, children: [ { type: 'div', props: { style: { display: 'flex', color: 'rgba(255,255,255,0.64)', }, children: subtitle || ' ', }, }, { type: 'div', props: { style: { display: 'flex', color: '#2fa50f' }, children: 'garden.c0smere.net', }, }, ], }, }, ], }, }, { width: 1200, height: 630, fonts: [ { name: 'JetBrains Mono', data: mono400, weight: 400, style: 'normal' }, { name: 'JetBrains Mono', data: mono800, weight: 800, style: 'normal' }, ], }, ); const png = new Resvg(svg, { fitTo: { mode: 'width', value: 1200 }, }).render().asPng(); return new Response(png, { headers: { 'Content-Type': 'image/png' } }); };