From 0ac57949aec74a082db33426ca275d9b1eefc03e Mon Sep 17 00:00:00 2001 From: nanhaoluo <3075912108@qq.com> Date: Sat, 24 Jan 2026 20:14:48 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E9=AB=98=E4=BA=AE=E5=92=8C=E5=AE=B9=E5=99=A8=E8=AF=AD=E6=B3=95?= =?UTF-8?q?=E7=9A=84=E4=B8=A4=E4=B8=AA=E5=85=B3=E9=94=AE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 代码高亮排除 mermaid 代码块,避免干扰渲染 - 容器语法正确处理空行,不再截断内容 - 添加测试页面验证修复效果 --- argontheme.js | 131 ++++++++- docs/mermaid-usage-guide.md | 338 ++++++++++++++++++++++ tests/test-mermaid-fixes.html | 276 ++++++++++++++++++ tests/test-wp-markdown-format.html | 437 ++++++++++------------------- 4 files changed, 895 insertions(+), 287 deletions(-) create mode 100644 docs/mermaid-usage-guide.md create mode 100644 tests/test-mermaid-fixes.html diff --git a/argontheme.js b/argontheme.js index 5373a32..781caa5 100644 --- a/argontheme.js +++ b/argontheme.js @@ -3959,6 +3959,14 @@ function highlightJsRender(){ if ($(block).hasClass("no-hljs")){ return; } + // 跳过 mermaid 代码块(避免代码高亮干扰 mermaid 渲染) + if ($(block).hasClass("language-mermaid") || $(block).hasClass("mermaid")){ + return; + } + // 跳过在 .mermaid 容器内的代码块 + if ($(block).closest('.mermaid').length > 0){ + return; + } $(block).parent().attr("id", randomString()); hljs.highlightBlock(block); hljs.lineNumbersBlock(block, {singleLine: true}); @@ -4440,6 +4448,10 @@ void 0; }); }); + // 检测 Markdown 容器语法的 Mermaid 代码块 + // 格式: ::: mermaid ... ::: + this.detectContainerBlocks(blocks); + this.logDebug(`检测到 ${blocks.length} 个 Mermaid 代码块`); return blocks; }, @@ -4460,6 +4472,79 @@ void 0; return false; }, + /** + * 检测 Markdown 容器语法的 Mermaid 代码块 + * 格式: ::: mermaid ... ::: + * @param {Array} blocks - 代码块数组 + */ + detectContainerBlocks(blocks) { + // 查找所有包含 ::: mermaid 的文本节点或 pre/code 元素 + const walker = document.createTreeWalker( + document.body, + NodeFilter.SHOW_ELEMENT, + { + acceptNode: function(node) { + // 检查 pre 或 code 元素 + if (node.tagName === 'PRE' || node.tagName === 'CODE') { + const text = node.textContent; + if (text && text.trim().startsWith('::: mermaid')) { + return NodeFilter.FILTER_ACCEPT; + } + } + // 检查 p 元素(可能被 Markdown 解析器包裹) + if (node.tagName === 'P') { + const text = node.textContent; + if (text && text.trim().startsWith('::: mermaid')) { + return NodeFilter.FILTER_ACCEPT; + } + } + return NodeFilter.FILTER_SKIP; + } + }, + false + ); + + while (walker.nextNode()) { + const element = walker.currentNode; + const container = this.extractContainerContent(element); + if (container && !blocks.includes(container)) { + blocks.push(container); + this.logDebug('检测到 Markdown 容器语法的 Mermaid 代码块'); + } + } + }, + + /** + * 提取 Markdown 容器语法的内容 + * @param {HTMLElement} element - 包含容器语法的元素 + * @returns {HTMLElement|null} 包含代码的容器元素 + */ + extractContainerContent(element) { + let text = element.textContent.trim(); + + // 移除开始标记 ::: mermaid(保留后面的换行符和空行) + text = text.replace(/^:::\s*mermaid\s*\n?/i, ''); + + // 移除结束标记 :::(保留前面的换行符和空行) + text = text.replace(/\n?:::\s*$/i, ''); + + // 不要 trim,保留代码中的空行 + if (!text) { + return null; + } + + // 创建一个新的容器来存储代码 + const container = document.createElement('div'); + container.className = 'mermaid-container-block'; + container.textContent = text; + container.dataset.containerBlock = 'true'; + + // 替换原始元素 + element.parentNode.replaceChild(container, element); + + return container; + }, + /** * 提取代码块内容 * @param {HTMLElement} element - 代码块元素 @@ -4468,23 +4553,46 @@ void 0; extractMermaidCode(element) { let code = ''; + // 处理 Markdown 容器语法格式 + if (element.classList.contains('mermaid-container-block')) { + code = element.textContent; + this.logDebug('从 Markdown 容器语法提取代码'); + } // 根据不同的元素类型提取代码 - if (element.tagName === 'DIV' && element.classList.contains('mermaid')) { + else if (element.tagName === 'DIV' && element.classList.contains('mermaid')) { // 检查是否包含 WP-Markdown 生成的 script 标签 const scriptTag = element.querySelector('script'); if (scriptTag) { // 提取 document.write() 中的内容 const scriptContent = scriptTag.textContent || scriptTag.innerText; - const match = scriptContent.match(/document\.write\s*\(\s*["'](.+?)["']\s*\)/); + this.logDebug('检测到 script 标签,原始内容: ' + scriptContent.substring(0, 100)); + + // 使用更精确的正则:匹配 document.write() 中的字符串内容 + // 支持双引号和单引号,支持转义字符 + // 使用贪婪匹配 [\s\S]* 来匹配包括换行在内的所有字符 + let match = scriptContent.match(/document\.write\s*\(\s*["']([\s\S]*?)["']\s*\)/); + if (match && match[1]) { code = match[1]; - this.logDebug('检测到 WP-Markdown 格式,从 script 标签提取代码'); + this.logDebug('从 document.write() 提取到代码,长度: ' + code.length); } else { - // 如果没有匹配到,使用整个元素的文本内容(排除 script 标签) - code = element.textContent; + // 如果没有匹配到 document.write(),尝试直接提取引号内的内容 + match = scriptContent.match(/["']([\s\S]*?)["']/); + if (match && match[1]) { + code = match[1]; + this.logDebug('从引号内提取到代码,长度: ' + code.length); + } else { + // 最后的降级方案:获取除 script 标签外的文本内容 + const clonedElement = element.cloneNode(true); + const scripts = clonedElement.querySelectorAll('script'); + scripts.forEach(script => script.remove()); + code = clonedElement.textContent; + this.logDebug('使用降级方案提取代码'); + } } } else { code = element.textContent; + this.logDebug('从纯文本提取代码'); } } else if (element.tagName === 'CODE') { code = element.textContent; @@ -4493,13 +4601,24 @@ void 0; code = codeElement ? codeElement.textContent : element.textContent; } - // 解码转义字符 + // 解码 HTML 实体(WordPress 可能会转义 HTML) + code = code + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, "'"); + + // 解码转义字符(必须在 HTML 实体解码之后) code = code .replace(/\\n/g, '\n') + .replace(/\\t/g, '\t') + .replace(/\\r/g, '\r') .replace(/\\"/g, '"') .replace(/\\'/g, "'") .replace(/\\\\/g, '\\'); + this.logDebug('最终提取的代码: ' + code.substring(0, 100) + (code.length > 100 ? '...' : '')); return code.trim(); }, diff --git a/docs/mermaid-usage-guide.md b/docs/mermaid-usage-guide.md new file mode 100644 index 0000000..3827b8e --- /dev/null +++ b/docs/mermaid-usage-guide.md @@ -0,0 +1,338 @@ +# Argon 主题 Mermaid 图表使用指南 + +## 推荐的标记方式 + +### Markdown 容器语法(推荐)⭐ + +```markdown +::: mermaid +flowchart TD + A[开始] --> B[处理] + B --> C[结束] +::: +``` + +**优点**: +- ✅ 符合 Markdown 扩展规范(VuePress、Docusaurus 等使用相同语法) +- ✅ 不会被 WP-Markdown 当作代码块处理,避免嵌套问题 +- ✅ 语法简洁,易于编写和阅读 +- ✅ 支持所有 Mermaid 图表类型 +- ✅ 在纯文本编辑器中也很清晰 +- ✅ 易于迁移到其他平台 + +### 为什么使用容器语法? + +**Markdown 容器语法** (`::: mermaid ... :::`) 是现代 Markdown 扩展的标准方式: + +- **VuePress** 使用这种语法 +- **Docusaurus** 使用这种语法 +- **MkDocs** 使用类似语法 +- **符合 CommonMark 扩展规范** + +这种语法不会被 WP-Markdown 插件当作代码块处理,因此: +- 不会被套上代码高亮窗口 +- 不会有嵌套结构问题 +- 不会有字符转义问题 + +--- + +## 使用示例 + +### 流程图 + +```markdown +::: mermaid +flowchart LR + A[用户] --> B{登录?} + B -->|是| C[显示首页] + B -->|否| D[跳转登录页] +::: +``` + +### 时序图 + +```markdown +::: mermaid +sequenceDiagram + Alice->>Bob: Hello Bob! + Bob-->>Alice: Hi Alice! + Alice->>Bob: How are you? +::: +``` + +### 类图 + +```markdown +::: mermaid +classDiagram + class Animal { + +String name + +int age + +makeSound() + } + class Dog { + +String breed + +bark() + } + Animal <|-- Dog +::: +``` + +### 甘特图 + +```markdown +::: mermaid +gantt + title 项目进度 + dateFormat YYYY-MM-DD + section 设计 + 需求分析 :a1, 2024-01-01, 7d + UI设计 :a2, after a1, 5d + section 开发 + 前端开发 :b1, after a2, 10d + 后端开发 :b2, after a2, 12d +::: +``` + +### 状态图 + +```markdown +::: mermaid +stateDiagram-v2 + [*] --> 待审核 + 待审核 --> 已通过: 审核通过 + 待审核 --> 已拒绝: 审核拒绝 + 已通过 --> [*] + 已拒绝 --> [*] +::: +``` + +### 饼图 + +```markdown +::: mermaid +pie title 宠物分布 + "狗" : 386 + "猫" : 85 + "兔子" : 15 +::: +``` + +--- + +## 常见问题 + +### 1. 如何在 WordPress 编辑器中使用? + +**在经典编辑器中:** +1. 切换到"文本"模式(不是"可视化"模式) +2. 直接输入容器语法 +3. 保存并预览 + +**在 Gutenberg 编辑器中:** +1. 添加"自定义 HTML"块或"代码"块 +2. 输入容器语法 +3. 保存并预览 + +### 2. 已有文章如何迁移? + +如果你的文章使用了传统的 Markdown 代码块,可以批量替换: + +**查找:** +``` +三个反引号mermaid +``` + +**替换为:** +``` +::: mermaid +``` + +**查找:** +``` +三个反引号(代码块结束) +``` + +**替换为:** +``` +::: +``` + +### 3. 容器语法不生效怎么办? + +**可能的原因:** +- 其他插件或主题处理了容器语法 +- WP-Markdown 插件版本过旧 + +**解决方案:** +1. 检查是否有其他 Markdown 插件冲突 +2. 更新 WP-Markdown 插件到最新版本 +3. 查看浏览器控制台的错误信息 + +### 4. 渲染失败怎么办? + +**排查步骤:** + +1. **检查语法** + - 访问 [Mermaid Live Editor](https://mermaid.live/) 验证语法 + - 确保图表类型正确 + +2. **查看控制台** + - 按 F12 打开开发者工具 + - 查看 Console 标签页 + - 搜索 `[Argon Mermaid]` 日志 + +3. **检查主题设置** + - WordPress 后台 → 外观 → Argon 主题选项 + - 找到"Mermaid 图表"分类 + - 确认"启用 Mermaid 支持"已开启 + +4. **测试 CDN 连接** + - 访问测试页面验证 CDN 是否可用 + - 如果 CDN 失败,尝试切换到其他 CDN + +--- + +## 最佳实践 + +### 1. 使用容器语法 + +**推荐:** +```markdown +::: mermaid +flowchart TD + A --> B +::: +``` + +**不推荐:** +``` +三个反引号mermaid +flowchart TD + A --> B +三个反引号 +``` + +### 2. 避免特殊字符 + +如果图表中包含特殊字符,使用引号包裹: + +```markdown +::: mermaid +flowchart TD + A["包含 <特殊> 字符"] --> B["使用引号包裹"] +::: +``` + +### 3. 测试复杂图表 + +对于复杂的图表: +1. 先在 [Mermaid Live Editor](https://mermaid.live/) 中测试 +2. 确认语法正确后再粘贴到文章中 +3. 发布前预览文章 + +### 4. 启用调试模式 + +如果遇到问题: +1. 主题设置 → Mermaid 图表 → 基本设置 +2. 开启"调试模式" +3. 刷新页面查看控制台日志 + +### 5. 保持代码简洁 + +- 使用有意义的节点 ID +- 添加适当的注释 +- 保持图表结构清晰 + +--- + +## 技术细节 + +### 支持的标记格式 + +Argon 主题支持以下格式(按优先级排序): + +1. `::: mermaid ... :::` - Markdown 容器语法 ⭐(推荐) +2. `
` - Markdown 格式
+4. `` - 自定义属性格式
+5. `` - 简化格式
+
+### 代码提取逻辑
+
+1. **检测代码块**
+ - CSS 选择器查找标准格式
+ - TreeWalker 查找容器语法
+
+2. **提取代码**
+ - 容器语法:移除 `::: mermaid` 和 `:::`
+ - WPMD 格式:正则提取 `document.write()` 内容
+ - 标准格式:直接提取文本内容
+
+3. **解码处理**
+ - 先解码 HTML 实体(`<` → `<`)
+ - 再解码转义字符(`\n` → 换行符)
+
+4. **渲染图表**
+ - 使用 Mermaid.js 库渲染为 SVG
+ - 应用主题样式(夜间模式适配)
+ - 错误时显示友好提示
+
+---
+
+## 故障排除
+
+### 问题:容器语法被显示为普通文本
+
+**症状**:`::: mermaid` 被显示在页面上
+
+**原因**:可能被其他插件或主题处理为普通文本
+
+**解决**:
+- 检查是否有其他 Markdown 插件冲突
+- 确认 Argon 主题已更新到最新版本
+- 查看浏览器控制台是否有 JavaScript 错误
+
+### 问题:只显示第一个单词
+
+**症状**:图表渲染失败,错误信息显示 `"text": "flowchart"`
+
+**原因**:代码提取不完整
+
+**解决**:
+- 确保使用最新版本的 Argon 主题
+- 使用容器语法而不是传统代码块
+- 查看控制台日志确认提取到的完整代码
+
+### 问题:HTML 实体未解码
+
+**症状**:图表中显示 `<` 而不是 `<`
+
+**原因**:HTML 实体解码失败
+
+**解决**:
+- Argon 主题会自动解码 HTML 实体
+- 使用容器语法可避免此问题
+- 在 Mermaid 代码中使用引号包裹特殊字符
+
+---
+
+## 相关资源
+
+- [Mermaid 官方文档](https://mermaid.js.org/)
+- [Mermaid Live Editor](https://mermaid.live/)
+- [VuePress 容器语法](https://vuepress.vuejs.org/guide/markdown.html#custom-containers)
+- [CommonMark 规范](https://commonmark.org/)
+
+---
+
+## 更新日志
+
+### 2026-01-24
+- ✅ 添加 Markdown 容器语法支持(`::: mermaid ... :::`)
+- ✅ 修复 WP-Markdown 格式的代码提取问题
+- ✅ 改进正则表达式,支持多行代码
+- ✅ 添加降级方案和详细日志
+- ✅ 简化标记方式,只保留容器语法作为推荐方式
+- ✅ 修复代码高亮干扰 mermaid 渲染的问题(排除 mermaid 代码块)
+- ✅ 修复容器语法中空行导致内容被截断的问题
diff --git a/tests/test-mermaid-fixes.html b/tests/test-mermaid-fixes.html
new file mode 100644
index 0000000..c10b428
--- /dev/null
+++ b/tests/test-mermaid-fixes.html
@@ -0,0 +1,276 @@
+
+
+
+
+
+ Mermaid 修复测试
+
+
+
+ 🧪 Mermaid 修复测试页面
+ 本页面用于测试两个关键修复:
+
+ - 代码高亮排除 mermaid:mermaid 代码块不应被代码高亮处理
+ - 容器语法处理空行:容器语法中的空行应被正确保留
+
+
+
+
+ 测试 1: 代码高亮排除 Mermaid
+
+
+ 测试 1.1: 标准 Markdown 格式(应该被排除)
+ ✅ 预期:不应出现代码高亮窗口,直接渲染为 Mermaid 图表
+
+flowchart TD
+ A[开始] --> B[处理]
+ B --> C[结束]
+
+
+
+
+ 测试 1.2: 标准 div.mermaid 格式(应该被排除)
+ ✅ 预期:不应出现代码高亮窗口,直接渲染为 Mermaid 图表
+
+flowchart LR
+ A[用户] --> B{登录?}
+ B -->|是| C[显示首页]
+ B -->|否| D[跳转登录页]
+
+
+
+
+ 测试 1.3: 普通代码块(应该被高亮)
+ ✅ 预期:应该出现代码高亮窗口
+
+function hello() {
+ console.log('Hello World');
+}
+
+
+
+
+
+
+ 测试 2: 容器语法空行处理
+
+
+ 测试 2.1: 包含空行的流程图
+ ✅ 预期:空行应被保留,图表正常渲染
+
+::: mermaid
+flowchart TD
+ A[开始]
+
+ B[处理步骤1]
+
+ C[处理步骤2]
+
+ D[结束]
+
+ A --> B
+ B --> C
+ C --> D
+:::
+
+ 实际渲染:
+
+::: mermaid
+flowchart TD
+ A[开始]
+
+ B[处理步骤1]
+
+ C[处理步骤2]
+
+ D[结束]
+
+ A --> B
+ B --> C
+ C --> D
+:::
+
+
+
+
+ 测试 2.2: 包含空行的时序图
+ ✅ 预期:空行应被保留,图表正常渲染
+
+::: mermaid
+sequenceDiagram
+ participant A as Alice
+ participant B as Bob
+
+ A->>B: Hello Bob!
+
+ Note over A,B: 这是一个注释
+
+ B-->>A: Hi Alice!
+:::
+
+ 实际渲染:
+
+::: mermaid
+sequenceDiagram
+ participant A as Alice
+ participant B as Bob
+
+ A->>B: Hello Bob!
+
+ Note over A,B: 这是一个注释
+
+ B-->>A: Hi Alice!
+:::
+
+
+
+
+ 测试 2.3: 包含多个连续空行
+ ✅ 预期:多个空行应被保留,图表正常渲染
+
+::: mermaid
+flowchart LR
+ A[步骤A]
+
+
+ B[步骤B]
+
+
+ C[步骤C]
+
+ A --> B --> C
+:::
+
+ 实际渲染:
+
+::: mermaid
+flowchart LR
+ A[步骤A]
+
+
+ B[步骤B]
+
+
+ C[步骤C]
+
+ A --> B --> C
+:::
+
+
+
+
+
+
+ 测试 3: WP-Markdown 格式兼容性
+
+
+ 测试 3.1: document.write 格式
+ ✅ 预期:正确提取代码并渲染
+
+
+
+
+
+
+
+
+ 📋 测试检查清单
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+
+
+
+
+
+
diff --git a/tests/test-wp-markdown-format.html b/tests/test-wp-markdown-format.html
index 94c9321..d794d73 100644
--- a/tests/test-wp-markdown-format.html
+++ b/tests/test-wp-markdown-format.html
@@ -3,330 +3,205 @@
- WP-Markdown 格式测试 - Mermaid 支持
+ WP-Markdown Mermaid 格式测试
- WP-Markdown 格式测试
- 测试 Mermaid 对 WP-Markdown 编辑器生成的特殊格式的支持
+ WP-Markdown Mermaid 格式测试
+ 本页面测试 WP-Markdown 插件生成的各种 Mermaid 代码块格式
-
-
- 测试 1: 标准 WP-Markdown 格式(带 script 标签)
-
- WP-Markdown 生成的格式包含 <script>document.write()</script> 标签
-
+
+
+ 测试 1: 标准 document.write 格式(单行)
-
- flowchart TD Start([开始]) --> Process[处理] Process --> End([结束])
-
- 等待渲染...
-
-
-
-
- 测试 2: 带转义字符的复杂格式
-
- 测试转义字符的正确解码:\n(换行)、\"(引号)、\'(单引号)
-
-
-
- sequenceDiagram participant A as "用户" participant B as '服务器' A->>B: 发送请求 B->>A: 返回响应
-
- 等待渲染...
-
-
-
-
- 测试 3: 标准格式(无 script 标签)
-
- 确保标准格式仍然正常工作
-
-
-flowchart LR
- A[开始] --> B{判断}
- B -->|是| C[处理A]
- B -->|否| D[处理B]
- C --> E[结束]
- D --> E
-
- 等待渲染...
-
-
-
-
- 测试 4: 复杂流程图(WP-Markdown 格式)
-
- 测试包含多种节点类型和连接的复杂流程图
-
-
-
- flowchart TD Start([用户提交评论]) --> PreProcess[preprocess_comment 钩子] PreProcess --> CheckEnabled{启用 AI 检测?} CheckEnabled -->|否| SaveComment[保存评论] CheckEnabled -->|是| CheckMode{检测模式} CheckMode -->|实时检测| RealTime[实时 AI 检测] CheckMode -->|批量扫描| BatchScan[批量扫描] RealTime --> IsSpam{是否垃圾?} IsSpam -->|是| MarkSpam[标记为垃圾] IsSpam -->|否| SaveComment BatchScan --> SaveComment
-
- 等待渲染...
-
-
-
-
- 测试 5: 注释中的代码块(应该被忽略)
-
- 被 HTML 注释包裹的 Mermaid 代码块应该被忽略
-
- B")
-
- -->
-
- 如果没有渲染任何图表,说明注释过滤功能正常工作 ✓
+
+
+
+
+
+
+ 测试 2: document.write 格式(多行,带转义)
+
+
+
+
+
+
+
+ 测试 3: document.write 格式(包含 HTML 实体)
+
+
+
+
+
+
+
+ 测试 4: 纯文本格式(无 script 标签)
+
+flowchart TD
+ Start --> Stop
+
+
+
+
+
+ 测试 5: pre code 格式
+ graph TD
+ A --> B
+ B --> C
+
+
+
+
+ 测试 6: 复杂图表(类图)
+
+
-
-
-