feat: 移除 Mermaid 支持并创建需求文档

- 从 settings.php 移除 Mermaid 设置项和选项保存逻辑
- 从 functions.php 移除 Mermaid 代码块预处理函数
- 从 footer.php 移除 Mermaid 加载和渲染代码
- 从 style.css 移除 Mermaid 图表样式
- 删除本地镜像文件 assets/vendor/external/mermaid/
- 创建 mermaid-support-requirements.md 需求文档

原因:WP-Markdown 编辑器保存的 Markdown 源文件中 Mermaid 代码是一整行,
没有真正的换行符,导致 Mermaid 解析器持续报错。所有尝试的解决方案均失败。
需求文档中详细说明了问题原因和推荐的替代方案。
This commit is contained in:
2026-01-23 22:11:09 +08:00
parent 5704531a4d
commit 54cbb400b9
7 changed files with 275 additions and 2465 deletions

View File

@@ -1,25 +0,0 @@
# Mermaid 本地镜像
本目录用于存放 Mermaid 库的本地镜像文件。
## 需要的文件
请从以下地址下载 Mermaid 库文件并放置在此目录:
- **mermaid.min.js**: https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js
## 使用方法
1. 下载上述文件到本目录
2. 在主题设置页面的"功能增强 > Mermaid 图表"中勾选"使用本地镜像"
3. 保存设置后,主题将优先使用本地文件而非 CDN
## 版本信息
- Mermaid 版本: 10.x
- 文件大小: 约 1.5MB (压缩后)
## 更新
如需更新到最新版本,请访问:
https://github.com/mermaid-js/mermaid/releases

File diff suppressed because one or more lines are too long

View File

