Files
argon-theme/.kiro/specs/mermaid-codeblock-magic/design.md
nanhaoluo 29bfd284e0 feat: 实现 Mermaid 代码块魔改支持
- 添加 convertMermaidCodeblocks() 函数,在代码高亮前拦截 mermaid 代码块
- 支持标准 Markdown 代码块 (\\\mermaid) 渲染
- 更新 detectMermaidBlocks() 添加 mermaid-from-codeblock 选择器
- 更新 extractMermaidCode() 支持新容器类型
- 创建测试文件 test-codeblock-magic.html
- 更新用户文档、开发者文档和 FAQ
- 完全绕过代码高亮和 WordPress 格式化
- 支持 PJAX 页面切换
- 特殊字符和换行符正确保留
2026-01-24 21:35:12 +08:00

822 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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">`(降级)
#### 模块 3Mermaid 检测器 (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 集成点设计
#### 集成点 1highlightJsRender() 函数
**位置**`argontheme.js` 第 3942 行
**修改前**
```javascript
function highlightJsRender(){
if (typeof(hljs) == "undefined"){
return;
}
// ... 代码高亮逻辑
}
```
**修改后**
```javascript
function highlightJsRender(){
// 在代码高亮之前,先处理 Mermaid 代码块
convertMermaidCodeblocks();
if (typeof(hljs) == "undefined"){
return;
}
// ... 代码高亮逻辑
}
```
**设计理由**
- 在代码高亮之前执行,确保 mermaid 代码块不被处理
- 不影响其他代码块的高亮
- 执行顺序:转换 → 高亮 → 渲染
#### 集成点 2detectMermaidBlocks() 函数
**位置**`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`
- 保留降级选择器,确保兼容性
#### 集成点 3extractMermaidCode() 函数
**位置**`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. 验证图表正确渲染
#### 测试场景 3PJAX 兼容性
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 切换正常工作
- ✅ 性能无明显影响
- ✅ 兼容所有主流浏览器