Add cover images for six articles missing them - #25
Merged
juntao merged 6 commits intoAug 2, 2026
Conversation
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.
There was a problem hiding this comment.
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 acrossen/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.
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 |
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, & 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 rowsrun-any-model-in-codex-through-token-station: config.toml card highlightingwire_api = "responses"run-any-model-in-openclaw-through-token-station: one endpoint fanning out to Token Station model IDskimi-k3-token-station: price tag plus harness chipsqwen-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 coverEach article's
coveris wired in frontmatter across all four languages, so the generated/posts*.jsonmanifests 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_apivalue, 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
WritingLayoutemitted noog: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
coverfrontmatter and emitsog:imagewith width, height, and alt;twitter:cardissummary_large_imagewhen a cover exists andsummarywhen 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:
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-spacingneedspxunits. 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.<text>per visual line rather than one per text node.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 theog:imagecontract.Verification
npm run buildclean, 45 pages.npm run check:legacy-linkspasses.cover:path in frontmatter resolves to a file; all 12 cover PNGs present indist/.Known state after this PR
The two
*-benchmarks.htmlchart sources are the last HTML holdouts, flagged in CLAUDE.md to move when next touched.blog/asset-sources/is still copied intodist/and served, which predates this PR; worth a separate decision about whether the sync script should skip it.