@@ -147,224 +147,6 @@
<?php }?>
<?php if (get_option('argon_enable_mermaid') == 'true') { /*Mermaid*/?>
<?php
// 判断是否使用本地镜像
$mermaid_url = get_option('argon_mermaid_cdn_url') == '' ? '//cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js' : get_option('argon_mermaid_cdn_url');
if (get_option('argon_mermaid_use_local') == 'true') {
$mermaid_url = $GLOBALS['assets_path'] . '/assets/vendor/external/mermaid/mermaid.min.js';
}
?>
<script src="<?php echo $mermaid_url; ?>"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
mermaid.initialize({
startOnLoad: false,
theme: '<?php echo get_option('argon_mermaid_theme', 'default'); ?>',
securityLevel: 'loose',
flowchart: {
useMaxWidth: true,
htmlLabels: true
}
});
// 处理 WP-Markdown 生成的特殊格式
document.querySelectorAll('div.mermaid').forEach(function(mermaidDiv) {
// 检查是否已经处理过
if (mermaidDiv.classList.contains('mermaid-processed')) {
return;
}
mermaidDiv.classList.add('mermaid-processed');
// 优先使用 PHP 预处理的 data 属性
let code = mermaidDiv.getAttribute('data-mermaid-code');
if (!code) {
// 如果没有 data 属性,尝试从 script 标签提取
let scriptElement = mermaidDiv.querySelector('script[type="text/javascript"]');
if (scriptElement) {
let scriptContent = scriptElement.textContent;
let match = scriptContent.match(/document\.write\("(.*)"\)/s);
if (match && match[1]) {
code = match[1]
.replace(/\\n/g, '\n')
.replace(/\\"/g, '"')
.replace(/\\'/g, "'")
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&amp;/g, '&');
}
}
}
if (!code) {
console.warn('No mermaid code found in div');
return;
}
// 清空 div 内容
mermaidDiv.innerHTML = '';
// 渲染 Mermaid 图表
try {
let id = 'mermaid-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
mermaid.render(id, code).then(function(result) {
mermaidDiv.innerHTML = result.svg;
}).catch(function(e) {
console.error('Mermaid rendering error:', e);
mermaidDiv.innerHTML = '<pre style="color: red;">Mermaid 渲染失败: ' + e.message + '</pre>';
});
} catch (e) {
console.error('Mermaid rendering error:', e);
mermaidDiv.innerHTML = '<pre style="color: red;">Mermaid 渲染失败: ' + e.message + '</pre>';
}
});
// 递归获取所有文本节点,保留换行符
function getTextWithLineBreaks(element) {
let text = '';
for (let node of element.childNodes) {
if (node.nodeType === Node.TEXT_NODE) {
text += node.textContent;
} else if (node.nodeType === Node.ELEMENT_NODE) {
if (node.tagName === 'BR') {
text += '\n';
} else {
text += getTextWithLineBreaks(node);
}
}
}
return text;
}
// 处理标准格式的 mermaid 代码块
document.querySelectorAll('pre code.language-mermaid, pre code.mermaid').forEach(function(element) {
// 检查是否已经处理过
if (element.classList.contains('mermaid-processed')) {
return;
}
element.classList.add('mermaid-processed');
let pre = element.parentElement;
// 优先从 data-mermaid-code 属性获取代码
let code = element.getAttribute('data-mermaid-code');
// 如果没有 data 属性,尝试从内容获取
if (!code) {
code = getTextWithLineBreaks(element);
}
// 去除首尾空白
code = code.trim();
// 验证是否为有效的 Mermaid 代码
let validKeywords = ['graph', 'flowchart', 'sequenceDiagram', 'classDiagram', 'stateDiagram', 'erDiagram', 'journey', 'gantt', 'pie', 'gitGraph', 'mindmap', 'timeline', 'quadrantChart'];
let isValid = false;
for (let keyword of validKeywords) {
if (code.toLowerCase().startsWith(keyword.toLowerCase())) {
isValid = true;
break;
}
}
// 如果不是有效的 Mermaid 代码,跳过
if (!isValid) {
console.log('Skipping invalid Mermaid code:', code.substring(0, 50));
return;
}
// 调试:输出代码内容和换行符
console.log('Standard Mermaid code found (length: ' + code.length + ')');
console.log('Contains newlines:', code.indexOf('\n') !== -1);
console.log('First 100 chars:', code.substring(0, 100));
// 创建 Mermaid 容器
let mermaidDiv = document.createElement('div');
mermaidDiv.className = 'mermaid';
mermaidDiv.textContent = code;
// 替换原来的 pre 元素
pre.parentNode.replaceChild(mermaidDiv, pre);
// 立即渲染
try {
mermaid.init(undefined, mermaidDiv);
console.log('Standard Mermaid diagram rendered successfully');
} catch (e) {
console.error('Mermaid rendering error:', e);
console.error('Code that failed:', code.substring(0, 200));
}
});
});
</script>
<?php }?>
<?php if (get_option('argon_enable_code_highlight') == 'true') { /*Highlight.js*/?>
<link rel="stylesheet" href="<?php echo $GLOBALS['assets_path']; ?>/assets/vendor/highlight/styles/<?php echo get_option('argon_code_theme') == '' ? 'vs2015' : get_option('argon_code_theme'); ?>.css">

View File

@@ -3566,31 +3566,6 @@ function argon_fancybox($content){
return $content;
}
function the_content_filter($content){
// 处理 Mermaid 代码块,确保换行符正确
if (get_option('argon_enable_mermaid') == 'true') {
// 匹配 <pre><code class="language-mermaid">...</code></pre> 或 <pre><code class="mermaid">...</code></pre>
$content = preg_replace_callback(
'/<pre><code\s+class=["\'](?:language-)?mermaid["\']>(.*?)<\/code><\/pre>/is',
function($matches) {
$code = $matches[1];
// 解码 HTML 实体
$code = html_entity_decode($code, ENT_QUOTES | ENT_HTML5, 'UTF-8');
// 去除首尾空白
$code = trim($code);
// 如果没有换行符尝试恢复WordPress 可能把换行符转换成了空格)
// 检测常见的 Mermaid 关键字后应该有换行
$keywords = ['flowchart', 'graph', 'sequenceDiagram', 'classDiagram', 'stateDiagram', 'erDiagram', 'journey', 'gantt', 'pie'];
foreach ($keywords as $keyword) {
// 匹配关键字后跟方向TD/LR等或其他内容但没有换行符的情况
$code = preg_replace('/(' . preg_quote($keyword, '/') . '\s+(?:TD|LR|RL|BT|TB))\s+(?![\r\n])/', "$1\n", $code);
}
// 返回带换行符的代码块,使用 data 属性存储原始代码
return '<pre><code class="language-mermaid" data-mermaid-code="' . esc_attr($code) . '">' . esc_html($code) . '</code></pre>';
},
$content
);
}
// 根据设置决定是否启用懒加载
if (get_option('argon_enable_lazyload') !== 'false') {
$content = argon_lazyload($content);
@@ -3608,51 +3583,6 @@ function the_content_filter($content){
}
add_filter('the_content' , 'the_content_filter',20);
// Mermaid 代码块预处理
function argon_format_mermaid_code($content) {
if (get_option('argon_mermaid_enable', 'false') != 'true') {
return $content;
}
// 匹配 WP-Markdown 生成的 mermaid 代码块格式
$pattern = '/<div class="mermaid">(.*?)<\/div>/s';
$content = preg_replace_callback($pattern, function($matches) {
$div_content = $matches[1];
// 检查是否包含 script 标签WP-Markdown 格式)
if (preg_match('/<script[^>]*>document\.write\("(.*)"\)<\/script>/s', $div_content, $script_match)) {
$mermaid_code = $script_match[1];
// 解码转义字符
$mermaid_code = str_replace('\\n', "\n", $mermaid_code);
$mermaid_code = str_replace('\\"', '"', $mermaid_code);
$mermaid_code = str_replace("\\'", "'", $mermaid_code);
$mermaid_code = html_entity_decode($mermaid_code);
// 如果代码看起来像一行前100个字符中换行符很少添加换行
$first_100 = substr($mermaid_code, 0, 100);
$newline_count = substr_count($first_100, "\n");
if ($newline_count < 2) {
// 在箭头前添加换行
$mermaid_code = preg_replace('/\s*-->\s*/', "\n --> ", $mermaid_code);
// 在 style 语句前添加换行
$mermaid_code = preg_replace('/\s*style\s+/', "\nstyle ", $mermaid_code);
}
// 返回带 data 属性的 div
return '<div class="mermaid" data-mermaid-code="' . esc_attr($mermaid_code) . '">' . $div_content . '</div>';
}
// 不是 WP-Markdown 格式,保持原样
return $matches[0];
}, $content);
return $content;
}
add_filter('the_content', 'argon_format_mermaid_code', 25);
//使用 CDN 加速 gravatar
function gravatar_cdn($url){
$cdn = get_option('argon_gravatar_cdn', 'gravatar.pho.ink/avatar/');

View File

@@ -0,0 +1,275 @@
# Argon 主题 Mermaid 图表支持需求文档
## 需求背景
用户希望在 Argon WordPress 主题中添加 Mermaid 图表支持,以便在文章中使用流程图、时序图等可视化图表。
## 技术环境
- **WordPress 主题**: Argon
- **Markdown 编辑器**: WP-Markdown (wdmd) - 注意:这是编辑器,不是插件
- **Mermaid 版本**: 10.x 或更高
## 核心问题
### 1. WP-Markdown 渲染格式特殊
WP-Markdown 编辑器渲染 Mermaid 代码块时,生成的 HTML 格式为:
```html
<div class="mermaid">
<script>document.write("flowchart TD\n Start --> End")</script>
flowchart TD Start --> End
</div>
```
特点:
- 使用 `<div class="mermaid">` 包裹
- 内部包含 `<script>` 标签,使用 `document.write()` 输出转义后的代码
- 转义字符包括:`\n`(换行)、`\"`(引号)、`\'`(单引号)
- script 标签后还有原始文本(未转义)
### 2. Markdown 源文件格式问题
**关键问题**:用户在 WP-Markdown 编辑器中编辑时,虽然界面显示有换行(编辑器自动换行),但保存到 Markdown 源文件时,**整个 Mermaid 代码块是一整行,没有真正的换行符**。
示例:
```markdown
```mermaid
flowchart TD Start([用户提交评论]) --> PreProcess[preprocess_comment 钩子] PreProcess --> CheckEnabled{启用 AI 检测?} CheckEnabled -->|否| SaveComment[保存评论]
```
```
这导致:
- Mermaid 解析器要求代码有正确的换行格式
- 持续报错:`Parse error on line 1: Expecting 'NEWLINE', 'SPACE', 'GRAPH'`
- 所有尝试在 JavaScript 或 PHP 端添加换行的方案都失败
### 3. 已尝试的解决方案(均失败)
#### 方案 1: JavaScript 端解码
- 从 script 标签提取代码
- 解码 `\n` 为真正的换行符
- **失败原因**Markdown 源文件中就没有 `\n`,只是一整行
#### 方案 2: 使用 mermaid.render()
- 替代 `mermaid.init()` 方法
- 手动渲染并插入 SVG
- **失败原因**:解析器仍然要求正确的换行格式
#### 方案 3: PHP 端预处理
- 在 `functions.php` 中添加 `argon_format_mermaid_code()` 函数
- 使用正则表达式在箭头、关键字前添加换行
- **失败原因**:无法准确判断应该在哪里添加换行
#### 方案 4: 智能检测单行代码
- 检测前 100 个字符中的换行符数量
- 如果少于 2 个,自动格式化
- **失败原因**:格式化规则不完善,仍然解析失败
## 需求分析
### 必要条件
要成功支持 Mermaid需要满足以下条件之一
1. **修改 WP-Markdown 编辑器**
- 保存时保留真正的换行符
- 或者在保存前格式化 Mermaid 代码块
- **难度**:需要修改编辑器源码,可能影响其他功能
2. **使用支持 Mermaid 的 WordPress 插件**
- 例如WP Githuber MD、Markdown Block 等
- 这些插件原生支持 Mermaid
- **难度**:需要更换编辑器,用户可能不愿意
3. **开发完整的 Mermaid 代码格式化器**
- 能够智能识别 Mermaid 语法
- 自动添加正确的换行符
- **难度**:需要深入理解 Mermaid 语法规则,工作量大
### 功能需求
如果要实现 Mermaid 支持,应包含以下功能:
#### 1. 基础功能
- [ ] 支持 Mermaid 所有图表类型(流程图、时序图、类图等)
- [ ] 响应式设计,图表自适应容器宽度
- [ ] 支持夜间模式,自动切换图表主题
- [ ] 支持本地镜像和 CDN 两种加载方式
#### 2. 设置选项
- [ ] 启用/禁用 Mermaid 支持
- [ ] 选择 CDN 地址jsDelivr、unpkg、自定义
- [ ] 选择图表主题default、dark、forest、neutral 等)
- [ ] 是否使用本地镜像
#### 3. 样式优化
- [ ] 图表容器样式(背景、圆角、阴影)
- [ ] 夜间模式适配
- [ ] 移动端响应式
- [ ] 横向滚动支持(超宽图表)
#### 4. 错误处理
- [ ] 解析失败时显示友好的错误信息
- [ ] 在控制台输出详细的调试信息
- [ ] 提供代码预览功能
## 推荐方案
### 方案 A: 使用支持 Mermaid 的插件(推荐)
**优点**
- 开箱即用,无需开发
- 插件维护者会持续更新
- 兼容性好,经过大量用户测试
**推荐插件**
1. **WP Githuber MD** - 功能强大的 Markdown 编辑器
2. **Markdown Block** - Gutenberg 原生 Markdown 块
3. **Code Syntax Block** - 支持 Mermaid 的代码块插件
**实施步骤**
1. 安装并激活插件
2. 在插件设置中启用 Mermaid 支持
3. 测试各种图表类型
4. 根据需要调整样式
### 方案 B: 要求用户手动格式化代码
**优点**
- 无需修改主题代码
- 用户完全控制代码格式
**缺点**
- 用户体验差
- 容易出错
**实施步骤**
1. 在主题文档中说明 Mermaid 代码必须有正确的换行
2. 提供代码格式化工具或在线编辑器链接
3. 用户在外部工具中编辑好后,复制到 WordPress
### 方案 C: 开发完整的格式化器(不推荐)
**优点**
- 用户体验好,自动处理
**缺点**
- 开发工作量大
- 需要维护 Mermaid 语法规则
- 可能出现边缘情况无法处理
**实施步骤**
1. 研究 Mermaid 完整语法规则
2. 开发智能格式化算法
3. 处理各种边缘情况
4. 大量测试
## 技术实现参考
### 标准 Mermaid 集成方式
```html
<!-- 加载 Mermaid 库 -->
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
<!-- 初始化 -->
<script>
mermaid.initialize({
startOnLoad: true,
theme: 'default',
securityLevel: 'loose'
});
</script>
<!-- 使用 -->
<div class="mermaid">
flowchart TD
Start --> End
</div>
```
### WordPress 插件集成方式
```php
// 注册 Mermaid 脚本
function my_theme_enqueue_mermaid() {
if (is_single() || is_page()) {
wp_enqueue_script(
'mermaid',
'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js',
[],
'10.0.0',
true
);
}
}
add_action('wp_enqueue_scripts', 'my_theme_enqueue_mermaid');
// 初始化脚本
function my_theme_mermaid_init() {
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
mermaid.initialize({
startOnLoad: true,
theme: '<?php echo get_option('mermaid_theme', 'default'); ?>'
});
});
</script>
<?php
}
add_action('wp_footer', 'my_theme_mermaid_init');
```
## 测试用例
### 测试 1: 基础流程图
```mermaid
flowchart TD
Start([开始]) --> Process[处理]
Process --> Decision{判断}
Decision -->|是| End1([结束1])
Decision -->|否| End2([结束2])
```
### 测试 2: 时序图
```mermaid
sequenceDiagram
participant A as 用户
participant B as 服务器
A->>B: 发送请求
B->>A: 返回响应
```
### 测试 3: 类图
```mermaid
classDiagram
class Animal {
+String name
+int age
+makeSound()
}
class Dog {
+bark()
}
Animal <|-- Dog
```
## 相关资源
- [Mermaid 官方文档](https://mermaid.js.org/)
- [Mermaid Live Editor](https://mermaid.live/) - 在线编辑器
- [WP Githuber MD 插件](https://wordpress.org/plugins/wp-githuber-md/)
- [Markdown Block 插件](https://wordpress.org/plugins/markdown-block/)
## 结论
由于 WP-Markdown 编辑器的特殊渲染方式和 Markdown 源文件格式问题,在 Argon 主题中直接支持 Mermaid 存在技术障碍。
**建议**
1. **短期方案**:使用支持 Mermaid 的 WordPress 插件(如 WP Githuber MD
2. **长期方案**:等待 WP-Markdown 编辑器更新,或考虑更换为更现代的 Markdown 编辑器
如果必须在当前环境下实现,需要投入大量时间开发完整的 Mermaid 代码格式化器,且无法保证 100% 兼容所有语法。

View File

@@ -2824,80 +2824,6 @@ function themeoptions_page(){
</tr>
<tr><th class="subtitle"><h3 id="subsection-mermaid"><?php _e('Mermaid 图表', 'argon');?></h3></th></tr>
<tr>
<th><label><?php _e('启用 Mermaid 图表渲染', 'argon');?></label></th>
<td>
<select name="argon_enable_mermaid">
<?php $argon_enable_mermaid = get_option('argon_enable_mermaid', 'false'); ?>
<option value="false" <?php if ($argon_enable_mermaid=='false'){echo 'selected';} ?>><?php _e('不启用', 'argon');?></option>
<option value="true" <?php if ($argon_enable_mermaid=='true'){echo 'selected';} ?>><?php _e('启用', 'argon');?></option>
</select>
<p class="description"><?php _e('启用后,文章中的 Mermaid 代码块会被自动渲染为图表。使用方式:在代码块中指定语言为 mermaid', 'argon');?></p>
</td>
</tr>
<tr>
<th><label><?php _e('Mermaid 配置', 'argon');?></label></th>
<td>
<label style="display: block; margin-bottom: 10px;">
<input type="checkbox" name="argon_mermaid_use_local" value="true" <?php if (get_option('argon_mermaid_use_local')=='true'){echo 'checked';}?>/>
<?php _e('使用本地镜像', 'argon');?>
</label>
Mermaid CDN <?php _e('地址', 'argon');?>:
<input type="text" class="regular-text" name="argon_mermaid_cdn_url" value="<?php echo get_option('argon_mermaid_cdn_url') == '' ? '//cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js' : get_option('argon_mermaid_cdn_url'); ?>"/>
<p class="description"><?php _e('默认为', 'argon');?> <code>//cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js</code><br/><?php _e('勾选"使用本地镜像"后将优先使用主题内置的完整版本', 'argon');?></p>
</td>
</tr>
<tr>
<th><label><?php _e('Mermaid 主题', 'argon');?></label></th>
<td>
<select name="argon_mermaid_theme">
<?php $argon_mermaid_theme = get_option('argon_mermaid_theme', 'default'); ?>
<option value="default" <?php if ($argon_mermaid_theme=='default'){echo 'selected';} ?>><?php _e('默认', 'argon');?></option>
<option value="forest" <?php if ($argon_mermaid_theme=='forest'){echo 'selected';} ?>><?php _e('森林', 'argon');?></option>
<option value="dark" <?php if ($argon_mermaid_theme=='dark'){echo 'selected';} ?>><?php _e('暗色', 'argon');?></option>
<option value="neutral" <?php if ($argon_mermaid_theme=='neutral'){echo 'selected';} ?>><?php _e('中性', 'argon');?></option>
</select>
<p class="description"><?php _e('选择 Mermaid 图表的配色主题', 'argon');?></p>
</td>
</tr>
<tr><th class="subtitle"><h3 id="subsection-lazyload">Lazyload</h3></th></tr>
<tr>
@@ -6807,20 +6733,6 @@ function argon_update_themeoptions(){
argon_update_option('argon_lazyload_loading_style');
//Mermaid 相关
argon_update_option('argon_enable_mermaid');
argon_update_option('argon_mermaid_cdn_url');
argon_update_option('argon_mermaid_theme');
argon_update_option_checkbox('argon_mermaid_use_local');
argon_update_option_checkbox('argon_mermaid_use_local');
//图片缩放预览相关
argon_update_option('argon_enable_fancybox');

View File

@@ -9582,43 +9582,8 @@ pre.hljs-codeblock.hljs-codeblock-fullscreen .hljs-control-fullscreen:before {
pre.hljs-codeblock.hljs-codeblock-fullscreen .hljs-control-fullscreen > i:before {
content: "\f066";
}
/* ========== Mermaid 图表样式 ========== */
article .mermaid {
text-align: center;
margin: 20px 0;
padding: 20px;
background: rgba(255, 255, 255, 0.5);
border-radius: var(--card-radius);
overflow-x: auto;
}
html.darkmode article .mermaid {
background: rgba(66, 66, 66, 0.5);
}
article .mermaid svg {
max-width: 100%;
height: auto;
}
/*==========Style-Dark==========*/