@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
97 lines (96 loc) • 3.14 kB
JavaScript
"use client";
import { prepareInlineMermaidSvg } from "../Mermaid/SyntaxMermaid/prepareInlineSvg.mjs";
import { createCdnMermaidConfig, renderWithCdnMermaid } from "./useMermaidCdn.mjs";
import { useEffect, useId, useMemo, useState } from "react";
import { useThemeMode } from "antd-style";
import { THEMES, renderMermaidSVG } from "beautiful-mermaid";
//#region src/hooks/useMermaid.ts
/**
* beautiful-mermaid emits these as CSS custom properties on the SVG root and
* derives the rest with color-mix(), so pointing them at antd's variables lets
* the diagram follow the theme through CSS alone — no token subscription, and
* no re-render (or re-layout) when the appearance changes.
*/
const LOBE_THEME_OPTIONS = {
accent: "var(--ant-color-primary)",
bg: "var(--ant-color-bg-container)",
border: "var(--ant-color-border)",
fg: "var(--ant-color-text)",
line: "var(--ant-color-text-secondary)",
muted: "var(--ant-color-text-description)",
surface: "var(--ant-color-fill-tertiary)",
transparent: true
};
const isNamedTheme = (theme) => !!theme && theme !== "lobe-theme" && theme in THEMES;
const createMermaidOptions = (customTheme) => isNamedTheme(customTheme) ? THEMES[customTheme] : LOBE_THEME_OPTIONS;
/**
* beautiful-mermaid covers six diagram types; anything else throws here and is
* handed to the CDN-loaded upstream mermaid instead.
*/
const renderMermaid = (content, scopeId, customTheme) => {
if (!content) return { svg: "" };
try {
return { svg: prepareInlineMermaidSvg(renderMermaidSVG(content, createMermaidOptions(customTheme)), scopeId) };
} catch {
return {
error: "unsupported",
svg: ""
};
}
};
const useCdnMermaidFallback = (content, { enabled, theme: customTheme }) => {
const { isDarkMode } = useThemeMode();
const reactId = useId();
const [result, setResult] = useState({ svg: "" });
useEffect(() => {
if (!enabled || !content) {
setResult({ svg: "" });
return;
}
let active = true;
setResult({
loading: true,
svg: ""
});
const config = createCdnMermaidConfig(isDarkMode, isNamedTheme(customTheme) ? customTheme : void 0);
renderWithCdnMermaid(content, `mermaid-cdn-${reactId.replaceAll(":", "")}`, config).then((svg) => {
if (!active) return;
setResult(svg ? { svg } : {
error: "Failed to render diagram",
svg: ""
});
}).catch(() => {
if (active) setResult({
error: "Failed to render diagram",
svg: ""
});
});
return () => {
active = false;
};
}, [
enabled,
content,
isDarkMode,
customTheme,
reactId
]);
return result;
};
const useMermaid = (content, { theme: customTheme } = {}) => {
const reactId = useId();
const scopeId = useMemo(() => `mermaid-${reactId.replaceAll(":", "")}`, [reactId]);
const primary = useMemo(() => renderMermaid(content, scopeId, customTheme), [
content,
scopeId,
customTheme
]);
const fallback = useCdnMermaidFallback(content, {
enabled: Boolean(primary.error),
theme: customTheme
});
return primary.error ? fallback : primary;
};
//#endregion
export { createMermaidOptions, renderMermaid, useCdnMermaidFallback, useMermaid };
//# sourceMappingURL=useMermaid.mjs.map