# Mermaid 代码块魔改支持 - 设计文档 ## 1. 设计概述 ### 1.1 设计目标 实现标准 Markdown 代码块 (` ```mermaid `) 的 Mermaid 图表渲染,通过在代码高亮之前拦截并转换代码块,完全绕过 WordPress 和代码高亮的干扰。 ### 1.2 核心设计理念 **提前拦截,转换容器**:在代码高亮处理之前,将 `
` 转换为 `
`,使其不被代码高亮处理,同时能被 Mermaid 渲染系统识别。 ### 1.3 设计参考 参考主题中数学公式的实现方式: - 数学公式使用特殊分隔符(`$...$`),不会被代码高亮处理 - Mermaid 代码块通过提前转换,达到类似效果 - 两者都在 PJAX 加载后重新处理 ### 1.4 技术栈 - **JavaScript**:原生 JavaScript + jQuery(主题现有技术栈) - **Mermaid.js**:主题已集成的图表渲染库 - **执行时机**:代码高亮之前(`highlightJsRender()` 函数开始处) ## 2. 架构设计 ### 2.1 整体流程 ```mermaid flowchart TD A[页面加载/PJAX切换] --> B[highlightJsRender 调用] B --> C[convertMermaidCodeblocks 执行] C --> D[查找 mermaid 代码块] D --> E{找到代码块?} E -->|是| F[提取纯文本代码] E -->|否| G[继续代码高亮] F --> H[创建 mermaid-from-codeblock 容器] H --> I[替换原始代码块] I --> J[标记已处理] J --> G G --> K[代码高亮处理其他代码块] K --> L[detectMermaidBlocks 检测] L --> M[提取 Mermaid 代码] M --> N[mermaid.init 渲染] ``` ### 2.2 模块设计 #### 模块 1:代码块转换器 (convertMermaidCodeblocks) **职责**:在代码高亮前拦截并转换 mermaid 代码块 **输入**:DOM 树(包含未处理的代码块) **输出**:转换后的 DOM 树(mermaid 代码块已替换为容器) **核心逻辑**: 1. 使用多个选择器查找代码块 2. 提取纯文本代码 3. 创建新容器 4. 替换原始元素 #### 模块 2:代码提取器 (extractMermaidCode) **职责**:从不同格式的容器中提取 Mermaid 代码 **输入**:DOM 元素(可能是 div、pre、code) **输出**:纯文本 Mermaid 代码 **支持格式**: - `
`(新增) - `
` - `
` - `
`(降级)

#### 模块 3:Mermaid 检测器 (detectMermaidBlocks)
**职责**:检测页面中所有需要渲染的 Mermaid 容器

**输入**:DOM 树
**输出**:需要渲染的元素列表

**检测优先级**:
1. `div.mermaid-shortcode`(Shortcode 格式)
2. `div.mermaid-from-codeblock`(代码块魔改格式)
3. `div.mermaid`(标准格式)
4. `pre code.language-mermaid`(降级格式)

### 2.3 数据流设计

```
原始 HTML:

flowchart TD
    A --> B
