Skip to content

feat(changelogs): add embeddable PNG cards with dark/light themes and…#741

Merged
castrojo merged 1 commit into
projectbluefin:mainfrom
castrojo:feature/embeddable-card-images
Apr 12, 2026
Merged

feat(changelogs): add embeddable PNG cards with dark/light themes and…#741
castrojo merged 1 commit into
projectbluefin:mainfrom
castrojo:feature/embeddable-card-images

Conversation

@castrojo
Copy link
Copy Markdown
Contributor

… embed button

Generates 6 PNG cards (3 streams × 2 themes) during CI via Satori + resvg-js. Cards match the OsReleaseCard visual style on /changelogs, with version bump highlights (gold chip + ↑ arrow) for packages that changed vs the previous release.

  • scripts/generate-card-images.mjs: reads bluefin/lts feed JSON, renders PNGs
  • scripts/lib/card-template.mjs: Satori element tree matching OsReleaseCard
  • OsReleaseCard.tsx: adds "Embed ↗" button that copies GitHub-flavored markdown (alt + dark variant) to clipboard
  • OsReleaseCard.module.css: .embedButton style matching .viewLink aesthetic
  • .github/workflows/pages.yml: generate-card-images step before build
  • .gitignore: static/img/cards/ (generated, not committed)
  • package.json: satori, @resvg/resvg-js, @fontsource/inter devDeps + script
  • docs/images.md: documents markdown embed snippets for all 3 cards
  • static/img/characters/*.png: PNG copies of mascots (Satori needs PNG/JPEG)

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements automated generation of embeddable PNG cards for OS releases using Satori and resvg-js. It adds a generation script, a card template, and a new "Embed" button in the OsReleaseCard component for copying Markdown snippets. Feedback recommends adding error handling to the clipboard API call and using Docusaurus site configuration for the base URL to ensure link reliability across environments.

Comment thread src/components/OsReleaseCard.tsx Outdated
Comment on lines +175 to +178
navigator.clipboard.writeText(snippet).then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The navigator.clipboard.writeText(snippet) promise can reject if, for example, the user denies clipboard permission. This will cause an unhandled promise rejection in the browser. You should add a .catch() to handle this case gracefully, for instance by logging the error.

Suggested change
navigator.clipboard.writeText(snippet).then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
});
navigator.clipboard.writeText(snippet).then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}).catch(err => console.error("Failed to copy snippet:", err));

Comment thread src/components/OsReleaseCard.tsx Outdated
Comment on lines +240 to +244
const BASE_URL = "https://docs.projectbluefin.io";
const embedSnippet = [
`[![${cardAlt}](${BASE_URL}/img/cards/${cardSlug}-light.png#gh-light-mode-only)](${BASE_URL}/changelogs)`,
`[![${cardAlt}](${BASE_URL}/img/cards/${cardSlug}-dark.png#gh-dark-mode-only)](${BASE_URL}/changelogs)`,
].join("\n");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Hardcoding the base URL is not robust and can lead to broken links in different environments (e.g., staging, previews). It's better to retrieve the URL from Docusaurus's context via useDocusaurusContext.

You'll also need to add import { useDocusaurusContext } from "@docusaurus/useDocusaurusContext"; at the top of the file.

Suggested change
const BASE_URL = "https://docs.projectbluefin.io";
const embedSnippet = [
`[![${cardAlt}](${BASE_URL}/img/cards/${cardSlug}-light.png#gh-light-mode-only)](${BASE_URL}/changelogs)`,
`[![${cardAlt}](${BASE_URL}/img/cards/${cardSlug}-dark.png#gh-dark-mode-only)](${BASE_URL}/changelogs)`,
].join("\n");
const { siteConfig } = useDocusaurusContext();
const embedSnippet = [
"[![" + cardAlt + "](" + siteConfig.url + "/img/cards/" + cardSlug + "-light.png#gh-light-mode-only)](" + siteConfig.url + "/changelogs)",
"[![" + cardAlt + "](" + siteConfig.url + "/img/cards/" + cardSlug + "-dark.png#gh-dark-mode-only)](" + siteConfig.url + "/changelogs)",
].join("\n");

… embed button

Generates 6 PNG cards (3 streams × 2 themes) during CI via Satori + resvg-js.
Cards match the OsReleaseCard visual style on /changelogs, with version bump
highlights (gold chip + ↑ arrow) for packages that changed vs the previous
release.

- scripts/generate-card-images.mjs: reads bluefin/lts feed JSON, renders PNGs
- scripts/lib/card-template.mjs: Satori element tree matching OsReleaseCard
- OsReleaseCard.tsx: adds "Embed ↗" button that copies GitHub-flavored markdown
  ([![alt](light#gh-light-mode-only)](changelogs) + dark variant) to clipboard
- OsReleaseCard.module.css: .embedButton style matching .viewLink aesthetic
- .github/workflows/pages.yml: generate-card-images step before build
- .gitignore: static/img/cards/ (generated, not committed)
- package.json: satori, @resvg/resvg-js, @fontsource/inter devDeps + script
- docs/images.md: documents markdown embed snippets for all 3 cards
- static/img/characters/*.png: PNG copies of mascots (Satori needs PNG/JPEG)
@castrojo castrojo force-pushed the feature/embeddable-card-images branch from 681a3ca to d0f55c9 Compare April 12, 2026 02:37
@castrojo castrojo merged commit f0e7a2d into projectbluefin:main Apr 12, 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.

1 participant