优化 Mermaid 流程图的支持
This commit is contained in:
parent
5cf1777c00
commit
589370e456
@ -18,7 +18,7 @@ import { Copy, Clock, Type, Trash2 } from 'lucide-react'
|
||||
import { useLocalStorage } from '@/hooks/use-local-storage'
|
||||
import { codeThemes, type CodeThemeId } from '@/config/code-themes'
|
||||
import '@/styles/code-themes.css'
|
||||
import { copyHandler } from './utils/copy-handler'
|
||||
import { copyHandler, initializeMermaid, MERMAID_CONFIG } from './utils/copy-handler'
|
||||
|
||||
// 计算阅读时间(假设每分钟阅读300字)
|
||||
const calculateReadingTime = (text: string): string => {
|
||||
@ -59,6 +59,14 @@ export default function WechatEditor() {
|
||||
const { handleScroll } = useEditorSync(editorRef)
|
||||
const { handleEditorChange } = useAutoSave(value, setIsDraft)
|
||||
|
||||
// 初始化 Mermaid
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
await initializeMermaid()
|
||||
}
|
||||
init()
|
||||
}, [])
|
||||
|
||||
// 处理编辑器输入
|
||||
const handleInput = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const newValue = e.target.value
|
||||
@ -241,6 +249,14 @@ export default function WechatEditor() {
|
||||
try {
|
||||
const content = getPreviewContent()
|
||||
setPreviewContent(content)
|
||||
|
||||
// 等待下一个渲染周期,确保内容已更新到 DOM
|
||||
await new Promise(resolve => requestAnimationFrame(resolve))
|
||||
// 重新初始化 Mermaid,但不阻塞界面
|
||||
initializeMermaid().catch(error => {
|
||||
console.error('Failed to initialize Mermaid:', error)
|
||||
// 不显示错误提示,让界面继续运行
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error updating preview:', error)
|
||||
toast({
|
||||
|
@ -4,6 +4,86 @@ declare global {
|
||||
interface Window {
|
||||
mermaid: {
|
||||
init: (config: any, nodes: NodeListOf<Element>) => Promise<void>
|
||||
initialize: (config: any) => void
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mermaid 配置
|
||||
export const MERMAID_CONFIG = {
|
||||
theme: 'neutral',
|
||||
themeVariables: {
|
||||
primaryColor: '#f5f8fe',
|
||||
primaryBorderColor: '#c9e0ff',
|
||||
primaryTextColor: '#000000',
|
||||
lineColor: '#000000',
|
||||
textColor: '#000000',
|
||||
fontSize: '14px'
|
||||
},
|
||||
flowchart: {
|
||||
htmlLabels: true,
|
||||
curve: 'basis',
|
||||
padding: 15,
|
||||
nodeSpacing: 50,
|
||||
rankSpacing: 50,
|
||||
useMaxWidth: false
|
||||
},
|
||||
sequence: {
|
||||
useMaxWidth: false,
|
||||
boxMargin: 10,
|
||||
mirrorActors: false,
|
||||
bottomMarginAdj: 2
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 Mermaid
|
||||
*/
|
||||
export async function initializeMermaid() {
|
||||
// 等待 Mermaid 加载完成
|
||||
if (typeof window !== 'undefined') {
|
||||
try {
|
||||
// 如果 mermaid 还没加载,等待它加载完成(最多等待 5 秒)
|
||||
if (!window.mermaid) {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const startTime = Date.now()
|
||||
const checkMermaid = () => {
|
||||
// 如果等待超过 5 秒,就放弃等待
|
||||
if (Date.now() - startTime > 5000) {
|
||||
reject(new Error('Mermaid initialization timeout'))
|
||||
return
|
||||
}
|
||||
|
||||
if (window.mermaid) {
|
||||
resolve()
|
||||
} else {
|
||||
setTimeout(checkMermaid, 100)
|
||||
}
|
||||
}
|
||||
checkMermaid()
|
||||
})
|
||||
}
|
||||
|
||||
// 配置 Mermaid
|
||||
window.mermaid.initialize({
|
||||
...MERMAID_CONFIG,
|
||||
startOnLoad: true,
|
||||
})
|
||||
|
||||
// 初始化所有图表
|
||||
const mermaidElements = document.querySelectorAll('.mermaid')
|
||||
if (mermaidElements.length > 0) {
|
||||
// 设置超时
|
||||
const initPromise = window.mermaid.init(undefined, mermaidElements)
|
||||
const timeoutPromise = new Promise((_, reject) => {
|
||||
setTimeout(() => reject(new Error('Chart initialization timeout')), 5000)
|
||||
})
|
||||
|
||||
await Promise.race([initPromise, timeoutPromise])
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Mermaid initialization error:', error)
|
||||
// 即使初始化失败也继续执行,不阻塞整个应用
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -88,13 +168,15 @@ async function handlePreviewCopy(previewContent: string): Promise<boolean> {
|
||||
const tempDiv = document.createElement('div')
|
||||
tempDiv.innerHTML = previewContent
|
||||
|
||||
// 等待所有 Mermaid 图表渲染完成
|
||||
// 首先找到所有 Mermaid 图表的源代码
|
||||
const mermaidElements = tempDiv.querySelectorAll('.mermaid')
|
||||
if (mermaidElements.length > 0) {
|
||||
try {
|
||||
// 确保 mermaid 已经初始化
|
||||
if (typeof window.mermaid !== 'undefined') {
|
||||
// 重新初始化所有图表
|
||||
// 使用相同的配置
|
||||
window.mermaid.initialize(MERMAID_CONFIG)
|
||||
// 重新渲染所有图表
|
||||
await window.mermaid.init(undefined, mermaidElements)
|
||||
}
|
||||
} catch (error) {
|
||||
@ -102,15 +184,29 @@ async function handlePreviewCopy(previewContent: string): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
// 处理所有的 Mermaid 图表
|
||||
// 处理渲染后的 SVG
|
||||
const mermaidDiagrams = tempDiv.querySelectorAll('.mermaid svg')
|
||||
mermaidDiagrams.forEach(svg => {
|
||||
const container = svg.closest('.mermaid')
|
||||
if (container) {
|
||||
// 保存原始的 SVG 内容
|
||||
const originalSvg = svg.cloneNode(true)
|
||||
container.innerHTML = ''
|
||||
container.appendChild(originalSvg)
|
||||
// 设置 SVG 的样式
|
||||
const svgElement = svg as SVGElement
|
||||
Object.assign(svgElement.style, {
|
||||
backgroundColor: 'transparent',
|
||||
maxWidth: '100%',
|
||||
width: '100%',
|
||||
height: 'auto'
|
||||
})
|
||||
|
||||
// 更新图表容器的样式
|
||||
container.setAttribute('style', `
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
margin: 16px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
`)
|
||||
}
|
||||
})
|
||||
|
||||
@ -118,10 +214,25 @@ async function handlePreviewCopy(previewContent: string): Promise<boolean> {
|
||||
const htmlContent = tempDiv.innerHTML
|
||||
const plainText = tempDiv.textContent || tempDiv.innerText
|
||||
|
||||
// 复制到剪贴板
|
||||
// 复制到剪贴板,添加必要的样式
|
||||
const styledHtml = `
|
||||
<div style="
|
||||
background-color: transparent;
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
color: #000000;
|
||||
line-height: 1.5;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
">
|
||||
${htmlContent}
|
||||
</div>
|
||||
`
|
||||
|
||||
await navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
'text/html': new Blob([htmlContent], { type: 'text/html' }),
|
||||
'text/html': new Blob([styledHtml], { type: 'text/html' }),
|
||||
'text/plain': new Blob([plainText], { type: 'text/plain' })
|
||||
})
|
||||
])
|
||||
|
Loading…
Reference in New Issue
Block a user