Skip to content

Instantly share code, notes, and snippets.

@zhanglianxin
Created April 21, 2025 13:18
Show Gist options
  • Save zhanglianxin/b6b39ddaf7dbea958149713d4612093b to your computer and use it in GitHub Desktop.
Save zhanglianxin/b6b39ddaf7dbea958149713d4612093b to your computer and use it in GitHub Desktop.
Add: support Mermaid syntax parser for Flarum(v1.x)
<?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;
}),
];
@zhanglianxin
Copy link
Author

CAUTION: The Mermaid diagrams will be rendered repeatedly before the page is reloaded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment