Created
April 21, 2025 13:18
-
-
Save zhanglianxin/b6b39ddaf7dbea958149713d4612093b to your computer and use it in GitHub Desktop.
Add: support Mermaid syntax parser for Flarum(v1.x)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
return [ | |
// 添加对 Mermaid 图表的支持 | |
(new Flarum\Extend\Formatter) | |
->configure(function (\s9e\TextFormatter\Configurator $configurator) { | |
// 添加 Mermaid 标签 | |
$configurator->tags->add('MERMAID', [ | |
'template' => '<div class="mermaid-container"><pre class="mermaid">{@text}</pre></div>' | |
]); | |
// 配置 Markdown 插件,处理 mermaid 语言的代码块 | |
$configurator->Markdown->addCodeLanguage('mermaid', 'MERMAID'); | |
}), | |
// 添加前端脚本和样式 | |
(new Flarum\Extend\Frontend('forum')) | |
->content(function (Flarum\Frontend\Document $document) { | |
// 添加 Mermaid 库 | |
$document->head[] = '<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>'; | |
$document->foot[] = <<<HTML | |
<script> | |
(function() { | |
// 初始化 mermaid | |
mermaid.initialize({ | |
startOnLoad: false, | |
theme: 'neutral', | |
securityLevel: 'loose' | |
}); | |
// 渲染函数 | |
function renderMermaidDiagrams() { | |
const mermaidCodeBlocks = document.querySelectorAll('pre code.language-mermaid:not([data-processed="true"])'); | |
if (mermaidCodeBlocks.length === 0) { | |
// 尝试查找由格式化程序生成的 .mermaid 元素 | |
const mermaidElements = document.querySelectorAll('.mermaid:not([data-processed="true"])'); | |
if (mermaidElements.length > 0) { | |
mermaidElements.forEach(block => { | |
block.setAttribute('data-processed', 'true'); | |
}); | |
try { | |
mermaid.init(undefined, mermaidElements); | |
} catch (error) { | |
console.error('Mermaid error:', error); | |
} | |
} | |
return; | |
} | |
mermaidCodeBlocks.forEach(codeBlock => { | |
// 标记为已处理 | |
codeBlock.setAttribute('data-processed', 'true'); | |
// 创建容器 | |
const container = document.createElement('div'); | |
container.className = 'mermaid-container'; | |
// 创建 mermaid div | |
const mermaidDiv = document.createElement('div'); | |
mermaidDiv.className = 'mermaid'; | |
mermaidDiv.textContent = codeBlock.textContent; | |
container.appendChild(mermaidDiv); | |
// 替换原来的代码块 | |
const preBlock = codeBlock.parentNode; | |
preBlock.parentNode.insertBefore(container, preBlock); | |
preBlock.style.display = 'none'; | |
// 渲染图表 | |
try { | |
mermaid.init(undefined, [mermaidDiv]); | |
} catch (error) { | |
console.error('Mermaid error:', error); | |
const errorDiv = document.createElement('div'); | |
errorDiv.className = 'mermaid-error'; | |
errorDiv.innerHTML = '<p>Error parsing diagram</p>'; | |
container.insertBefore(errorDiv, mermaidDiv); | |
preBlock.style.display = 'block'; | |
} | |
}); | |
} | |
// 初始渲染 - 使用延迟确保 mermaid 已加载 | |
function initMermaid() { | |
if (typeof mermaid === 'undefined') { | |
setTimeout(initMermaid, 100); | |
return; | |
} | |
renderMermaidDiagrams(); | |
setupObserver(); | |
} | |
// 设置观察器 | |
function setupObserver() { | |
// 监视 DOM 变化 | |
const observer = new MutationObserver(mutations => { | |
let shouldRender = false; | |
mutations.forEach(mutation => { | |
if (mutation.type === 'childList' && mutation.addedNodes.length) { | |
for (let i = 0; i < mutation.addedNodes.length; i++) { | |
const node = mutation.addedNodes[i]; | |
if (node.nodeType === Node.ELEMENT_NODE && | |
(node.querySelector('pre code.language-mermaid') || | |
node.querySelector('.mermaid') || | |
(node.nodeName === 'PRE' && node.querySelector('code.language-mermaid')))) { | |
shouldRender = true; | |
break; | |
} | |
} | |
} | |
}); | |
if (shouldRender) { | |
renderMermaidDiagrams(); | |
} | |
}); | |
observer.observe(document.body, { | |
childList: true, | |
subtree: true | |
}); | |
} | |
// 页面加载完成后启动 | |
if (document.readyState === 'loading') { | |
document.addEventListener('DOMContentLoaded', initMermaid); | |
} else { | |
initMermaid(); | |
} | |
})(); | |
</script> | |
<style> | |
.mermaid-container { | |
overflow: auto; | |
margin: 1em 0; | |
text-align: center; | |
} | |
.mermaid-container .mermaid { | |
min-width: 100%; | |
display: inline-block; | |
} | |
.mermaid-error { | |
color: #e74c3c; | |
background: #ffeeee; | |
padding: 10px; | |
border-radius: 4px; | |
margin-bottom: 10px; | |
border-left: 3px solid #e74c3c; | |
} | |
/* 暗模式适配 */ | |
.flarum-night-mode .mermaid-error { | |
background: #442222; | |
color: #ff9999; | |
} | |
.flarum-night-mode .mermaid .label { | |
color: #f8f8f2; | |
} | |
.flarum-night-mode .mermaid .nodeLabel { | |
color: #f8f8f2; | |
} | |
.flarum-night-mode .mermaid .node rect, | |
.flarum-night-mode .mermaid .node circle, | |
.flarum-night-mode .mermaid .node ellipse, | |
.flarum-night-mode .mermaid .node polygon, | |
.flarum-night-mode .mermaid .node path { | |
stroke: #cccccc; | |
} | |
.flarum-night-mode .mermaid .edgePath .path { | |
stroke: #cccccc; | |
} | |
</style> | |
HTML; | |
}), | |
]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
CAUTION: The Mermaid diagrams will be rendered repeatedly before the page is reloaded.