fix(library): generate every post cover from one template - #5980
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR SummaryLow Risk Overview The script uses Root devDependencies add Reviewed by Cursor Bugbot for commit cbee086. Configure here. |
Greptile SummaryThis PR standardizes library cover generation and validation.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| scripts/generate-library-covers.tsx | Adds deterministic generation and visual synchronization checks for every conventionally structured library cover. |
| package.json | Exposes generation and check commands and adds the required rendering dependencies. |
| scripts/tsconfig.json | Enables JSX compilation and includes TSX scripts. |
| bun.lock | Locks the newly added cover-generation dependencies and their transitive packages. |
Reviews (5): Last reviewed commit: "fix(library): parse frontmatter with gra..." | Re-trigger Greptile
013425f to
cf94236
Compare
|
@cursor review |
|
@cursor review |
|
@cursor review |
Three posts shipped an `ogImage` pointing at a file that was never committed, so the library index rendered broken images and their `og:image`, JSON-LD, and sitemap entries all 404'd. Several others were authored without the brand font loaded or with the title clipping off the bottom edge. Covers were hand-made per post with no generator, which is why they drifted. Adds `bun run library:covers`, rendering each cover from the post's frontmatter title using the reference template already encoded in the docs OG route, and regenerates all 20 so the grid is uniform. Line widths come from the font's real advance metrics rather than an average-glyph-width estimate: the template joins words with non-breaking spaces to dodge a Satori space-measurement bug, which leaves hyphens as the only fallback break points, so an under-measured line breaks mid-compound. Also drops six orphaned `cover.png` sources left over from the JPEG compression pass in #5528.
Covers are derived artifacts, so skipping outputs that already exist left an image showing the old title after a post's frontmatter `title` changed. Every run now re-renders from scratch; rendering is deterministic, so an unchanged title re-encodes to identical bytes and a full run stays a no-op in git. Replaces `--force` (now the default) with `--check`, which renders in memory and compares against the committed bytes without writing, so CI can catch both a stale cover and the missing-cover case that caused the original breakage.
Byte-equality on the mozjpeg output assumed portable encoder bytes. libvips/mozjpeg does not guarantee that across OS and CPU, so identical input can encode differently on a contributor's machine or a Linux CI runner and fail the check for no real reason — exactly where the check was meant to run. Decodes both images to greyscale and compares mean absolute difference instead, which discards encoder variance while still testing what the check is about. Measured on this cover set: re-encoding an identical render with a deliberately different encoder moves it ~0.26, a one-word title change moves it ~12; the threshold of 2 sits between them with ~8x margin.
Averaging the difference diluted a local edit across all 810,000 pixels. Changing a title's "2026" to "2027" moved the mean by 0.42 — under the tolerance that absorbed encoder noise — so the check passed a cover still showing the old year. Counts pixels that moved more than 48 greyscale levels instead. Measured on this cover set, that one-character edit redraws 2,559 pixels while three deliberately different encodes of an identical render (quality 60/70 without mozjpeg, quality 95 with) redraw none, so the count separates real drift from encoder variance in both directions.
…tokens Two issues in the cover generator, neither reachable from a current title. The hand-rolled frontmatter regex could disagree with `gray-matter`, which is what renders the page and its `og:title`. On a double-quoted escape or a block scalar the cover would have rendered a title the page never shows, with `--check` calling it in sync. Uses `gray-matter` directly so there is one parser. `wrapTitleLines` only breaks between space-separated words, so a token wider than the title box on its own stayed on an overflowing line, and the non-breaking spaces left Satori no recourse but to break it at a hyphen — the mid-compound break this layout exists to prevent. Oversized tokens now split here, at hyphens first and per-character only for something like a URL, and a font size is accepted only if every line measures within the box. All 20 covers re-render byte-identically, so neither change alters current output.
94aa82a to
cbee086
Compare
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit cbee086. Configure here.
Summary
mcp-security,ai-agents-in-procurement,ai-agent-observability) shipped anogImagepointing at a file that was never committed — broken images on the library index, plus 404s inog:image, JSON-LD, and the image sitemapbun run library:covers, which renders each cover from the post's frontmatter title using the reference template already encoded inapps/docs/app/api/og/route.tsxbun run library:covers:checkso CI can catch a missing or stale cover instead of shipping a 404cover.pngsources (3.9 MB) left over from the JPEG compression pass in fix(landing): revert unoptimized removal, compress source images instead #5528Page weight
These render with
unoptimized(per #5528, the runtime optimizer was breaking/blogon staging), so the committed bytes ship verbatim and file size is page weight directly. The generator uses mozjpeg quality 82 — the exact setting #5528 established, now applied automatically instead of by hand.−266 KB (−33%) while serving three more images. Above the fold (the 3
prioritycards) is 68.0 → 73.9 KB, the +5.9 KB being entirelymcp-securitynow actually loading. Quality measured at 54.9 dB PSNR — visually lossless; resolution unchanged at 1200×675.Notes
opentype.js, not an average-glyph-width estimate. The template joins words with non-breaking spaces to dodge a Satori space-measurement bug, which leaves hyphens as the only fallback break points — an under-measured line breaks mid-compound (the BYOK title was rendering asBYOK Multi-/Model AI Agent)what-is-an-mcp-serverloses a bespoke diagram — it was the only cover with artwork and it was in the fallback font. Happy to restore that one file if we'd rather keep itapache-2-0-vs-fair-code). If we want an escape hatch, an optionalcoverTitlefrontmatter field is the clean wayFollow-up (not in this PR)
apps/sim/lib/content/schema.ts:30still says covers are "rendered vianext/imagewithoutunoptimized" — stale since fix(landing): revert unoptimized removal, compress source images instead #5528unoptimizedmeanssizes='140px'generates no srcset. Fixing it needs either the optimizer back (blocked on fix(landing): revert unoptimized removal, compress source images instead #5528's unresolved root cause) or a second derivative per postlibrary:covers:checkis not yet wired into a CI workflowType of Change
Testing
Tested manually — regenerated all 20 covers and reviewed them as a contact sheet; verified deterministic output across runs; verified
--checkfails on an edited frontmatter title and passes once regenerated.Checklist