import { useEffect, useState } from 'react' export interface UseMermaidRenderResult { svg: string error: string } /** 根据 data-style 取 Mermaid 节点圆角(px),与 scale.css 各风格对应 */ function getMermaidRadius(dataStyle: string): string { const style = (dataStyle || 'neutral').toLowerCase() if (style === 'soft') return '12px' if (style === 'sharp') return '0' if (style === 'dense') return '4px' return '6px' // neutral / compact 等 } /** 从 document 读取当前 data-style(仅在无传入 dataStyle 时使用) */ function getDataStyleFromDocument(): string { if (typeof document === 'undefined') return 'neutral' return ( document.documentElement.getAttribute('data-style') || document.body?.getAttribute('data-style') || 'neutral' ) } export function useMermaidRender( content: string, id: string, dataStyle?: string ): UseMermaidRenderResult { const [svg, setSvg] = useState('') const [error, setError] = useState('') const style = dataStyle ?? getDataStyleFromDocument() const radius = getMermaidRadius(style) useEffect(() => { let cancelled = false const run = async () => { setError('') setSvg('') if (!content) return try { const mod = await import('mermaid') const mermaid = mod?.default ?? mod const themeCSS = [ /* 矩形:直接设 rx/ry */ `.node rect { rx: ${radius}; ry: ${radius}; }`, `.cluster rect { rx: ${radius}; ry: ${radius}; }`, `.label rect { rx: ${radius}; ry: ${radius}; }`, `.edgeLabel rect { rx: ${radius}; ry: ${radius}; }`, /* 菱形、六边形等 polygon:描边圆角 */ `.node polygon { stroke-linejoin: round; stroke-linecap: round; }`, `.node path { stroke-linejoin: round; stroke-linecap: round; }`, `.cluster polygon { stroke-linejoin: round; stroke-linecap: round; }`, `.cluster path { stroke-linejoin: round; stroke-linecap: round; }`, ].join('\n') mermaid.initialize({ startOnLoad: false, securityLevel: 'strict', theme: 'dark', themeCSS, }) const renderId = `mermaid-${id.replace(/:/g, '')}` const { svg: nextSvg } = await mermaid.render(renderId, content) if (!cancelled) setSvg(nextSvg ?? '') } catch (e) { if (!cancelled) setError((e as Error)?.message ?? 'Failed to render mermaid.') } } run() return () => { cancelled = true } }, [id, content, radius]) return { svg, error } }