Vertically center all content (badge, title, description, avatar) to
eliminate dead space. Move accent bar to top, inline badge+date row,
site name bottom-right. Bump DESIGN_VERSION to 3 for full regeneration.
Confab-Link: http://localhost:8080/sessions/5565387e-4eb5-4441-89fb-2c6347de8e0c
- Light background, clean typography hierarchy, avatar, metadata row, accent bar
- extractBodyText → extractFirstParagraph (stops at first paragraph break)
- Articles with fm.title get body text as description; notes show first paragraph as title
- DESIGN_VERSION bump forces full regeneration without manual cache clearing
- sanitize() strips non-renderable chars to prevent Satori NO GLYPH artifacts
Confab-Link: http://localhost:8080/sessions/5565387e-4eb5-4441-89fb-2c6347de8e0c
extractBodyText() was too naive - markdown tables (|...|), heading anchors
({#id}), list numbering, and HTML tags leaked into OG image titles for
notes without explicit titles. Characters outside Inter font coverage
caused Satori to render "NO GLYPH" vertically on the card.
Confab-Link: http://localhost:8080/sessions/5565387e-4eb5-4441-89fb-2c6347de8e0c
Full OG regeneration (2,350 images) OOM-kills when the Eleventy watcher
is running (~1.8 GB RSS), leaving only ~1.2 GB headroom in the 3 GB
container. WASM native memory from Satori/Resvg grows beyond what GC
can reclaim within a single process.
Solution: spawn og-cli in batches of 100 images. Each invocation exits
after its batch, fully releasing all WASM native memory. Exit code 2
signals "more work remains" and the spawner re-loops. Peak memory per
batch stays under ~500 MB regardless of total image count.
Also seed newManifest from existing manifest so unscanned entries survive
batch writes.
Confab-Link: http://localhost:8080/sessions/edb1b7b0-da66-4486-bd9c-d1cfa7553b88
Full OG regeneration (2,350 images) was OOM-killed because WASM native
memory from Satori/Resvg accumulated between GC cycles. The previous
GC interval of 50 images allowed ~2.5-5 GB of native allocations before
reclamation. Reduce to every 5 images to keep peak RSS under ~400 MB.
Also reduce --max-old-space-size from 768 to 512 MB (V8 heap only uses
~22 MB) and add peak RSS tracking to the completion log.
Confab-Link: http://localhost:8080/sessions/edb1b7b0-da66-4486-bd9c-d1cfa7553b88
Satori (Yoga WASM) and Resvg (Rust WASM) allocate native memory that
V8 doesn't track against the heap limit. Without periodic GC, the JS
wrappers accumulate and native RSS grows to ~2 GB during OG image
generation for 3400+ posts.
- Add --expose-gc to og-cli spawn
- Call global.gc() every 50 images to reclaim native memory
- Log final RSS/heap for monitoring
Confab-Link: http://localhost:8080/sessions/edb1b7b0-da66-4486-bd9c-d1cfa7553b88
The Nunjucks race condition in Eleventy 3.x affects page.url too —
its value changes between {% set %} and {{ }} within the same
template render during parallel builds. Instead of trying to derive
slugs from page data, name OG images with the full filename
(including date prefix) to match URL path segments exactly.
Eleventy v3 parses YYYY-MM-DD- from filenames and removes it from
page.fileSlug. The OG generator was using the full filename (with
date prefix) causing a slug mismatch — hasOgImage filter checked
for 'slug.png' while the file was 'YYYY-MM-DD-slug.png'.
Also removes debug logging from hasOgImage filter.
The OG generation in the eleventy.before hook consumed too much memory
alongside Eleventy's data cascade, causing the Eleventy process to be
OOM-killed on Cloudron. Fix by running OG generation in a separate
child process with its own 768MB heap limit. Also write the manifest
incrementally (every 10 images) to preserve progress if interrupted.
Uses Satori + @resvg/resvg-js to create branded 1200x630 social
preview cards at build time. Cards show post title, type badge,
date, and site name on a dark background with blue accent.
Generated images are cached in .cache/og/ (persistent on Cloudron)
and passthrough-copied to the output. Posts with photos continue
using their own images. Untitled posts (notes) use body text.