Files
hoelee-blog/scripts/og-gen/README.md
T
hoelee 2060aaa166
Deploy / build (push) Successful in 1m4s
docs(project-state): truth-up four stale statuses, record the batch blockers
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.
2026-10-18 16:33:21 +08:00

88 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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">&nbsp;</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.