Add Mermaid

This commit is contained in:
Ho Ngoc Hai
2026-01-08 10:28:30 +07:00
parent 9884947953
commit 41232909d3
5 changed files with 881 additions and 2 deletions

View File

@@ -18,6 +18,7 @@
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.4.0",
"lucide-react": "^0.555.0",
"mermaid": "^11.12.2",
"next": "16.0.4",
"next-intl": "^4.5.5",
"react": "19.2.0",

View File

@@ -6,6 +6,7 @@ export type MDXComponents = {
[key: string]: ComponentType<any>;
};
import TableWrapper from '@/components/docs/TableWrapper';
import Mermaid from '@/components/mdx/Mermaid';
// Helper function to extract text from React children
function getTextFromChildren(children: React.ReactNode): string {
@@ -69,7 +70,6 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
h3: createHeadingComponent(3),
h4: createHeadingComponent(4),
h5: createHeadingComponent(5),
h6: createHeadingComponent(6),
// Custom table component with wrapper for responsive scrolling
table: ({ children, ...props }: React.HTMLAttributes<HTMLTableElement>) => (
<TableWrapper>
@@ -78,6 +78,22 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
</table>
</TableWrapper>
),
// Custom pre component to handle mermaid diagrams
pre: ({ children, ...props }: React.HTMLAttributes<HTMLPreElement>) => {
// Check if children is a code element
const codeElement = React.Children.toArray(children)[0];
if (
React.isValidElement(codeElement) &&
(codeElement as React.ReactElement<{ className?: string }>).props.className?.includes('language-mermaid')
) {
// Extract text content from the code element
const chart = getTextFromChildren((codeElement as React.ReactElement<{ children?: React.ReactNode }>).props.children);
return <Mermaid chart={chart} />;
}
// Default pre rendering
return <pre {...props}>{children}</pre>;
},
// Ensure other components work
...components,
};

View File

@@ -0,0 +1,66 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
import mermaid from "mermaid";
interface MermaidProps {
chart: string;
}
const Mermaid: React.FC<MermaidProps> = ({ chart }) => {
const ref = useRef<HTMLDivElement>(null);
const [rendered, setRendered] = useState("");
useEffect(() => {
// Only verify we're on client side
if (typeof window === "undefined") return;
// Initialize mermaid
mermaid.initialize({
startOnLoad: false,
theme: 'default',
securityLevel: 'loose',
fontFamily: 'inherit',
});
let isMounted = true;
const renderChart = async () => {
try {
if (!ref.current) return;
// Use a unique ID for each render to avoid conflicts
const id = `mermaid-${Math.random().toString(36).substring(2, 9)}`;
// Render the chart
const { svg } = await mermaid.render(id, chart);
if (isMounted) {
setRendered(svg);
}
} catch (error) {
console.error("Mermaid rendering failed:", error);
// Show error in UI
if (isMounted) {
setRendered(`<div class="text-red-500 text-sm bg-red-50 p-2 rounded">Failed to render diagram</div>`);
}
}
};
renderChart();
return () => {
isMounted = false;
};
}, [chart]);
return (
<div
className="mermaid my-4 flex justify-center bg-white dark:bg-zinc-900/50 p-4 rounded-lg overflow-x-auto"
ref={ref}
dangerouslySetInnerHTML={{ __html: rendered }}
/>
);
};
export default Mermaid;

12
docs/en/test-mermaid.md Normal file
View File

@@ -0,0 +1,12 @@
# Mermaid Test
This is a test diagram.
```mermaid
graph TD;
A[Start] --> B{Is it working?};
B -- Yes --> C[Great!];
B -- No --> D[Debug more];
```
End of test.

786
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff