Skip to content

Add cover images for six articles missing them - #25

Merged
juntao merged 6 commits into
second-state:mainfrom
ethanwestfall:images/add-missing-blog-covers
Aug 2, 2026
Merged

Add cover images for six articles missing them#25
juntao merged 6 commits into
second-state:mainfrom
ethanwestfall:images/add-missing-blog-covers

Conversation

@ethanwestfall

@ethanwestfall ethanwestfall commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Adds covers to the six articles that had none, then reworks how covers are produced and consumed site-wide.

Covers for six articles

Branded cover art for:

  • use-any-model-in-claude-code-through-token-station: Opus/Sonnet/Haiku slot to model mapping rows
  • run-any-model-in-codex-through-token-station: config.toml card highlighting wire_api = "responses"
  • run-any-model-in-openclaw-through-token-station: one endpoint fanning out to Token Station model IDs
  • kimi-k3-token-station: price tag plus harness chips
  • qwen-3-8-max-token-station: checkmark scorecard (all three models passed; Qwen tagged for its actual measured finding, latency consistency, not overall score)
  • use-grok-build-in-codex-and-claude-code: price tag plus harness chips, replacing a placeholder terminal screenshot that had been doing double duty as both an in-body figure and the cover

Each article's cover is wired in frontmatter across all four languages, so the generated /posts*.json manifests pick it up.

Every claim baked into the images was checked against the article body rather than taken from memory: Kimi's $3/$15 and $0.30 cache-hit rate, the Claude Code slot mapping, Grok's $1/$2, the Codex wire_api value, OpenClaw's 250+ models across 21 providers, and Qwen's steadiest-latency finding.

og:image

The covers are 1200x630, the Open Graph card size, but WritingLayout emitted no og:image, so a shared link got a text-only preview and the covers only ever reached the listing cards.

The layout now derives an absolute URL from the cover frontmatter and emits og:image with width, height, and alt; twitter:card is summary_large_image when a cover exists and summary when it does not. All 45 article pages carry it, translations included, sharing the one image.

Cover sources are now SVG

All 11 cover sources move from HTML to standalone SVG, rasterised to the committed PNG with headless Chrome. Each source is self-contained: no shared stylesheet, no include, no parameters, so the words in a cover are readable in the diff of the file named after its PNG, and re-rendering one cover cannot disturb another.

Geometry was measured from each original rendering rather than rebuilt by eye, and every cover was diffed pixel by pixel against the PNG that shipped:

changed >8/255 changed >64/255
six flat covers 0.07-0.46% 0.00%
five with a rotated tag or card 0.22-0.59% 0.03-0.14%

The first column is antialiasing. The second is visible change, and a diff mask confirms the non-zero cases are confined to glyph edges inside the rotated group, where SVG and CSS rasterise sub-pixels differently. No layout, colour, or wording changed on any cover.

Three conversion traps, each caught by the pixel diff rather than by looking:

  • letter-spacing needs px units. Chrome parses the SVG attribute as CSS, where a unitless non-zero length is invalid, and drops the declaration silently. Headlines rendered progressively wider, up to 240/255 off.
  • SVG has no text wrapping, so a wrapped line has to become one <text> per visual line rather than one per text node.
  • A CSS transform applies to a whole subtree, so a rotated card's children rotate about the card's centre, not their own.

CLAUDE.md

Records the rule the above implements: every cover ships pre-rendered with its text burnt in, from one SVG source per output PNG. Nothing about a cover is assembled when a reader loads the page, when a card is drawn, or when a link preview is fetched, which rules out request-time templates, live HTML or SVG covers, and generated-on-demand OG images. Re-rendering is a manual authoring step, never part of npm run build. Also documents the SVG authoring notes and the og:image contract.

Verification

  • npm run build clean, 45 pages.
  • npm run check:legacy-links passes.
  • Re-rendering all 11 sources with the documented Chrome command reproduces the committed PNGs.
  • Every cover: path in frontmatter resolves to a file; all 12 cover PNGs present in dist/.

Known state after this PR

The two *-benchmarks.html chart sources are the last HTML holdouts, flagged in CLAUDE.md to move when next touched. blog/asset-sources/ is still copied into dist/ and served, which predates this PR; worth a separate decision about whether the sync script should skip it.

