Commit 84526182 by Liu

fix:饼图展示效果

parent ce98b1a5
......@@ -62,66 +62,79 @@ async function getMermaid() {
return mermaidInitPromise
}
function MermaidBlock({ chart }: { chart: string }) {
const mermaidSvgCache = new Map<string, string>()
const mermaidHeightCache = new Map<string, number>()
function MermaidBlock({ chart, cacheKey }: { chart: string, cacheKey: string }) {
const reactId = useId()
const renderId = useMemo(() => `mermaid-${reactId.replace(/:/g, '-')}`, [reactId])
const [svg, setSvg] = useState<string>('')
const [hidden, setHidden] = useState<boolean>(false)
const [svg, setSvg] = useState<string>(() => mermaidSvgCache.get(cacheKey) ?? '')
const [minHeight, setMinHeight] = useState<number>(() => mermaidHeightCache.get(cacheKey) ?? 0)
const svgContainerRef = useRef<HTMLDivElement | null>(null)
const lastGoodSvgRef = useRef<string>('')
useEffect(() => {
let cancelled = false
const code = normalizeMermaid(chart)
if (!code) {
// During streaming, chart text may be temporarily empty.
// Keep the previous SVG (if any) to avoid flicker, and only hide when nothing to show.
if (!svg) {
setSvg('')
setHidden(true)
}
return () => {
cancelled = true
}
const cached = mermaidSvgCache.get(cacheKey)
if (cached) {
lastGoodSvgRef.current = cached
setSvg(cached)
}
const cachedH = mermaidHeightCache.get(cacheKey)
if (cachedH)
setMinHeight(cachedH)
}, [cacheKey])
;(async () => {
try {
const mermaid = await getMermaid()
mermaid.initialize({ startOnLoad: false, suppressErrorRendering: true })
try {
await mermaid.parse(code)
}
catch {
if (!cancelled) {
useEffect(() => {
let cancelled = false
const code = normalizeMermaid(chart)
// During streaming, chart text is often incomplete. To avoid page flicker:
// - debounce render work (avoid per-token rerenders)
// - only update SVG on successful parse+render
// - keep the last successful SVG visible when parse fails temporarily
const t = window.setTimeout(() => {
;(async () => {
if (!code) {
if (!cancelled && !lastGoodSvgRef.current)
setSvg('')
setHidden(true)
}
return
}
try {
const mermaid = await getMermaid()
try {
await mermaid.parse(code)
}
catch {
// Keep previous SVG if any.
if (!cancelled && !lastGoodSvgRef.current)
setSvg('')
return
}
const res = await mermaid.render(renderId, code)
if (cancelled)
return
const res = await mermaid.render(renderId, code)
if (!cancelled) {
const outSvg = String(res?.svg || '')
if (/Syntax error in text/i.test(outSvg)) {
setSvg('')
setHidden(true)
}
else {
setSvg(outSvg)
setHidden(false)
if (!outSvg || /Syntax error in text/i.test(outSvg)) {
if (!lastGoodSvgRef.current)
setSvg('')
return
}
lastGoodSvgRef.current = outSvg
mermaidSvgCache.set(cacheKey, outSvg)
setSvg(outSvg)
}
}
catch {
if (!cancelled) {
setSvg('')
setHidden(true)
catch {
if (!cancelled && !lastGoodSvgRef.current)
setSvg('')
}
}
})()
})()
}, 150)
return () => {
cancelled = true
window.clearTimeout(t)
}
}, [chart, renderId])
......@@ -130,13 +143,23 @@ function MermaidBlock({ chart }: { chart: string }) {
if (!el)
return
el.innerHTML = svg
if (!svg)
return
// Measure height after DOM update to reduce layout shift.
const raf = window.requestAnimationFrame(() => {
const h = Math.ceil(el.getBoundingClientRect().height)
if (h > 0) {
mermaidHeightCache.set(cacheKey, h)
setMinHeight(prev => (h > prev ? h : prev))
}
})
return () => {
window.cancelAnimationFrame(raf)
}
}, [svg])
if (hidden || !svg)
return null
return (
<div className="my-[8px] overflow-x-auto">
<div className="my-[8px] overflow-x-auto" style={{ minHeight: minHeight || undefined }}>
<div ref={svgContainerRef} />
</div>
)
......@@ -178,7 +201,10 @@ export const MarkdownDetail: React.FC<MarkdownDetailProps> = memo(({ children, d
|| (isPie && !disablePie)
) {
const chart = toText((first.props as any)?.children)
return <MermaidBlock chart={chart} />
const node: any = (props as any)?.node
const startOffset = node?.position?.start?.offset
const key = `${lang || 'mermaid'}:${String(startOffset ?? 'na')}`
return <MermaidBlock chart={chart} cacheKey={key} />
}
}
return <pre {...props}>{children}</pre>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment