Skip to content

fix(dev-hub): render mermaid diagrams at build time#3728

Open
aditya520 wants to merge 7 commits into
mainfrom
claude/gallant-grothendieck-b41944
Open

fix(dev-hub): render mermaid diagrams at build time#3728
aditya520 wants to merge 7 commits into
mainfrom
claude/gallant-grothendieck-b41944

Conversation

@aditya520
Copy link
Copy Markdown
Member

Summary

The TON pull-integration page (and any other doc using ```mermaid fences) was rendering the diagram source as a plain Shiki-highlighted code block instead of an actual diagram. Example: https://docs.pyth.network/price-feeds/core/use-real-time-data/pull-integration/ton

Root cause. source.config.ts registered remarkMdxMermaid (a remark plugin) inside the rehype plugins array, so it never ran in any useful way. Even if it had, there was no <Mermaid /> MDX component registered to render the output. Net effect: fences fell through to Shiki and rendered as syntax-highlighted text.

Fix. Replace the broken remarkMdxMermaid registration with rehype-mermaid using the inline-svg strategy, and order it before rehypeCode so Shiki doesn't grab the block first. Diagrams are now rendered to inline SVG at build time via Playwright — zero client-side mermaid runtime, perfect SEO, works without JS.

Changes

Build cost

  • ~30s extra on cold builds for the Chromium download (cached on warm).
  • If Vercel's build container is missing a Chromium shared lib (e.g. libnss3.so, libgbm.so), the build will fail loudly during next build and we'll need a Vercel install hook. Dropped --with-deps from playwright install because it requires sudo apt-get.

Test plan

  • pnpm install
  • pnpm --filter @pythnetwork/developer-hub start:dev and open /price-feeds/core/use-real-time-data/pull-integration/ton — confirm the 7 sequence diagrams render as SVG, not code blocks.
  • pnpm --filter @pythnetwork/developer-hub build succeeds locally.
  • Vercel preview build succeeds and diagrams render on the deployed preview.

🤖 Generated with Claude Code

The previous setup registered remarkMdxMermaid (a remark plugin) in
the rehype plugins array and never registered a <Mermaid /> MDX
component, so ```mermaid fences fell through to Shiki and rendered
as plain syntax-highlighted code blocks (e.g. the TON pull-integration
page).

Replace it with rehype-mermaid using the inline-svg strategy, ordered
before rehypeCode so the block is consumed before Shiki touches it.
Diagrams are now rendered to inline SVG at build time via Playwright,
with zero client-side mermaid runtime.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
component-library Ready Ready Preview, Comment May 19, 2026 8:29pm
developer-hub Ready Ready Preview, Comment May 19, 2026 8:29pm
5 Skipped Deployments
Project Deployment Actions Updated (UTC)
api-reference Skipped Skipped May 19, 2026 8:29pm
entropy-explorer Skipped Skipped May 19, 2026 8:29pm
insights Skipped Skipped May 19, 2026 8:29pm
proposals Skipped Skipped May 19, 2026 8:29pm
staking Skipped Skipped May 19, 2026 8:29pm

Request Review

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous attempt used rehype-mermaid for build-time SVG generation,
but Vercel's Amazon Linux 2023 build container is missing libnspr4 (and
other Chromium shared libs) and we can't sudo apt-get install on Vercel.
The build failed with:

    chrome-headless-shell: error while loading shared libraries:
    libnspr4.so: cannot open shared object file

Pivot to Fumadocs' canonical client-side flow:

- Move remarkMdxMermaid from rehypePlugins to remarkPlugins (it's a
  remark plugin — the previous slot was a no-op).
- Add a small client <Mermaid /> component that lazy-imports mermaid
  and renders into a div via dangerouslySetInnerHTML. Theme follows
  next-themes resolvedTheme.
- Register <Mermaid /> in mdx-components.tsx.
- Drop playwright and rehype-mermaid; add mermaid as a direct dep.
- Revert the build script.

Mermaid is dynamically imported, so doc pages without diagrams pay no
bundle cost; pages with diagrams lazy-load mermaid (~250KB gz) after
hydration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous approach pulled in mermaid (~250KB gz client bundle) plus
40+ transitive deps for 7 sequence diagrams across the docs.