Generates branded cover art (blog/asset-sources/*.html -> blog/*.png,
same pipeline as the existing covers) for:

- use-any-model-in-claude-code-through-token-station: Opus/Sonnet/Haiku
  slot -> model mapping rows
- run-any-model-in-codex-through-token-station: config.toml card
  highlighting wire_api = "responses"
- run-any-model-in-openclaw-through-token-station: hub-and-spoke to
  real Token Station model IDs
- kimi-k3-token-station: price tag + harness chips
- qwen-3-8-max-token-station: checkmark scorecard (all three models
  passed; Qwen tagged for its actual measured finding, latency
  consistency, not overall score)
- use-grok-build-in-codex-and-claude-code: price tag + harness chips,
  replacing the placeholder terminal-screenshot cover

Wires each article's cover: frontmatter across all four languages
(en/zh/ja/ko) so the generated /posts*.json manifests pick it up.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds branded cover images to six ByteFuture Writings posts by (1) committing the cover-image source HTML used by the existing rendering pipeline and (2) wiring each article’s cover: frontmatter across all four languages so the generated /posts*.json manifests and listing pages can display the new covers.

Changes:

  • Added six new cover source files under blog/asset-sources/*-cover.html (for consistent 1200×630 cover generation).
  • Added/updated cover: in frontmatter for the six affected articles across en/zh/ja/ko.
  • Added the corresponding rendered PNG assets under blog/*-cover.png (referenced by the frontmatter).

Reviewed changes

Copilot reviewed 30 out of 36 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/content/writings/en/use-grok-build-in-codex-and-claude-code.md Updates cover frontmatter to point at the new branded PNG.
src/content/writings/zh/use-grok-build-in-codex-and-claude-code.md Updates cover frontmatter to point at the new branded PNG.
src/content/writings/ja/use-grok-build-in-codex-and-claude-code.md Updates cover frontmatter to point at the new branded PNG.
src/content/writings/ko/use-grok-build-in-codex-and-claude-code.md Updates cover frontmatter to point at the new branded PNG.
src/content/writings/en/use-any-model-in-claude-code-through-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/zh/use-any-model-in-claude-code-through-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/ja/use-any-model-in-claude-code-through-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/ko/use-any-model-in-claude-code-through-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/en/run-any-model-in-openclaw-through-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/zh/run-any-model-in-openclaw-through-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/ja/run-any-model-in-openclaw-through-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/ko/run-any-model-in-openclaw-through-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/en/run-any-model-in-codex-through-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/zh/run-any-model-in-codex-through-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/ja/run-any-model-in-codex-through-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/ko/run-any-model-in-codex-through-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/en/kimi-k3-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/zh/kimi-k3-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/ja/kimi-k3-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/ko/kimi-k3-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/en/qwen-3-8-max-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/zh/qwen-3-8-max-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/ja/qwen-3-8-max-token-station.md Adds cover frontmatter for listing/OG image usage.
src/content/writings/ko/qwen-3-8-max-token-station.md Adds cover frontmatter for listing/OG image usage.
blog/asset-sources/use-grok-build-in-codex-and-claude-code-cover.html New cover source template used to render the PNG.
blog/asset-sources/use-any-model-in-claude-code-through-token-station-cover.html New cover source template used to render the PNG.
blog/asset-sources/run-any-model-in-openclaw-through-token-station-cover.html New cover source template used to render the PNG.
blog/asset-sources/run-any-model-in-codex-through-token-station-cover.html New cover source template used to render the PNG.
blog/asset-sources/qwen-3-8-max-token-station-cover.html New cover source template used to render the PNG.
blog/asset-sources/kimi-k3-token-station-cover.html New cover source template used to render the PNG.
blog/use-grok-build-in-codex-and-claude-code-cover.png Rendered cover image referenced by article frontmatter.
blog/use-any-model-in-claude-code-through-token-station-cover.png Rendered cover image referenced by article frontmatter.
blog/run-any-model-in-openclaw-through-token-station-cover.png Rendered cover image referenced by article frontmatter.
blog/run-any-model-in-codex-through-token-station-cover.png Rendered cover image referenced by article frontmatter.
blog/qwen-3-8-max-token-station-cover.png Rendered cover image referenced by article frontmatter.
blog/kimi-k3-token-station-cover.png Rendered cover image referenced by article frontmatter.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@juntao

juntao commented Aug 1, 2026

Copy link
Copy Markdown
Member

Why do we need a "cover source template" to render the PNG? Cann't we just link to the PNG from the article body text? @copilot

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 36 changed files in this pull request and generated no new comments.

juntao and others added 5 commits August 1, 2026 14:59
Each cover source carried its own copy of the frame: the 1200x630 canvas,
the dot grid and washes, the type scale, the chip row, and the brand SVG.
Forty-one lines were byte-identical across all six, and the duplicates had
already drifted apart on four values nobody decided (h1 line-height 1.02 vs
1.03, .sub 34px vs 36px, .chip 22px vs 23px, .brand span 23px vs 24px).

blog/asset-sources/_cover.css now holds the shared frame, the reusable
price tag, and the ByteFuture mark as a data URI so it is defined once.
Each cover keeps only its motif and its copy, 40-60 lines instead of
90-100. Accent colour is a CSS variable; the Claude Code cover overrides
it to blue rather than hardcoding hexes.

Cover copy is unchanged. The PNGs are re-rendered because the drifted
values are now canonical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The covers are 1200x630, the Open Graph card size, but WritingLayout never
emitted og:image, so a shared link got a text-only preview on X, LinkedIn,
and Slack. The cover only ever reached the listing cards.

WritingLayout now turns the root-relative cover into an absolute URL and
emits og:image plus width, height, and alt; twitter:card is
summary_large_image when there is a cover and summary when there is not.
All 45 article pages carry it after this change.

CLAUDE.md documents both this and the shared cover stylesheet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Covers ship as flat PNGs committed to blog/, with the text as pixels in
the file. Nothing about a cover is assembled when a reader loads the page,
when a card is drawn, or when a link preview is fetched.

The rule names what is forbidden: rendering from a template at request
time or from a shared slug-parameterised template, live HTML/SVG/canvas
covers, text layered over a background at view time, generated-on-demand
OG images, and anything resolved at view time such as fonts or copy. A
cover must look identical with no network, no fonts, and no JS.

Also records that re-rendering is a manual authoring step, never part of
npm run build, and that _cover.css is authoring-time only: it takes no
parameters, produces no image, and changes nothing on the site until each
affected cover is re-rendered and its PNG committed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every generated image now has one SVG source in blog/asset-sources/ named
after its output PNG, rasterized once with headless Chrome so the text is
burnt into the pixels.

Adds the authoring notes SVG needs and HTML does not: one <text> per line
since SVG has no wrapping, letter-spacing as px rather than em, &amp; in
the Google Fonts @import, explicit width/height/viewBox, and native <text>
rather than <foreignObject>. Records that the first render needs network
for the webfonts, and that converting text to paths makes a source
self-contained.

Verified the pipeline end to end: a probe SVG rasterizes at 1200x630 with
Space Grotesk, JetBrains Mono, and Source Serif 4 all resolving.

The existing .html sources are marked legacy pending migration. No new
.html sources.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each cover is now a standalone SVG in blog/asset-sources/, rasterized to
the committed PNG with headless Chrome. No shared stylesheet, no include,
no parameters: everything an image needs is in the one file named after
its PNG, so the words are readable in that file's diff and re-rendering
one cover cannot disturb another. _cover.css is gone with the HTML.

Geometry was measured from each original rendering rather than eyeballed,
so the covers reproduce what shipped. Verified per-pixel against the
committed PNGs: six covers differ only at antialiasing level (0.00% of
pixels past a 64/255 delta) and the five with a rotated price tag or
config card differ on 0.03-0.14%, confined to glyph edges inside the
rotated group, where SVG and CSS rasterize sub-pixels differently.

Three conversion details worth recording, each caught by the pixel diff:
letter-spacing needs px units or Chrome parses it as CSS, finds a
unitless non-zero length invalid, and drops it; SVG has no text wrapping,
so a wrapped line becomes one <text> per visual line; and a CSS transform
applies to a whole subtree, so a rotated card's children rotate about the
card's centre rather than their own.

The two *-benchmarks.html chart sources are the last HTML holdouts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@juntao
juntao merged commit be53f8c into second-state:main Aug 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants