Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions apps/site/components/MDX/CodeBox/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { getLanguageDisplayName } from '@node-core/rehype-shiki';

import CodeBox from '#site/components/Common/CodeBox';
import Mermaid from '#site/components/MDX/Mermaid';

import type { FC, HTMLAttributes } from 'react';

const MDXCodeBox: FC<HTMLAttributes<HTMLElement>> = ({
children: code,
className,
}) => {
// Mermaid diagrams arrive as `<pre class="mermaid">` (see rehype-mermaid),
// so we render them as diagrams instead of code boxes
if (className?.split(' ').includes('mermaid')) {
return <Mermaid>{String(code)}</Mermaid>;
}

const matches = className?.match(/language-(?<language>[a-zA-Z]+)/);
const language = matches?.groups?.language ?? '';

Expand Down
10 changes: 10 additions & 0 deletions apps/site/components/MDX/Mermaid/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.mermaid {
display: flex;
justify-content: center;
margin: 1rem 0;
}

.mermaid svg {
height: auto;
max-width: 100%;
}
63 changes: 63 additions & 0 deletions apps/site/components/MDX/Mermaid/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
'use client';

import { useTheme } from 'next-themes';
import { useEffect, useId, useRef } from 'react';

import type { FC } from 'react';

import styles from './index.module.css';


type MermaidProps = {
children: string;
};

const Mermaid: FC<MermaidProps> = ({ children }) => {
const containerRef = useRef<HTMLDivElement>(null);
const { resolvedTheme } = useTheme();
const reactId = useId().replace(/:/g, '-');

useEffect(() => {
let cancelled = false;

const renderDiagram = async () => {
try {
// Mermaid is heavy, so we only load it on the client when needed
const { default: mermaid } = await import('mermaid');

mermaid.initialize({
startOnLoad: false,
securityLevel: 'strict',
theme: resolvedTheme === 'dark' ? 'dark' : 'default',
});

const { svg, bindFunctions } = await mermaid.render(
`mermaid-${reactId}`,
String(children).trim()
);

if (!cancelled && containerRef.current) {
containerRef.current.innerHTML = svg;
bindFunctions?.(containerRef.current);
}
} catch {
// On failure (chunk load or invalid diagram), keep the source readable
if (!cancelled && containerRef.current) {
const fallback = document.createElement('pre');
fallback.textContent = String(children);
containerRef.current.replaceChildren(fallback);
}
}
};

renderDiagram();

return () => {
cancelled = true;
};
}, [children, resolvedTheme, reactId]);

return <div ref={containerRef} className={styles.mermaid} />;
};

export default Mermaid;
4 changes: 4 additions & 0 deletions apps/site/mdx/plugins.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { shikiOptions } from '#platform/shiki.mjs';
import rehypeShikiji from '@node-core/rehype-shiki/plugin';
import remarkHeadings from '@vcarl/remark-headings';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import rehypeMermaid from 'rehype-mermaid';
import rehypeSlug from 'rehype-slug';
import remarkGfm from 'remark-gfm';
import readingTime from 'remark-reading-time';
Expand All @@ -21,6 +22,9 @@ export const rehypePlugins = [
rehypeSlug,
// Automatically add anchor links to headings (H1, ...)
[rehypeAutolinkHeadings, { behavior: 'wrap' }],
// Transforms ```mermaid code blocks into renderable diagrams;
// must run before Shiki so they are not highlighted as plain code
[rehypeMermaid, { strategy: 'pre-mermaid' }],
// Transforms sequential code elements into code tabs and
// adds our syntax highlighter (Shikiji) to Codeboxes
() => singletonShiki,
Expand Down
2 changes: 2 additions & 0 deletions apps/site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"github-slugger": "~2.0.0",
"gray-matter": "~4.0.3",
"mdast-util-to-string": "^4.0.0",
"mermaid": "^11.16.1",
"next": "catalog:",
"next-intl": "~4.13.4",
"next-themes": "~0.4.6",
Expand All @@ -54,6 +55,7 @@
"react-dom": "^19.2.8",
"reading-time": "~1.5.0",
"rehype-autolink-headings": "~7.1.0",
"rehype-mermaid": "^3.0.0",
"rehype-slug": "~6.0.0",
"remark-gfm": "~4.0.1",
"remark-reading-time": "~2.1.0",
Expand Down
Loading