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. `
` - 标准格式(WPMD 生成) +3. `
` - 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 修复测试页面

+

本页面用于测试两个关键修复:

+
    +
  1. 代码高亮排除 mermaid:mermaid 代码块不应被代码高亮处理
  2. +
  3. 容器语法处理空行:容器语法中的空行应被正确保留
  4. +
+ + +
+

测试 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: 复杂图表(类图)

+
+
- - -