diff --git a/src/renderer/src/components/Preview/GraphvizPreview.tsx b/src/renderer/src/components/Preview/GraphvizPreview.tsx index d7baa67c8f..77a7448ab2 100644 --- a/src/renderer/src/components/Preview/GraphvizPreview.tsx +++ b/src/renderer/src/components/Preview/GraphvizPreview.tsx @@ -14,8 +14,10 @@ const vizInitializer = new AsyncInitializer(async () => { return await module.instance() }) -/** 预览 Graphviz 图表 - * 使用 usePreviewRenderer hook 大幅简化组件逻辑 +/** + * 预览 Graphviz 图表 + * - 使用 useDebouncedRender 改善体验 + * - 使用 shadow dom 渲染 SVG */ const GraphvizPreview = ({ children, diff --git a/src/renderer/src/components/Preview/MermaidPreview.tsx b/src/renderer/src/components/Preview/MermaidPreview.tsx index 1be4cc1ea6..953fbcca71 100644 --- a/src/renderer/src/components/Preview/MermaidPreview.tsx +++ b/src/renderer/src/components/Preview/MermaidPreview.tsx @@ -8,9 +8,10 @@ import ImagePreviewLayout from './ImagePreviewLayout' import { BasicPreviewHandles, BasicPreviewProps } from './types' import { renderSvgInShadowHost } from './utils' -/** 预览 Mermaid 图表 - * 使用 usePreviewRenderer hook 重构,同时保留必要的可见性检测逻辑 - * FIXME: 等将来 mermaid-js 修复可见性问题后可以进一步简化 +/** + * 预览 Mermaid 图表 + * - 使用 useDebouncedRender 改善体验 + * - 使用 shadow dom 渲染 SVG */ const MermaidPreview = ({ children, @@ -21,20 +22,39 @@ const MermaidPreview = ({ const diagramId = useRef(`mermaid-${nanoid(6)}`).current const [isVisible, setIsVisible] = useState(true) - // 定义渲染函数 + /** + * 定义渲染函数,在临时容器中测量,在 shadow dom 中渲染。 + * 如果这个方案有问题,可以回退到 innerHTML。 + */ const renderMermaid = useCallback( async (content: string, container: HTMLDivElement) => { // 验证语法,提前抛出异常 await mermaid.parse(content) - const { svg } = await mermaid.render(diagramId, content, container) + // 获取容器宽度 + const { width } = container.getBoundingClientRect() + if (width === 0) return - // 避免不可见时产生 undefined 和 NaN - const fixedSvg = svg.replace(/translate\(undefined,\s*NaN\)/g, 'translate(0, 0)') + // 创建临时的 div 用于 mermaid 测量 + const measureEl = document.createElement('div') + measureEl.style.position = 'absolute' + measureEl.style.left = '-9999px' + measureEl.style.top = '-9999px' + measureEl.style.width = `${width}px` + document.body.appendChild(measureEl) - // 使用 shadow dom,如果有问题,可以回退到 innerHTML - renderSvgInShadowHost(fixedSvg, container) - // container.innerHTML = fixedSvg + try { + const { svg } = await mermaid.render(diagramId, content, measureEl) + + // 避免不可见时产生 undefined 和 NaN + const fixedSvg = svg.replace(/translate\(undefined,\s*NaN\)/g, 'translate(0, 0)') + + // 有问题可以回退到 innerHTML + renderSvgInShadowHost(fixedSvg, container) + // container.innerHTML = fixedSvg + } finally { + document.body.removeChild(measureEl) + } }, [diagramId, mermaid] ) @@ -67,7 +87,7 @@ const MermaidPreview = ({ const element = containerRef.current if (!element) return - const currentlyVisible = element.offsetParent !== null + const currentlyVisible = element.offsetParent !== null && element.offsetWidth > 0 && element.offsetHeight > 0 setIsVisible(currentlyVisible) }