Deploy / build (push) Successful in 1m4s
Measured against the live site and the cron outputs today, not carried over:
- A1 (author photo) and A3 (featured strip) were still ⬜ although both shipped
(B4 and 6e9d58b) — marked done with the evidence, /start-here left as a
deliberate deferral.
- E3 is fully automated: the LinkedIn half was already a daily no_agent cron
(id 35f8985f609a). 5 posts published as of today's 12:30 tick; its silence is
not proof of work, so the output files are the record.
- E6 closed: HSTS waived by the owner ("无视吧"), re-measured still max-age=0.
Do not re-open.
- E1: CF Web Analytics re-measured absent (0 cloudflareinsights hits, one Umami
tag). It is an owner dashboard action — no token here can enable it and CF
injects the beacon itself, so there is no repo change to make.
- B6 now states the real shape of the artwork work: 0 of 72 posts rolled out,
the live site still serves the v1 images, the 3 consultant questions are
unanswered, the rendered OG card is measured at 558 KB PNG vs 84 KB JPEG q85,
and the promised OCR pseudo-text gate does not exist yet.
88 lines
3.7 KiB
Markdown
88 lines
3.7 KiB
Markdown
# og-gen — per-post OG image generator
|
||
|
||
Generates a branded 1200×630 social-share image for a blog post, matching the
|
||
site's cobalt-blue `#295cff` branding, the `logo-square.png` mark, and the
|
||
"terminal / debugging" visual style.
|
||
|
||
## Usage
|
||
|
||
```bash
|
||
node scripts/og-gen/generate.mjs <post-slug>
|
||
```
|
||
|
||
Reads frontmatter (`title`, `category`, `tags`) from
|
||
`src/content/posts/<slug>.md`, fills `template.html`, renders with headless
|
||
Chrome, and writes `public/og/<slug>.png`.
|
||
|
||
The post's frontmatter needs `ogImage: /og/<slug>.png` so the blog serves it
|
||
(layout already renders `og:image` + width/height; PostList shows it as a
|
||
card thumbnail).
|
||
|
||
## Customizing the terminal box
|
||
|
||
Each post shows a "terminal" panel (the red error / green fix lines). Add an
|
||
entry keyed by slug in `TERMINALS` inside `generate.mjs`, using these tokens:
|
||
|
||
```html
|
||
<div class="line"><span class="prompt">$</span><span class="cmd">command</span></div>
|
||
<div class="line"><span class="prompt"> </span><span class="err">error line</span></div>
|
||
<div class="line"><span class="prompt">$</span><span class="cmd">fix</span><span class="fix">→ done ✓</span></div>
|
||
```
|
||
|
||
No entry → a generic default terminal.
|
||
|
||
## Requirements
|
||
|
||
- Google Chrome at `C:\Program Files\Google\Chrome\Application\chrome.exe`
|
||
(override with `CHROME_PATH` env var).
|
||
- Node 20+.
|
||
|
||
## Notes
|
||
|
||
- PNG (not JPG) is intentional: for flat UI/terminal art, PNG is smaller and
|
||
sharper than JPG (see commit history).
|
||
- The `logo-square.png` referenced lives at `public/logo-square.png`.
|
||
|
||
---
|
||
|
||
## v2 pipeline ("one art, four targets") — `render_from_art.mjs`
|
||
|
||
The consultant-approved redesign replaces the generated terminal panels with **one
|
||
zero-text base-art frame per post**, composited into four assets:
|
||
|
||
| Target | Path | Size |
|
||
|---|---|---|
|
||
| hero banner | `public/banners/<slug>.webp` (+ `.jpg` fallback from frontmatter) | 1600×900 |
|
||
| social card | `public/og/<slug>.jpg` (EN), `public/og/zh-<slug>.jpg` (ZH) | 1200×630 |
|
||
| bento/list thumb | `public/og/thumb/<slug>-800.webp` | 800×420 |
|
||
| small thumb | `public/og/thumb/<slug>-400.webp` | 400×210 |
|
||
|
||
The card is `template-v2.2.html` (frosted bento + blueprint grid + full-width evidence
|
||
bar) filled from the post's own frontmatter plus `art-meta.json`; the self-fitting
|
||
headline script reports `FIT <n>px` through `<title>`, and a render that does not
|
||
settle fails instead of shipping a silently-shrunken card.
|
||
|
||
```bash
|
||
# metadata first: art path (default <art-dir>/<slug>.png) + a HAND-WRITTEN evidence line
|
||
$EDITOR scripts/og-gen/art-meta.json
|
||
|
||
node scripts/og-gen/render_from_art.mjs --only <slug>[,<slug>] --out /tmp/og-check
|
||
node scripts/og-gen/render_from_art.mjs --all # writes into public/
|
||
```
|
||
|
||
- `--format jpg` (default) or `png`. **JPEG is the default because it is measured**:
|
||
a composited card renders 558 KB as PNG and lands at **84 KB at mozjpeg q85**
|
||
(256-colour PNG = 240 KB). The default site card already ships as JPG for the
|
||
same reason.
|
||
- `evidence` is **required and never derived** — it is editorial text, and inventing
|
||
one from the description is the "invented specificity" failure the blog avoids.
|
||
A missing entry aborts with the slug list.
|
||
- `ogTitle_en` / `ogTitle_zh` shorten only the card's headline (the article keeps
|
||
its real title) — the consultant's answer to the English headline-rag problem.
|
||
- Reproducibility check: re-rendering the two sample posts produced banners and
|
||
800px thumbs **byte-identical** to the consultant's accepted delivery
|
||
(sha256 16e6a7b0…, f3b29f38…, c4e28291…, 8095ec6c…).
|
||
- ⚠ Changing `ogImage` from `.png` to `.jpg` also needs `PostList.astro`'s thumb
|
||
helper updated (`/\.(png|jpe?g)$/`), or the listing derives
|
||
`…-800.webp` from `slug.jpg` and every thumb 404s.
|