↓ convertMermaidCodeblocks() 转换后 HTML:
flowchart TD A --> B
↓ detectMermaidBlocks() 检测到的代码: "flowchart TD\n A --> B" ↓ mermaid.init() 渲染后 HTML:
...
``` ## 3. 详细设计 ### 3.1 代码块转换函数 #### 函数签名 ```javascript function convertMermaidCodeblocks() ``` #### 实现位置 `argontheme.js` 第 3942 行之前(`highlightJsRender()` 函数开始处) #### 选择器设计 ```javascript const selectors = [ 'pre > code.language-mermaid', // 标准 Markdown 格式(最常见) 'pre > code.mermaid', // 简化格式 'code.language-mermaid', // 无 pre 包裹 'pre[data-lang="mermaid"]' // 自定义属性格式 ]; ``` **设计理由**: - 支持多种插件生成的 HTML 结构 - 优先匹配最常见的格式 - 提供降级支持 #### 重复处理防护 ```javascript if (element.dataset.mermaidProcessed) { return; // 跳过已处理的元素 } ``` **设计理由**: - 避免 PJAX 切换时重复处理 - 防止多次调用导致的错误 - 使用 data 属性标记状态 #### 代码提取逻辑 ```javascript let code = element.textContent.trim(); ``` **设计理由**: - `textContent` 获取纯文本,避免 HTML 实体 - `trim()` 移除前后空白 - 不进行任何字符转换,保持原始内容 #### 容器创建逻辑 ```javascript const container = document.createElement('div'); container.className = 'mermaid-from-codeblock'; container.textContent = code; container.dataset.processed = 'true'; ``` **设计理由**: - 使用 `textContent` 而非 `innerHTML`,避免 XSS - 添加特定类名,便于识别来源 - 标记已处理状态 #### 元素替换逻辑 ```javascript const targetElement = element.closest('pre') || element; targetElement.parentNode.replaceChild(container, targetElement); ``` **设计理由**: - 优先替换整个 `
` 元素
- 如果没有 `
` 包裹,替换 `` 元素
- 保留原始位置和上下文

### 3.2 集成点设计

#### 集成点 1:highlightJsRender() 函数
**位置**:`argontheme.js` 第 3942 行

**修改前**:
```javascript
function highlightJsRender(){
	if (typeof(hljs) == "undefined"){
		return;
	}
	// ... 代码高亮逻辑
}
```

**修改后**:
```javascript
function highlightJsRender(){
	// 在代码高亮之前,先处理 Mermaid 代码块
	convertMermaidCodeblocks();
	
	if (typeof(hljs) == "undefined"){
		return;
	}
	// ... 代码高亮逻辑
}
```

**设计理由**:
- 在代码高亮之前执行,确保 mermaid 代码块不被处理
- 不影响其他代码块的高亮
- 执行顺序:转换 → 高亮 → 渲染

#### 集成点 2:detectMermaidBlocks() 函数
**位置**:`argontheme.js` 第 4430 行

**修改前**:
```javascript
const selectors = [
	'div.mermaid-shortcode',
	'div.mermaid',
	'pre code.language-mermaid',
	// ...
];
```

**修改后**:
```javascript
const selectors = [
	'div.mermaid-shortcode',         // Shortcode 格式
	'div.mermaid-from-codeblock',    // 代码块魔改格式(新增)
	'div.mermaid',                   // 标准格式
	'pre code.language-mermaid',     // Markdown 格式(降级)
	'pre[data-lang="mermaid"]',      // 自定义属性格式
	'code.mermaid'                   // 简化格式
];
```

**设计理由**:
- 添加新的容器类型到检测列表
- 优先级高于标准 `mermaid` 类
- 保留降级选择器,确保兼容性

#### 集成点 3:extractMermaidCode() 函数
**位置**:`argontheme.js` 第 4650 行

**修改前**:
```javascript
// 处理 Shortcode 格式
if (element.classList.contains('mermaid-shortcode')) {
	code = element.textContent;
	this.logDebug('从 Shortcode 格式提取代码');
}
```

**修改后**:
```javascript
// 处理 Shortcode 格式
if (element.classList.contains('mermaid-shortcode')) {
	code = element.textContent;
	this.logDebug('从 Shortcode 格式提取代码');
}
// 处理代码块魔改格式
else if (element.classList.contains('mermaid-from-codeblock')) {
	code = element.textContent;
	this.logDebug('从代码块魔改格式提取代码');
}
```

**设计理由**:
- 与 Shortcode 格式使用相同的提取方式
- 添加调试日志,便于追踪
- 保持代码一致性

### 3.3 PJAX 兼容设计

#### 执行时机
PJAX 加载完成后的回调链(`argontheme.js` 第 2862-2890 行):
```javascript
$(document).on('pjax:complete', function() {
	// ... 其他初始化
	try { highlightJsRender(); } catch (err) { ... }  // 包含代码块转换
	// ... 其他初始化
});
```

**设计理由**:
- `highlightJsRender()` 已在 PJAX 回调中调用
- 代码块转换自动在每次 PJAX 切换后执行
- 无需额外修改 PJAX 逻辑

#### 重复处理防护
使用 `data-processed` 属性标记已处理的元素:
```javascript
if (element.dataset.mermaidProcessed) {
	return;
}
// ... 处理逻辑
element.dataset.mermaidProcessed = 'true';
```

**设计理由**:
- 避免同一元素被多次转换
- 支持 PJAX 页面切换
- 轻量级标记,不影响性能

### 3.4 错误处理设计

#### 空代码检查
```javascript
let code = element.textContent.trim();
if (!code) {
	return; // 跳过空代码块
}
```

**设计理由**:
- 避免创建空容器
- 减少不必要的 DOM 操作
- 提高性能

#### Try-Catch 包裹
```javascript
try {
	convertMermaidCodeblocks();
} catch (err) {
	console.error('Mermaid 代码块转换失败:', err);
}
```

**设计理由**:
- 捕获异常,不中断其他代码块的处理
- 记录错误日志,便于调试
- 提供降级方案(代码块仍可通过降级选择器检测)

#### 降级支持
如果代码块转换失败,仍可通过降级选择器检测:
```javascript
'pre code.language-mermaid'  // 降级选择器
```

**设计理由**:
- 确保即使转换失败,仍能渲染
- 提供多层保障
- 增强系统健壮性

## 4. 接口设计

### 4.1 公共函数

#### convertMermaidCodeblocks()
```javascript
/**
 * 在代码高亮之前转换 Mermaid 代码块
 * 将 
 转换为 
* * @returns {void} */ function convertMermaidCodeblocks() { // 实现逻辑 } ``` ### 4.2 数据结构 #### 容器元素结构 ```html
flowchart TD A --> B
``` **属性说明**: - `class="mermaid-from-codeblock"`:标识来源于代码块 - `data-processed="true"`:标记已处理 - 内容:纯文本 Mermaid 代码 ### 4.3 选择器优先级 | 优先级 | 选择器 | 用途 | |--------|--------|------| | 1 | `pre > code.language-mermaid` | 标准 Markdown 格式 | | 2 | `pre > code.mermaid` | 简化格式 | | 3 | `code.language-mermaid` | 无 pre 包裹 | | 4 | `pre[data-lang="mermaid"]` | 自定义属性格式 | ## 5. 性能设计 ### 5.1 性能目标 - 单个代码块处理时间 < 10ms - 不影响页面加载速度 - 不增加额外的 HTTP 请求 ### 5.2 性能优化策略 #### 优化 1:使用原生 JavaScript ```javascript document.querySelectorAll(selector) // 而非 $(selector) ``` **理由**:原生方法性能更好,减少 jQuery 开销 #### 优化 2:提前返回 ```javascript if (element.dataset.mermaidProcessed) { return; // 提前返回,避免不必要的处理 } ``` **理由**:减少重复处理,提高效率 #### 优化 3:批量处理 ```javascript selectors.forEach(selector => { document.querySelectorAll(selector).forEach(element => { // 处理逻辑 }); }); ``` **理由**:一次性查找所有元素,减少 DOM 查询次数 #### 优化 4:最小化 DOM 操作 ```javascript const container = document.createElement('div'); container.className = 'mermaid-from-codeblock'; container.textContent = code; container.dataset.processed = 'true'; targetElement.parentNode.replaceChild(container, targetElement); ``` **理由**:一次性创建和替换,减少重排和重绘 ### 5.3 性能监控 #### 调试日志 ```javascript this.logDebug('处理了 ' + count + ' 个 Mermaid 代码块'); this.logDebug('代码内容(前100字符): ' + code.substring(0, 100)); ``` **理由**:便于追踪性能问题,不影响生产环境 ## 6. 安全设计 ### 6.1 XSS 防护 #### 使用 textContent 而非 innerHTML ```javascript container.textContent = code; // 安全 // container.innerHTML = code; // 不安全 ``` **理由**:`textContent` 会自动转义 HTML,防止 XSS 攻击 #### 代码来源验证 ```javascript let code = element.textContent.trim(); if (!code) { return; // 拒绝空代码 } ``` **理由**:避免处理恶意或无效的代码块 ### 6.2 DOM 操作安全 #### 安全的元素替换 ```javascript const targetElement = element.closest('pre') || element; if (targetElement.parentNode) { targetElement.parentNode.replaceChild(container, targetElement); } ``` **理由**:检查父节点存在,避免 null 引用错误 ## 7. 测试设计 ### 7.1 单元测试 #### 测试用例 1:标准代码块转换 ```javascript // 输入

flowchart TD
    A --> B
// 预期输出
flowchart TD A --> B
``` #### 测试用例 2:空代码块处理 ```javascript // 输入
// 预期输出
// 不转换 ``` #### 测试用例 3:重复处理防护 ```javascript // 第一次处理 convertMermaidCodeblocks(); // 转换成功 // 第二次处理 convertMermaidCodeblocks(); // 跳过已处理的元素 ``` #### 测试用例 4:特殊字符保留 ```javascript // 输入

A --> B
C -- text --> D
// 预期输出 代码中的 --> 和 -- 保持不变 ``` ### 7.2 集成测试 #### 测试场景 1:与代码高亮集成 1. 页面包含 mermaid 代码块和其他代码块 2. 调用 `highlightJsRender()` 3. 验证 mermaid 代码块被转换,其他代码块被高亮 #### 测试场景 2:与 Mermaid 渲染集成 1. 页面包含转换后的容器 2. 调用 `detectMermaidBlocks()` 3. 验证容器被检测到 4. 调用 `mermaid.init()` 5. 验证图表正确渲染 #### 测试场景 3:PJAX 兼容性 1. 初始页面加载,代码块正确转换和渲染 2. PJAX 切换到新页面 3. 验证新页面的代码块正确转换和渲染 4. 验证已转换的代码块不被重复处理 ### 7.3 浏览器测试 #### 测试矩阵 | 浏览器 | 版本 | 测试项 | |--------|------|--------| | Chrome | 最新版 | 全部功能 | | Firefox | 最新版 | 全部功能 | | Safari | 最新版 | 全部功能 | | Edge | 最新版 | 全部功能 | #### 测试项 - 代码块转换 - 图表渲染 - PJAX 切换 - 性能表现 ### 7.4 测试文件 创建 `tests/test-codeblock-magic.html`: ```html Mermaid 代码块魔改测试

	flowchart TD
	    A --> B
	

	graph LR
	    C --> D
	

	flowchart TD
	    A -- text --> B
	    C ==> D
	
``` ## 8. 部署设计 ### 8.1 部署步骤 #### 步骤 1:修改 argontheme.js 1. 在 `highlightJsRender()` 函数开始处添加 `convertMermaidCodeblocks()` 调用 2. 实现 `convertMermaidCodeblocks()` 函数 3. 修改 `detectMermaidBlocks()` 函数,添加新选择器 4. 修改 `extractMermaidCode()` 函数,支持新容器类型 #### 步骤 2:测试验证 1. 创建测试文件 `tests/test-codeblock-magic.html` 2. 在本地环境测试所有用例 3. 验证 PJAX 兼容性 4. 检查浏览器控制台无错误 #### 步骤 3:文档更新 1. 更新 `docs/mermaid-usage-guide.md` 2. 更新 `docs/mermaid-developer-guide.md` 3. 更新 `docs/mermaid-faq.md` #### 步骤 4:发布 1. 提交代码到 Git 2. 更新版本号 3. 发布更新 ### 8.2 回滚方案 如果出现问题,可以快速回滚: 1. 移除 `convertMermaidCodeblocks()` 调用 2. 移除新添加的选择器 3. 恢复原始代码 **降级方案**: - 用户仍可使用 Shortcode 格式 - 用户仍可使用容器语法 - 不影响现有功能 ### 8.3 兼容性保证 #### 向后兼容 - 不影响现有的 Shortcode 格式 - 不影响现有的容器语法 - 不影响其他代码块的高亮 #### 向前兼容 - 预留扩展接口 - 支持未来的新格式 - 易于维护和升级 ## 9. 监控与维护 ### 9.1 日志设计 #### 调试日志 ```javascript this.logDebug('开始转换 Mermaid 代码块'); this.logDebug('找到 ' + elements.length + ' 个代码块'); this.logDebug('代码内容: ' + code.substring(0, 100)); this.logDebug('转换完成'); ``` #### 错误日志 ```javascript console.error('Mermaid 代码块转换失败:', err); console.error('元素:', element); console.error('代码:', code); ``` ### 9.2 性能监控 #### 性能指标 - 代码块转换时间 - 页面加载时间 - 内存使用情况 #### 监控方法 ```javascript const startTime = performance.now(); convertMermaidCodeblocks(); const endTime = performance.now(); console.log('转换耗时:', endTime - startTime, 'ms'); ``` ### 9.3 维护计划 #### 定期检查 - 每月检查浏览器兼容性 - 每季度检查性能表现 - 每半年检查代码质量 #### 更新策略 - 跟进 Mermaid.js 版本更新 - 跟进 WordPress 版本更新 - 跟进浏览器标准更新 ## 10. 风险与缓解 ### 10.1 技术风险 #### 风险 1:与其他插件冲突 **影响**:中等 **概率**:低 **缓解措施**: - 使用唯一的类名 `mermaid-from-codeblock` - 添加命名空间,避免冲突 - 提供配置选项,允许禁用 #### 风险 2:性能问题 **影响**:低 **概率**:低 **缓解措施**: - 优化选择器,减少 DOM 查询 - 使用缓存,避免重复处理 - 添加性能监控 #### 风险 3:浏览器兼容性 **影响**:中等 **概率**:低 **缓解措施**: - 使用标准 API - 添加 polyfill - 提供降级方案 ### 10.2 用户体验风险 #### 风险 1:误转换其他代码块 **影响**:高 **概率**:极低 **缓解措施**: - 使用精确的选择器 - 添加类名检查 - 提供白名单机制 #### 风险 2:特殊字符丢失 **影响**:高 **概率**:极低 **缓解措施**: - 使用 `textContent` 保留原始内容 - 不进行任何字符转换 - 添加测试用例验证 ## 11. 未来扩展 ### 11.1 短期扩展(1-2 周) #### 配置选项 添加主题设置选项: - 是否启用代码块魔改 - 选择器优先级配置 - 调试模式开关 #### 性能优化 - 添加缓存机制 - 批量处理优化 - 延迟加载支持 ### 11.2 中期扩展(1-2 月) #### 编辑器支持 - 添加编辑器预览功能 - 支持实时渲染 - 提供语法提示 #### 移动端优化 - 优化移动端显示 - 支持触摸交互 - 响应式布局 ### 11.3 长期扩展(3-6 月) #### 多图表库支持 - 支持 PlantUML - 支持 GraphViz - 支持 D3.js #### 高级功能 - 图表编辑器 - 图表导出(PNG、SVG) - 图表分享 ## 12. 总结 ### 12.1 设计优势 1. **简单高效**:只需添加一个函数,修改三个位置 2. **兼容性好**:不影响现有功能,支持多种格式 3. **性能优秀**:使用原生 JavaScript,优化 DOM 操作 4. **易于维护**:代码清晰,注释详细,易于理解 5. **安全可靠**:防止 XSS,处理异常,提供降级 ### 12.2 关键决策 | 决策 | 理由 | |------|------| | 在代码高亮前拦截 | 避免代码高亮干扰 | | 使用 textContent | 保留原始内容,防止 XSS | | 创建新容器类型 | 便于识别来源,支持多种格式 | | 添加重复处理防护 | 支持 PJAX,避免重复转换 | | 提供降级支持 | 确保即使转换失败仍能渲染 | ### 12.3 实施建议 1. **分步实施**:先实现核心功能,再添加优化 2. **充分测试**:创建完整的测试用例,覆盖所有场景 3. **文档完善**:更新用户文档和开发者文档 4. **监控反馈**:收集用户反馈,持续优化 ### 12.4 成功标准 - ✅ 可以使用 ` ```mermaid ` 代码块编写图表 - ✅ 代码块不会被代码高亮处理 - ✅ 图表正确渲染 - ✅ 特殊字符不被转换 - ✅ 换行符正确保留 - ✅ PJAX 切换正常工作 - ✅ 性能无明显影响 - ✅ 兼容所有主流浏览器