Replace with a ~15-line inline remark plugin in source.config.ts
that rewrites ```mermaid fenced blocks into:

    <img src="https://mermaid.ink/svg/{base64}" alt="..." />

mermaid.ink is a free public service that renders mermaid source
to SVG. The diagrams ship as plain <img> tags — zero npm deps,
zero client-side JS, no Playwright, no Chromium.

Tradeoff: external service dependency. If mermaid.ink is ever
unavailable the images 404, but the page still renders.

Removes mermaid from dependencies, deletes the client Mermaid
component, and reverts the mdx-components registration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous remark-plugin approach was not producing img tags through
the fumadocs-mdx pipeline (still under investigation, possibly an
MDX node-type or plugin-order interaction). Since the TON page is
the only file in the entire repo using ```mermaid fences, drop the
plugin entirely and inline the 7 sequence diagrams directly as
<img src="https://mermaid.ink/svg/{base64}"> tags in the MDX source.

All 7 generated URLs verified returning 200 image/svg+xml from
mermaid.ink. Zero npm dependency footprint, zero runtime cost.

If new docs need diagrams later, run the same base64-encode pattern
or revisit the remark-plugin approach.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel vercel Bot temporarily deployed to Preview – proposals May 19, 2026 18:16 Inactive
@vercel vercel Bot temporarily deployed to Preview – api-reference May 19, 2026 18:16 Inactive
@vercel vercel Bot temporarily deployed to Preview – entropy-explorer May 19, 2026 18:16 Inactive
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

Open in Devin Review

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.

🚩 Assets in documentation should live in public/images per AGENTS.md

The AGENTS.md states: "assets belong in public/images". The new <img> tags reference external mermaid.ink URLs rather than local assets. While these are dynamically-rendered SVGs (not traditional image assets), the spirit of the guideline suggests documentation images should be hosted locally. Storing pre-rendered SVGs in public/images/ would align with this convention and also eliminate the external service dependency noted in ANALYSIS-0001. This is a grey area since the mermaid.ink URLs are essentially encoded diagram definitions, not static assets committed to the repo.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

