# Mermaid 代码块魔改支持 - 设计文档

## 1. 设计概述

### 1.1 设计目标
实现标准 Markdown 代码块 (` ```mermaid `) 的 Mermaid 图表渲染，通过在代码高亮之前拦截并转换代码块，完全绕过 WordPress 和代码高亮的干扰。

### 1.2 核心设计理念
**提前拦截，转换容器**：在代码高亮处理之前，将 `<pre><code class="language-mermaid">` 转换为 `<div class="mermaid-from-codeblock">`，使其不被代码高亮处理，同时能被 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 代码

**支持格式**：
- `<div class="mermaid-from-codeblock">`（新增）
- `<div class="mermaid-shortcode">`
- `<div class="mermaid">`
- `<pre><code class="language-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:
<pre><code class="language-mermaid">
flowchart TD
    A --> B
</code></pre>

↓ convertMermaidCodeblocks()

转换后 HTML:
<div class="mermaid-from-codeblock" data-processed="true">
flowchart TD
    A --> B
</div>

↓ detectMermaidBlocks()

检测到的代码:
"flowchart TD\n    A --> B"

↓ mermaid.init()

渲染后 HTML:
<div class="mermaid-from-codeblock" data-processed="true">
    <svg>...</svg>
</div>
```

## 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);
```

**设计理由**：
- 优先替换整个 `<pre>` 元素
- 如果没有 `<pre>` 包裹，替换 `<code>` 元素
- 保留原始位置和上下文

### 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 代码块
 * 将 <pre><code class="language-mermaid"> 转换为 <div class="mermaid-from-codeblock">
 * 
 * @returns {void}
 */
function convertMermaidCodeblocks() {
	// 实现逻辑
}
```

### 4.2 数据结构

#### 容器元素结构
```html
<div class="mermaid-from-codeblock" data-processed="true">
	flowchart TD
	    A --> B
</div>
```

**属性说明**：
- `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
// 输入
<pre><code class="language-mermaid">
flowchart TD
    A --> B
</code></pre>

// 预期输出
<div class="mermaid-from-codeblock" data-processed="true">
flowchart TD
    A --> B
</div>
```

#### 测试用例 2：空代码块处理
```javascript
// 输入
<pre><code class="language-mermaid"></code></pre>

// 预期输出
<pre><code class="language-mermaid"></code></pre>  // 不转换
```

#### 测试用例 3：重复处理防护
```javascript
// 第一次处理
convertMermaidCodeblocks();  // 转换成功

// 第二次处理
convertMermaidCodeblocks();  // 跳过已处理的元素
```

#### 测试用例 4：特殊字符保留
```javascript
// 输入
<pre><code class="language-mermaid">
A --> B
C -- text --> D
</code></pre>

// 预期输出
代码中的 --> 和 -- 保持不变
```

### 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
<!DOCTYPE html>
<html>
<head>
	<title>Mermaid 代码块魔改测试</title>
</head>
<body>
	<!-- 测试用例 1：标准格式 -->
	<pre><code class="language-mermaid">
	flowchart TD
	    A --> B
	</code></pre>
	
	<!-- 测试用例 2：多个代码块 -->
	<pre><code class="language-mermaid">
	graph LR
	    C --> D
	</code></pre>
	
	<!-- 测试用例 3：特殊字符 -->
	<pre><code class="language-mermaid">
	flowchart TD
	    A -- text --> B
	    C ==> D
	</code></pre>
	
	<!-- 测试用例 4：空代码块 -->
	<pre><code class="language-mermaid"></code></pre>
</body>
</html>
```

## 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 切换正常工作
- ✅ 性能无明显影响
- ✅ 兼容所有主流浏览器
