mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-25 19:30:17 +08:00
refactor(MermaidPreview): separate measurement from rendering
This commit is contained in:
parent
851b98bf5c
commit
c3e61690d7
@ -14,8 +14,10 @@ const vizInitializer = new AsyncInitializer(async () => {
|
||||
return await module.instance()
|
||||
})
|
||||
|
||||
/** 预览 Graphviz 图表
|
||||
* 使用 usePreviewRenderer hook 大幅简化组件逻辑
|
||||
/**
|
||||
* 预览 Graphviz 图表
|
||||
* - 使用 useDebouncedRender 改善体验
|
||||
* - 使用 shadow dom 渲染 SVG
|
||||
*/
|
||||
const GraphvizPreview = ({
|
||||
children,
|
||||
|
||||
@ -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<string>(`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)
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user