note right of P: Pyth Contract validates update data and <br/> sends prices with payload to EVAA Master contract
note over M: EVAA Master validates sender <br/> parses payload and <br/> processes the transaction
```
<img src="https://mermaid.ink/svg/c2VxdWVuY2VEaWFncmFtCiAgYXV0b251bWJlcgogIHBhcnRpY2lwYW50IFUgYXMgVXNlcgogIHBhcnRpY2lwYW50IFAgYXMgUHl0aCBDb250cmFjdAogIHBhcnRpY2lwYW50IE0gYXMgRVZBQSBNYXN0ZXIKCiAgbm90ZSBvdmVyIE06IG1hc3Rlci5mYzoxMjEg4oCUIHJlY2VpdmVkIGZyb20gUHl0aCAob3AgMHg1KQogIFUtPj5QOiBvcCAweDUgcGFyc2VfcHJpY2VfZmVlZF91cGRhdGVzIChwcmljZSBmZWVkcyArIHVwZGF0ZSBkYXRhKTxici8+cGF5bG9hZCAob3AgMHgzIGxpcXVpZGF0ZV9tYXN0ZXIgfCAweDQgc3VwcGx5X3dpdGhkcmF3X21hc3RlcikKICBub3RlIHJpZ2h0IG9mIFU6IHNlbmQgcmVxdWVzdCB0byBQeXRoIENvbnRyYWN0IHdpdGggdXBkYXRlIGRhdGEgYW5kIG9wZXJhdGlvbiBwYXlsb2FkPGJyLz5kZXN0aW5hdGlvbiBhZGRyZXNzIGlzIEVWQUEgTWFzdGVyIGNvbnRyYWN0CiAgUC0tPj5NOiBvcCAweDUgcGFyc2VfcHJpY2VfZmVlZF91cGRhdGVzIChwcmljZSBmZWVkcyArIHByaWNlcyk8YnIvPnBheWxvYWQgKG9wIDB4MyBsaXF1aWRhdGVfbWFzdGVyIHwgMHg0IHN1cHBseV93aXRoZHJhd19tYXN0ZXIpCiAgbm90ZSByaWdodCBvZiBQOiBQeXRoIENvbnRyYWN0IHZhbGlkYXRlcyB1cGRhdGUgZGF0YSBhbmQgPGJyLz4gc2VuZHMgcHJpY2VzIHdpdGggcGF5bG9hZCB0byBFVkFBIE1hc3RlciBjb250cmFjdAogIG5vdGUgb3ZlciBNOiBFVkFBIE1hc3RlciB2YWxpZGF0ZXMgc2VuZGVyIDxici8+IHBhcnNlcyBwYXlsb2FkIGFuZCA8YnIvPiBwcm9jZXNzZXMgdGhlIHRyYW5zYWN0aW9u" alt="Mermaid diagram" style={{ maxWidth: "100%", height: "auto", display: "block", margin: "1.5rem auto" }} />
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot May 19, 2026

Choose a reason for hiding this comment

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

📝 Info: External service dependency for diagram rendering

The approach delegates all mermaid rendering to mermaid.ink, an external third-party service. If mermaid.ink goes down or rate-limits requests, diagrams will appear as broken images to users. The previous approach (remarkMdxMermaid) rendered diagrams at build time without external dependencies. However, since the <img> tags are rendered client-side (the browser fetches the images), this doesn't block builds — it only affects the user's viewing experience. The commit history (apps/developer-hub/source.config.ts) shows multiple iterations trying to get mermaid rendering working, suggesting the fumadocs-core plugin had compatibility issues that motivated this approach.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0ab1ce8492

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

note right of P: Pyth Contract validates update data and <br/> sends prices with payload to EVAA Master contract
note over M: EVAA Master validates sender <br/> parses payload and <br/> processes the transaction
```
<img src="https://mermaid.ink/svg/c2VxdWVuY2VEaWFncmFtCiAgYXV0b251bWJlcgogIHBhcnRpY2lwYW50IFUgYXMgVXNlcgogIHBhcnRpY2lwYW50IFAgYXMgUHl0aCBDb250cmFjdAogIHBhcnRpY2lwYW50IE0gYXMgRVZBQSBNYXN0ZXIKCiAgbm90ZSBvdmVyIE06IG1hc3Rlci5mYzoxMjEg4oCUIHJlY2VpdmVkIGZyb20gUHl0aCAob3AgMHg1KQogIFUtPj5QOiBvcCAweDUgcGFyc2VfcHJpY2VfZmVlZF91cGRhdGVzIChwcmljZSBmZWVkcyArIHVwZGF0ZSBkYXRhKTxici8+cGF5bG9hZCAob3AgMHgzIGxpcXVpZGF0ZV9tYXN0ZXIgfCAweDQgc3VwcGx5X3dpdGhkcmF3X21hc3RlcikKICBub3RlIHJpZ2h0IG9mIFU6IHNlbmQgcmVxdWVzdCB0byBQeXRoIENvbnRyYWN0IHdpdGggdXBkYXRlIGRhdGEgYW5kIG9wZXJhdGlvbiBwYXlsb2FkPGJyLz5kZXN0aW5hdGlvbiBhZGRyZXNzIGlzIEVWQUEgTWFzdGVyIGNvbnRyYWN0CiAgUC0tPj5NOiBvcCAweDUgcGFyc2VfcHJpY2VfZmVlZF91cGRhdGVzIChwcmljZSBmZWVkcyArIHByaWNlcyk8YnIvPnBheWxvYWQgKG9wIDB4MyBsaXF1aWRhdGVfbWFzdGVyIHwgMHg0IHN1cHBseV93aXRoZHJhd19tYXN0ZXIpCiAgbm90ZSByaWdodCBvZiBQOiBQeXRoIENvbnRyYWN0IHZhbGlkYXRlcyB1cGRhdGUgZGF0YSBhbmQgPGJyLz4gc2VuZHMgcHJpY2VzIHdpdGggcGF5bG9hZCB0byBFVkFBIE1hc3RlciBjb250cmFjdAogIG5vdGUgb3ZlciBNOiBFVkFBIE1hc3RlciB2YWxpZGF0ZXMgc2VuZGVyIDxici8+IHBhcnNlcyBwYXlsb2FkIGFuZCA8YnIvPiBwcm9jZXNzZXMgdGhlIHRyYW5zYWN0aW9u" alt="Mermaid diagram" style={{ maxWidth: "100%", height: "auto", display: "block", margin: "1.5rem auto" }} />
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid hotlinking critical diagrams to mermaid.ink

