Files
2026-02-16 16:41:54 -08:00

69 lines
1.5 KiB
TypeScript

"use client";
import { useTheme } from "next-themes";
import { use, useEffect, useId, useState } from "react";
export const Mermaid = ({ chart }: { chart: string }) => {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return null;
}
return <MermaidContent chart={chart} />;
};
const cache = new Map<string, Promise<unknown>>();
function cachePromise<T>(
key: string,
setPromise: () => Promise<T>
): Promise<T> {
const cached = cache.get(key);
if (cached) {
return cached as Promise<T>;
}
const promise = setPromise();
cache.set(key, promise);
return promise;
}
function MermaidContent({ chart }: { chart: string }) {
const id = useId();
const { resolvedTheme } = useTheme();
const { default: mermaid } = use(
cachePromise("mermaid", () => import("mermaid"))
);
mermaid.initialize({
startOnLoad: false,
securityLevel: "loose",
fontFamily: "inherit",
themeCSS: "margin: 1.5rem auto 0;",
theme: resolvedTheme === "dark" ? "dark" : "default",
});
const { svg, bindFunctions } = use(
cachePromise(`${chart}-${resolvedTheme}`, () =>
mermaid.render(id, chart.replaceAll("\\n", "\n"))
)
);
return (
<div
// biome-ignore lint/security/noDangerouslySetInnerHtml: "this is needed."
dangerouslySetInnerHTML={{ __html: svg }}
ref={(container) => {
if (container) {
bindFunctions?.(container);
}
}}
/>
);
}