Replacing the inline mermaid source with <img src="https://mermaid.ink/..."> makes this documentation page depend on a third-party runtime service for core content. In environments where mermaid.ink is blocked, rate-limited, or temporarily unavailable, readers will see broken images instead of the flow diagrams, which is a regression from self-contained docs content. Please keep diagrams in-repo (mermaid source rendered at build time, or checked-in SVG assets) so page correctness does not rely on external availability.

Useful? React with 👍 / 👎.

…ponent

Root cause of the earlier "plugin didn't fire" — it did fire. Verified
by running through @mdx-js/mdx in isolation: the ```mermaid block was
correctly transformed into an mdxJsxFlowElement with name="img".

The actual failure was downstream: fumadocs-ui's defaultMdxComponents
maps the lowercase `img` JSX element to `next/image`, which requires
`width` and `height` props. My plugin output had only `src` and `alt`,
so next/image silently failed to render anything.

Fix: have the plugin emit a uppercase component name (<MermaidDiagram>)
which MDX resolves through the `components` prop. Register
MermaidDiagram in mdx-components.tsx as a plain <img>, bypassing
next/image entirely.

Now any future ```mermaid fence anywhere in the docs auto-renders.
Zero npm dependencies. Diagrams ship as inline <img> tags pointing
at mermaid.ink. The 7 TON page diagrams revert from hand-inlined
imgs back to the more readable ```mermaid source fences.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Standard base64 can emit `/` and `+` which are unsafe in URL paths.
A `/` would split the mermaid.ink path (e.g. /svg/abc/def) and cause
a 404. The current TON diagrams happen not to hit it, but any future
diagram whose source contains `?` (or other characters that base64
maps to `/`) would silently break.

base64url (RFC 4648 §5) replaces `+` -> `-` and `/` -> `_`, so the
encoded string is safe to drop directly into a URL path. Verified
mermaid.ink returns 200 for the base64url form.

Spotted by Devin's PR review on #3728.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment on lines +19 to +26
const encoded = Buffer.from(source, "utf8").toString("base64url");
node.type = "mdxJsxFlowElement";
node.name = "MermaidDiagram";
node.attributes = [
{
type: "mdxJsxAttribute",
name: "src",
value: `https://mermaid.ink/svg/${encoded}`,
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.

🚩 mermaid.ink encoding format: base64url without prefix

The plugin uses Buffer.from(source, 'utf8').toString('base64url') and places the result directly in the URL path (source.config.ts:19,26). The standard mermaid.ink encoding uses either pako-compressed JSON or a base64: prefixed format. Raw base64url without a prefix would normally cause mermaid.ink to attempt pako decompression, which would fail on raw UTF-8 text. However, the commit message (9f45b7d) explicitly states 'Verified mermaid.ink returns 200 for the base64url form', so the developer confirmed this works empirically. This may indicate mermaid.ink has updated its fallback behavior. Worth noting that if mermaid.ink changes its API in the future, this could break — there's an implicit dependency on undocumented behavior.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +11 to +18
export const MermaidDiagram = ({
src,
alt = "Mermaid diagram",
className,
}: MermaidDiagramProps) => (
// eslint-disable-next-line @next/next/no-img-element
<img src={src} alt={alt} className={clsx(styles.diagram, className)} />
);
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.

🚩 Dark mode compatibility for mermaid.ink SVGs

The MermaidDiagram component renders a plain <img> tag (src/components/MermaidDiagram/index.tsx:17). mermaid.ink renders SVGs using mermaid's default (light) theme — dark text on a light/transparent background. If the developer hub supports dark mode (it uses next-themes per package.json), these diagrams may be hard to read or visually jarring in dark mode since the SVG colors won't adapt. The old remarkMdxMermaid plugin may have had similar limitations, but it's worth considering adding ?theme=dark support or CSS filter: invert() for dark mode.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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