chore: 清理非必要的测试文件和文档

This commit is contained in:
2026-01-25 12:39:32 +08:00
parent bfaeaa2ca2
commit f5b1ac44d1
21 changed files with 0 additions and 5032 deletions

View File

@@ -1,171 +0,0 @@
fix: 修复容器语法和 WP-Markdown 的两个关键问题
## 问题描述
根据用户反馈,发现两个严重问题:
### 问题 1: WP-Markdown document.write 重复输出
**错误信息**
```
No diagram type detected matching given configuration for text:
document.write("flowchart TD ...")flowchart TD ...
```
**原因分析**
- WP-Markdown 生成的 HTML 结构:`<div class="mermaid"><script>document.write("...")</script>flowchart TD ...</div>`
- script 标签后面还有一份重复的代码
- 当前代码只提取了 script 内容,但没有移除 script 标签
- 导致后续的 `element.textContent` 包含了重复内容
### 问题 2: 容器语法空行导致内容截断
**错误信息**
```
Parse error on line 1: flowchart TD Sta
Expecting 'NEWLINE', 'SPACE', 'GRAPH', got 'ALPHA'
```
**原因分析**
- 使用 `::: mermaid ... :::` 语法时WP-Markdown 遇到空行会分割内容
- 空行后的内容被识别为新的段落或代码块
- 原有的单元素检测逻辑无法处理跨多个元素的情况
- 导致只提取到第一个元素的内容("flowchart TD Sta"
## 修复方案
### 修复 1: 移除 script 标签避免重复argontheme.js:4575-4578
在 `extractMermaidCode()` 函数中,提取完 script 内容后立即移除标签:
```javascript
// 重要:移除 script 标签,避免重复渲染
// WP-Markdown 会在 script 后面再输出一次代码
scriptTag.remove();
```
**效果**
- ✅ 避免 document.write 内容和后续文本重复
- ✅ 确保只提取一次完整代码
- ✅ 不影响其他格式的提取
### 修复 2: 跨元素收集容器语法内容argontheme.js:4468-4570
完全重写 `detectContainerBlocks()` 和 `extractContainerContent()` 函数:
**新的检测逻辑**
1. 查找所有可能包含 `::: mermaid` 的元素p, pre, code, div
2. 找到开始标记后,收集所有后续兄弟元素
3. 直到遇到 `:::` 结束标记
4. 合并所有内容,保留空行
**关键代码**
```javascript
// 收集所有内容直到结束标记
while (currentElement) {
processedElements.add(currentElement);
let text = currentElement.textContent.trim();
// 检查是否是结束标记
if (text === ':::' || text.endsWith(':::')) {
foundEnd = true;
break;
}
// 添加当前元素的内容
if (text) {
codeLines.push(text);
} else {
// 空元素,添加空行
codeLines.push('');
}
currentElement = currentElement.nextElementSibling;
}
// 合并所有行
let code = codeLines.join('\n').trim();
```
**效果**
- ✅ 支持跨多个元素收集内容
- ✅ 正确处理空行(保留为空字符串)
- ✅ 完整提取所有 mermaid 代码
- ✅ 避免内容截断
## 测试场景
### 场景 1: WP-Markdown 格式
```html
<div class="mermaid">
<script>document.write("flowchart TD\n A --> B")</script>
flowchart TD
A --> B
</div>
```
**预期**:只提取 script 中的内容,忽略后面的重复文本
### 场景 2: 容器语法(无空行)
```
::: mermaid
flowchart TD
A --> B
:::
```
**预期**:正常提取完整代码
### 场景 3: 容器语法(有空行)
```
::: mermaid
flowchart TD
A[开始]
B[处理]
C[结束]
A --> B --> C
:::
```
**预期**:保留所有空行,完整提取代码
### 场景 4: 容器语法(被分割成多个元素)
```html
<p>::: mermaid</p>
<p>flowchart TD</p>
<p> A[开始]</p>
<p></p>
<p> B[处理]</p>
<p>:::</p>
```
**预期**:跨元素收集,合并为完整代码
## 影响范围
- ✅ 修复了两个严重的渲染错误
- ✅ 不影响其他标记格式
- ✅ 向后兼容
- ✅ 提升容器语法的鲁棒性
## 相关文件
- `argontheme.js` - 核心修复
- `docs/mermaid-usage-guide.md` - 更新文档
## 提交信息
```
fix: 修复容器语法和 WP-Markdown 的两个关键问题
- 修复 WP-Markdown document.write 重复输出问题
- 改进容器语法检测,支持跨多个元素收集内容
- 解决空行导致内容被分割成多个元素的问题
```
Commit: b6d9f8c
## 后续建议
1. **测试验证**:在实际文章中测试包含空行的复杂 mermaid 图表
2. **性能优化**:如果页面有大量元素,可以考虑优化查找算法
3. **错误处理**:添加更详细的日志,帮助调试边缘情况

View File

@@ -1,69 +0,0 @@
feat: 添加 Markdown 容器语法支持,修复 WPMD 代码提取问题
问题描述:
- WP-Markdown 插件将 Markdown 代码块转换为特殊格式
- 传统标记方式会被 WPMD 套上代码高亮窗口,导致嵌套结构
- 某些字符被自动转义,导致渲染失败
- 之前的正则表达式只能提取到第一个单词
解决方案:
添加 Markdown 容器语法支持(::: mermaid ... :::
- 符合 Markdown 扩展规范VuePress、Docusaurus 等使用相同语法)
- 不会被 WPMD 当作代码块处理,避免嵌套问题
- 语法简洁,易于编写和阅读
- 易于迁移到其他平台
修复内容:
1. 改进正则表达式为 ([\s\S]*?),匹配包括换行在内的所有字符
2. 添加三层降级方案:
- 方案1: 匹配 document.write("...")
- 方案2: 直接提取引号内容
- 方案3: 纯文本提取
3. 增加详细的调试日志(原始内容、提取长度、最终代码)
4. 优化解码顺序(先 HTML 实体,后转义字符)
5. 添加容器语法检测逻辑TreeWalker
代码变更:
- functions.php:
* argon_has_mermaid_content(): 添加容器语法检测
- argontheme.js:
* detectMermaidBlocks(): 添加容器语法检测
* detectContainerBlocks(): 新增函数,检测 ::: mermaid 容器
* extractContainerContent(): 新增函数,提取容器内容
* extractMermaidCode(): 支持容器语法的代码提取
- tests/test-wp-markdown-format.html:
* 添加 7 种测试场景
* 包含容器语法和标准格式测试
- docs/mermaid-usage-guide.md:
* 详细说明容器语法使用方法
* 提供使用示例和最佳实践
* 包含故障排除指南
支持的标记格式(按优先级):
1. ::: mermaid ... ::: - Markdown 容器语法 ⭐(推荐)
2. <div class="mermaid"> - 标准格式WPMD 生成)
3. <pre><code class="language-mermaid"> - Markdown 格式
4. <pre data-lang="mermaid"> - 自定义属性格式
5. <code class="mermaid"> - 简化格式
推荐使用方式:
::: mermaid
flowchart TD
A[开始] --> B[处理]
B --> C[结束]
:::
测试方法:
1. 访问测试页面https://blog.cartol.top/wp-content/themes/argon/tests/test-wp-markdown-format.html
2. 查看控制台日志,确认所有测试用例都能正确提取和渲染
3. 在文章中使用推荐的容器语法
4. 如果还有问题,查看控制台的详细日志
注意事项:
- 强烈推荐使用 Markdown 容器语法,避免 WPMD 处理
- 传统 Markdown 代码块仍然支持,但可能有嵌套问题
- 所有标记方式都会自动解码 HTML 实体和转义字符
- 容器语法符合现代 Markdown 扩展规范,易于迁移

View File

@@ -1,207 +0,0 @@
fix: 修复 Mermaid 容器语法的换行符和特殊字符问题
## 问题描述
用户报告 Mermaid 图表仍然渲染失败:
1. `flowchart TDStart` - `TD` 和 `Start` 之间没有换行符(有两个空格)
2. 箭头符号变成全角 `>` 而不是 `-->`
## 根本原因分析
### 问题 1: 换行符丢失
WP-Markdown 可能将整个容器语法内容放在**一个** `<p>` 元素中:
```html
<p>::: mermaid<br>flowchart TD<br>Start --> End<br>:::</p>
```
而不是多个独立的 `<p>` 元素。之前的代码只处理了多元素的情况。
### 问题 2: 特殊字符转换
WordPress 的自动格式化功能会将某些字符转换为排版字符:
- `--` → `` (U+2013 EN DASH)
- `---` → `—` (U+2014 EM DASH)
- `->` → `→` (U+2192 RIGHTWARDS ARROW)
这些字符在 Mermaid 语法中有特殊含义,必须保持原样。
## 修复方案
### 1. 支持单元素容器语法
在 `extractContainerContent()` 函数中添加检测逻辑:
```javascript
// 检查是否整个内容都在一个元素中
if (startText.includes(':::') && startText.lastIndexOf(':::') > 10) {
// 整个容器语法在一个元素中
let fullText = this.htmlToText(startHTML);
// 移除开始和结束标记
fullText = fullText.replace(/^:::\s*mermaid\s*/i, '').trim();
fullText = fullText.replace(/:::\s*$/, '').trim();
// 创建容器并返回
}
```
### 2. 增强 htmlToText() 函数
添加更多字符转换规则:
```javascript
// HTML 实体
.replace(/&#8211;/g, '-') // EN DASH
.replace(/&#8212;/g, '--') // EM DASH
.replace(/&#8594;/g, '->') // RIGHTWARDS ARROW
.replace(/&ndash;/g, '-') // EN DASH (named entity)
.replace(/&mdash;/g, '--') // EM DASH (named entity)
.replace(/&rarr;/g, '->'); // RIGHTWARDS ARROW (named entity)
// Unicode 字符
.replace(//g, '-') // U+2013 EN DASH
.replace(/—/g, '--') // U+2014 EM DASH
.replace(/→/g, '->'); // U+2192 RIGHTWARDS ARROW
```
### 3. 添加详细调试日志
```javascript
this.logDebug('检查元素textContent: ' + startText.substring(0, 50));
this.logDebug('innerHTML: ' + startHTML.substring(0, 100));
this.logDebug('检测到单元素容器语法');
this.logDebug('转换后的完整文本: ' + fullText.substring(0, 200));
this.logDebug('移除标记后的代码: ' + fullText.substring(0, 200));
```
## 处理流程
### 单元素容器语法
```
1. 检测到 ::: mermaid 开始标记
2. 检查 textContent 中是否包含结束标记 :::
3. 如果包含,说明整个内容在一个元素中
4. 提取 innerHTML
原始: "::: mermaid<br>flowchart TD<br>Start > End<br>:::"
5. 使用 htmlToText() 转换
- <br> → \n
- → -
- → → ->
结果: "::: mermaid\nflowchart TD\nStart --> End\n:::"
6. 移除开始和结束标记
结果: "flowchart TD\nStart --> End"
7. 创建容器元素存储代码
```
### 多元素容器语法(保持原有逻辑)
```
1. 检测到 ::: mermaid 开始标记
2. 遍历后续兄弟元素
3. 收集所有内容直到 ::: 结束标记
4. 使用 \n 连接所有行
5. 创建容器元素存储代码
```
## 测试验证
### 测试用例 1: 单元素容器语法
```html
<p>::: mermaid<br>
flowchart TD<br>
A[开始] > B[处理]<br>
B > C[结束]<br>
:::</p>
```
预期结果:
```
flowchart TD
A[开始] --> B[处理]
B --> C[结束]
```
### 测试用例 2: 多元素容器语法
```html
<p>::: mermaid</p>
<p>flowchart TD</p>
<p>A --> B</p>
<p>:::</p>
```
预期结果:
```
flowchart TD
A --> B
```
### 测试用例 3: 复杂流程图AI 评论审核)
- 100+ 个节点
- 多行文本(`<br/>` 标签)
- 箭头符号(`-->`, `-->`
- 样式定义(`style` 语句)
## 影响范围
- 仅影响 Markdown 容器语法(`::: mermaid ... :::`
- 不影响其他格式(`<div class="mermaid">`, `<pre><code>` 等)
- 向后兼容,支持单元素和多元素两种情况
## 相关文件
- `argontheme.js` - 修改 `extractContainerContent()` 和 `htmlToText()`
- `tests/test-ai-comment-flow.md` - 测试文档
## 技术细节
### WordPress 自动格式化
WordPress 的 `wptexturize()` 函数会自动转换以下字符:
- `--` → `` (EN DASH)
- `---` → `—` (EM DASH)
- `->` → `→` (RIGHTWARDS ARROW)
- `(c)` → `©` (COPYRIGHT SIGN)
- `(r)` → `®` (REGISTERED SIGN)
- `(tm)` → `™` (TRADE MARK SIGN)
这些转换对于普通文本是有益的,但对于代码块(如 Mermaid是有害的。
### 解决方案
1. **禁用自动格式化**(不推荐)
- 会影响整个网站的排版
- 需要修改 WordPress 核心代码
2. **在提取时反转换**(推荐)✅
- 只影响 Mermaid 代码块
- 不影响其他内容
- 易于维护
### 字符映射表
| 原始字符 | HTML 实体 | Unicode | 显示 |
|---------|----------|---------|------|
| `-` | `&#45;` | U+002D | - |
| `--` | `&#8211;` / `&ndash;` | U+2013 | |
| `---` | `&#8212;` / `&mdash;` | U+2014 | — |
| `->` | `&#8594;` / `&rarr;` | U+2192 | → |
## 注意事项
1. **性能影响**:增加了字符串替换操作,但性能影响可忽略
2. **安全性**:只处理 Mermaid 代码块,不执行任何脚本
3. **兼容性**:支持所有现代浏览器
4. **调试**:添加详细日志,便于排查问题
## 后续优化
1. 考虑使用 DOMParser 解析 HTML更可靠
2. 添加更多特殊字符的转换规则
3. 支持自定义字符映射表
4. 添加单元测试
## 参考资料
- [WordPress wptexturize() 函数](https://developer.wordpress.org/reference/functions/wptexturize/)
- [Unicode 字符表](https://unicode-table.com/)
- [Mermaid 语法文档](https://mermaid.js.org/intro/syntax-reference.html)

View File

@@ -1,96 +0,0 @@
fix: 修复代码高亮和容器语法的两个关键问题
## 问题描述
1. **代码高亮干扰 mermaid 渲染**
- 传统 Markdown 代码块(```mermaid使用 document.write
- 代码高亮会处理这些代码块,导致 mermaid 无法正常渲染
2. **容器语法空行处理错误**
- 使用 ::: mermaid 语法时,空行会导致内容被截断
- WPMD 将空行后的内容识别为代码块
## 修复方案
### 1. 代码高亮排除 mermaidargontheme.js:3942-3962
在 `highlightJsRender()` 函数中添加检查:
```javascript
// 跳过 mermaid 代码块(避免代码高亮干扰 mermaid 渲染)
if ($(block).hasClass("language-mermaid") || $(block).hasClass("mermaid")){
return;
}
// 跳过在 .mermaid 容器内的代码块
if ($(block).closest('.mermaid').length > 0){
return;
}
```
**效果**
- ✅ mermaid 代码块不会被代码高亮处理
- ✅ 避免嵌套结构和渲染冲突
- ✅ 普通代码块仍正常高亮
### 2. 容器语法保留空行argontheme.js:4508-4530
修改 `extractContainerContent()` 函数:
```javascript
// 移除开始标记 ::: mermaid保留后面的换行符和空行
text = text.replace(/^:::\s*mermaid\s*\n?/i, '');
// 移除结束标记 :::(保留前面的换行符和空行)
text = text.replace(/\n?:::\s*$/i, '');
// 不要 trim保留代码中的空行
if (!text) {
return null;
}
```
**效果**
- ✅ 空行被正确保留
- ✅ 多行 mermaid 代码完整提取
- ✅ 避免内容被截断
## 测试验证
创建了 `tests/test-mermaid-fixes.html` 测试页面,包含:
1. **代码高亮排除测试**
- Markdown 格式 mermaid应排除
- div.mermaid 格式(应排除)
- 普通代码块(应高亮)
2. **容器语法空行测试**
- 包含空行的流程图
- 包含空行的时序图
- 包含多个连续空行
3. **WP-Markdown 兼容性测试**
- document.write 格式
## 影响范围
- ✅ 不影响现有功能
- ✅ 向后兼容所有标记格式
- ✅ 提升用户体验
## 相关文件
- `argontheme.js` - 核心修复
- `docs/mermaid-usage-guide.md` - 更新文档
- `tests/test-mermaid-fixes.html` - 测试页面
## 提交信息
```
fix: 修复代码高亮和容器语法的两个关键问题
- 代码高亮排除 mermaid 代码块,避免干扰渲染
- 容器语法正确处理空行,不再截断内容
- 添加测试页面验证修复效果
```
Commit: 0ac5794

View File

@@ -1,78 +0,0 @@
feat: 添加多种 Mermaid 标记方式,避免 WPMD 处理问题
问题描述:
- WP-Markdown 插件将 Markdown 代码块转换为特殊格式
- 传统标记方式会被 WPMD 套上代码高亮窗口,导致嵌套结构
- 某些字符被自动转义,导致渲染失败
- 之前的正则表达式只能提取到第一个单词
新增功能:
1. Argon 自定义标记(推荐):<div class="argon-mermaid">...</div>
- 不会被 WPMD 处理
- 语法简单清晰
- 支持所有 Mermaid 图表类型
2. HTML 注释标记:<!-- mermaid -->...<!-- /mermaid -->
- 在编辑器中不可见
- 不会被任何插件处理
- 适合需要隐藏标记的场景
3. data 属性标记:<div data-mermaid>...</div>
- 符合 HTML5 规范
- 语义化标记
- 不会被 WPMD 处理
修复内容:
1. 改进正则表达式为 ([\s\S]*?),匹配包括换行在内的所有字符
2. 添加三层降级方案:
- 方案1: 匹配 document.write("...")
- 方案2: 直接提取引号内容
- 方案3: 纯文本提取
3. 增加详细的调试日志(原始内容、提取长度、最终代码)
4. 优化解码顺序(先 HTML 实体,后转义字符)
5. 添加 HTML 注释标记检测逻辑TreeWalker
代码变更:
- argontheme.js:
* detectMermaidBlocks(): 添加新选择器和注释检测
* detectCommentBlocks(): 新增函数,检测 HTML 注释标记
* extractCommentContent(): 新增函数,提取注释之间的内容
* extractMermaidCode(): 支持新标记格式的代码提取
- tests/test-wp-markdown-format.html:
* 添加 9 种测试场景
* 包含所有支持的标记格式
* 使用改进后的提取逻辑
- docs/mermaid-wpmd-usage.md:
* 详细说明所有标记方式
* 提供使用示例和最佳实践
* 包含故障排除指南
支持的标记格式(按优先级):
1. <div class="mermaid"> - WPMD 标准格式
2. <div class="argon-mermaid"> - Argon 自定义格式 ⭐
3. <div data-mermaid> - data 属性格式
4. <!-- mermaid -->...<!-- /mermaid --> - HTML 注释格式
5. <pre><code class="language-mermaid"> - Markdown 格式
6. <pre data-lang="mermaid"> - 自定义属性格式
7. <code class="mermaid"> - 简化格式
测试方法:
1. 访问测试页面https://blog.cartol.top/wp-content/themes/argon/tests/test-wp-markdown-format.html
2. 查看控制台日志,确认所有测试用例都能正确提取和渲染
3. 在文章中使用推荐的标记方式:<div class="argon-mermaid">...</div>
4. 如果还有问题,查看控制台的详细日志
推荐使用方式:
<div class="argon-mermaid">
flowchart TD
A[开始] --> B[处理]
B --> C[结束]
</div>
注意事项:
- 强烈推荐使用 Argon 自定义标记,避免 WPMD 处理
- HTML 注释标记适合需要隐藏标记的场景
- 传统 Markdown 代码块仍然支持,但可能有嵌套问题
- 所有标记方式都会自动解码 HTML 实体和转义字符

View File

@@ -1,129 +0,0 @@
fix: 修复 Mermaid 容器语法换行符丢失问题
## 问题描述
用户提供的 AI 评论审核流程 Mermaid 图表渲染失败,错误信息显示:
- `flowchart TDStart` - `TD` 和 `Start` 之间缺少换行符
- 箭头符号被转换成全角 `>` 而不是 `-->`
## 根本原因
WP-Markdown 插件将容器语法 `::: mermaid ... :::` 转换为 HTML 时:
1. 每一行代码都被包裹在 `<p>` 标签中
2. 行内的换行使用 `<br>` 标签表示
3. 使用 `textContent` 或 `innerText` 提取时,`<br>` 标签被忽略或转换为空格
4. 导致多行代码被合并成一行Mermaid 语法解析失败
## 修复方案
### 1. 新增 `htmlToText()` 辅助函数
```javascript
htmlToText(html) {
// 将 <br> 和 <br/> 转换为换行符
let text = html.replace(/<br\s*\/?>/gi, '\n');
// 移除其他 HTML 标签
text = text.replace(/<[^>]+>/g, '');
// 解码 HTML 实体
text = text
.replace(/&nbsp;/g, ' ')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&amp;/g, '&')
.replace(/&quot;/g, '"')
.replace(/&#039;/g, "'");
return text;
}
```
### 2. 改进 `extractContainerContent()` 函数
- 使用 `innerHTML` 而不是 `textContent` 或 `innerText`
- 调用 `htmlToText()` 将 `<br>` 标签转换为换行符
- 保留所有换行符和空行
- 添加详细的调试日志
### 3. 处理流程
```
1. 检测到 ::: mermaid 开始标记
2. 提取 innerHTML包含 <br> 标签)
3. 使用 htmlToText() 转换:
- <br> → \n
- 移除其他 HTML 标签
- 解码 HTML 实体
4. 收集所有行内容
5. 使用 \n 连接所有行
6. 创建容器元素存储完整代码
```
## 测试验证
### 测试用例 1简单流程图
```markdown
::: mermaid
flowchart TD
A[开始] --> B[处理]
B --> C[结束]
:::
```
### 测试用例 2复杂流程图AI 评论审核)
- 包含 100+ 个节点
- 包含多行文本(`<br/>` 标签)
- 包含箭头符号(`-->`, `-->`
- 包含样式定义(`style` 语句)
### 预期结果
- ✅ 所有换行符正确保留
- ✅ 箭头符号不被转换
- ✅ 多行文本正确显示
- ✅ 样式定义正确应用
## 影响范围
- 仅影响 Markdown 容器语法(`::: mermaid ... :::`
- 不影响其他格式(`<div class="mermaid">`, `<pre><code>` 等)
- 向后兼容,不影响现有功能
## 相关文件
- `argontheme.js` - 修改 `extractContainerContent()` 和新增 `htmlToText()`
- `tests/test-ai-comment-flow.md` - 测试文档
- `docs/mermaid-usage-guide.md` - 使用指南
## 技术细节
### HTML 结构示例
WP-Markdown 生成的 HTML
```html
<p>::: mermaid<br>
flowchart TD<br>
Start([用户提交评论]) --> PreProcess[预处理]<br>
:::</p>
```
### 提取过程
1. **原始 HTML**: `::: mermaid<br>flowchart TD<br>Start --> End<br>:::`
2. **htmlToText()**: `::: mermaid\nflowchart TD\nStart --> End\n:::`
3. **移除标记**: `flowchart TD\nStart --> End`
4. **最终代码**:
```
flowchart TD
Start --> End
```
## 注意事项
1. **性能影响**:使用 `innerHTML` 和正则替换,性能影响可忽略
2. **安全性**:只处理 Mermaid 代码块,不执行任何脚本
3. **兼容性**:支持所有现代浏览器
4. **调试**:添加详细日志,便于排查问题
## 后续优化
1. 考虑支持更多 Markdown 容器语法(如 `::: warning`, `::: tip` 等)
2. 优化正则表达式性能
3. 添加单元测试

View File

@@ -1,213 +0,0 @@
# Mermaid 容器语法修复总结
## 修复的问题
### 问题 1: 代码高亮干扰 Mermaid 渲染
- **症状**: 使用 ` ```mermaid ` 标记时,代码高亮会处理 mermaid 代码块,导致渲染冲突
- **修复**: 在 `highlightJsRender()` 函数中添加检查,跳过 `language-mermaid``mermaid` 类的代码块
- **Commit**: 0ac5794
### 问题 2: 容器语法空行导致内容截断
- **症状**: 容器语法 `::: mermaid ... :::` 中的空行导致内容被截断
- **修复**: 修改 `extractContainerContent()` 函数,不使用 `trim()` 删除空行
- **Commit**: 0ac5794
### 问题 3: WP-Markdown document.write 重复输出
- **症状**: WP-Markdown 生成 `<div class="mermaid"><script>document.write("...")</script>flowchart TD ...</div>`script 后面有重复代码
- **修复**: 在 `extractMermaidCode()` 中提取完 script 内容后立即 `scriptTag.remove()`
- **Commit**: b6d9f8c
### 问题 4: 容器语法跨元素收集失败
- **症状**: 容器语法遇到空行时WP-Markdown 将内容分割成多个元素,只提取到第一个元素
- **修复**: 重写 `detectContainerBlocks()``extractContainerContent()`,支持跨多个兄弟元素收集内容
- **Commit**: b6d9f8c
### 问题 5: 换行符丢失 ⭐(核心问题)
- **症状**:
- 流程图渲染失败,错误显示 `flowchart TDStart``TD``Start` 之间缺少换行)
- 箭头符号被转换成全角 `>` 而不是 `-->`
- **根本原因**:
- WP-Markdown 将每一行代码包裹在 `<p>` 标签中
- 行内换行使用 `<br>` 标签表示
- 使用 `textContent``innerText` 提取时,`<br>` 标签被忽略或转换为空格
- 导致多行代码被合并成一行Mermaid 语法解析失败
- **修复**:
- 新增 `htmlToText()` 辅助函数,将 `<br>` 标签转换为换行符
- 改进 `extractContainerContent()` 函数,使用 `innerHTML` 而不是 `textContent`
- 调用 `htmlToText()` 正确处理 HTML 结构
- **Commit**: b4ba37a
## 技术细节
### WP-Markdown 生成的 HTML 结构
```html
<p>::: mermaid<br>
flowchart TD<br>
Start([用户提交评论]) --> PreProcess[预处理]<br>
PreProcess --> CheckEnabled{启用 AI 检测?}<br>
:::</p>
```
### htmlToText() 函数
```javascript
htmlToText(html) {
// 将 <br> 和 <br/> 转换为换行符
let text = html.replace(/<br\s*\/?>/gi, '\n');
// 移除其他 HTML 标签
text = text.replace(/<[^>]+>/g, '');
// 解码 HTML 实体
text = text
.replace(/&nbsp;/g, ' ')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&amp;/g, '&')
.replace(/&quot;/g, '"')
.replace(/&#039;/g, "'");
return text;
}
```
### 提取流程
```
1. 检测到 ::: mermaid 开始标记
2. 提取 innerHTML包含 <br> 标签)
原始: "::: mermaid<br>flowchart TD<br>Start --> End<br>:::"
3. 使用 htmlToText() 转换
结果: "::: mermaid\nflowchart TD\nStart --> End\n:::"
4. 移除开始和结束标记
结果: "flowchart TD\nStart --> End"
5. 创建容器元素存储完整代码
```
## 测试验证
### 测试文件
- `tests/test-mermaid-fixes.html` - 基础功能测试
- `tests/test-mermaid-newline.html` - 换行符测试
- `tests/test-ai-comment-flow.md` - 复杂流程图测试100+ 节点)
### 测试用例
#### 1. 简单流程图
```markdown
::: mermaid
flowchart TD
A[开始] --> B[处理]
B --> C[结束]
:::
```
#### 2. 复杂流程图AI 评论审核)
- 包含 100+ 个节点
- 包含多行文本(`<br/>` 标签)
- 包含箭头符号(`-->`, `-->`
- 包含样式定义(`style` 语句)
#### 3. 带样式的流程图
```markdown
::: mermaid
flowchart LR
A[开始] --> B[处理]
B --> C[结束]
style A fill:#e1f5e1,stroke:#2e7d32
style C fill:#ffe1e1,stroke:#c62828
:::
```
### 预期结果
- ✅ 所有换行符正确保留
- ✅ 箭头符号不被转换
- ✅ 多行文本正确显示
- ✅ 样式定义正确应用
- ✅ 复杂流程图正确渲染
## Git 提交历史
```
0ac5794 - fix: 修复代码高亮和容器语法的两个关键问题
b6d9f8c - fix: 修复容器语法和 WP-Markdown 的两个关键问题
b4ba37a - fix: 修复 Mermaid 容器语法换行符丢失问题
759804d - docs: 更新 Mermaid 使用指南和添加换行符测试页面
```
## 影响范围
### 修改的文件
- `argontheme.js` - 核心修复代码
- `docs/mermaid-usage-guide.md` - 使用指南
- `tests/test-mermaid-fixes.html` - 测试页面
- `tests/test-mermaid-newline.html` - 换行符测试
- `tests/test-ai-comment-flow.md` - 复杂流程图测试
### 影响的功能
- ✅ Markdown 容器语法(`::: mermaid ... :::`
- ✅ WP-Markdown 格式(`<div class="mermaid"><script>document.write()</script></div>`
- ✅ 标准格式(`<div class="mermaid">`, `<pre><code class="language-mermaid">`
- ✅ 代码高亮(排除 mermaid 代码块)
### 向后兼容性
- ✅ 完全向后兼容
- ✅ 不影响现有功能
- ✅ 支持所有现代浏览器
## 性能影响
- **DOM 操作**: 使用 `innerHTML` 和正则替换,性能影响可忽略
- **内存占用**: 无显著增加
- **渲染速度**: 无影响
## 安全性
- ✅ 只处理 Mermaid 代码块,不执行任何脚本
- ✅ HTML 实体正确解码
- ✅ 无 XSS 风险
## 调试支持
- 添加详细的 `console.log` 日志
- 支持调试模式(主题设置中开启)
- 错误时显示友好提示
## 后续优化建议
1. **支持更多容器语法**
- `::: warning` - 警告框
- `::: tip` - 提示框
- `::: danger` - 危险框
2. **性能优化**
- 缓存已处理的代码块
- 使用 IntersectionObserver 延迟渲染
3. **测试覆盖**
- 添加单元测试
- 添加集成测试
- 添加性能测试
4. **文档完善**
- 添加更多示例
- 添加常见问题解答
- 添加故障排除指南
## 相关资源
- [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/)
## 总结
通过这次修复Argon 主题的 Mermaid 支持已经非常完善:
1. ✅ 支持标准的 Markdown 容器语法
2. ✅ 兼容 WP-Markdown 插件的特殊格式
3. ✅ 正确处理换行符和特殊字符
4. ✅ 支持复杂的流程图和样式定义
5. ✅ 提供详细的调试信息
6. ✅ 完全向后兼容
用户现在可以放心使用容器语法编写 Mermaid 图表,无需担心格式问题。

View File

@@ -1,184 +0,0 @@
# Mermaid 图表尺寸优化
## 问题描述
在之前的实现中Mermaid 图表容器没有对 SVG 的高度进行限制,导致:
1. **低内容图表显示过大**:只有 2-3 个节点的简单流程图可能被渲染得非常大,占据整个屏幕
2. **用户体验不佳**:需要大量滚动才能看到后续内容
3. **视觉不协调**:简单图表与复杂图表的尺寸差异过大
## 优化方案
### 1. 最大高度限制
为 SVG 元素设置最大高度,避免图表显示过大:
```css
.mermaid-container svg {
max-height: 600px; /* 桌面端 */
}
```
### 2. 居中显示
使用 Flexbox 让图表在容器中居中显示:
```css
.mermaid-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100px;
}
```
### 3. 宽度自适应
使用 `width: auto !important` 让图表保持原始宽高比:
```css
.mermaid-container svg {
max-width: 100%;
width: auto !important;
height: auto;
}
```
### 4. 响应式适配
针对不同屏幕尺寸设置不同的最大高度:
```css
/* 桌面端 */
.mermaid-container svg {
max-height: 600px;
}
/* 平板(<768px */
@media screen and (max-width: 768px) {
.mermaid-container svg {
max-height: 500px;
}
}
/* 手机(<480px */
@media screen and (max-width: 480px) {
.mermaid-container svg {
max-height: 400px;
}
}
```
## 优化效果
### 优化前
- ❌ 简单图表2-3 节点)可能显示超大,占据整个屏幕
- ❌ 图表尺寸不可控,用户体验差
- ❌ 需要大量滚动才能看到后续内容
### 优化后
- ✅ 简单图表显示合理大小,不会过大
- ✅ 图表在容器中居中显示,视觉协调
- ✅ 复杂图表高度限制在 600px出现滚动条
- ✅ 响应式适配,移动端体验更好
## 测试方法
使用测试文件验证优化效果:
```bash
# 在浏览器中打开测试文件
tests/test-mermaid-size-optimization.html
```
### 测试检查清单
- [ ] 极简图表2-3 节点)不会显示过大
- [ ] 图表在容器中居中显示
- [ ] 中等复杂度图表显示正常
- [ ] 复杂图表高度限制在 600px
- [ ] 横向图表宽度自适应
- [ ] 夜间模式下图表显示正常
- [ ] 移动端响应式适配正常
## 技术细节
### CSS 关键属性
```css
.mermaid-container {
/* 使用 Flexbox 居中 */
display: flex;
justify-content: center;
align-items: center;
/* 最小高度,避免空白过小 */
min-height: 100px;
/* 其他样式 */
background: var(--card-background);
border-radius: var(--card-radius);
padding: 20px;
margin: 20px 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow-x: auto;
max-width: 100%;
transition: opacity 0.3s ease-in;
}
.mermaid-container svg {
/* 宽度限制 */
max-width: 100%;
width: auto !important;
/* 高度限制 */
max-height: 600px;
height: auto;
/* 居中显示 */
display: block;
margin: 0 auto;
}
```
### 为什么使用 `width: auto !important`
Mermaid 会自动设置 SVG 的 `width` 属性,可能导致图表过大。使用 `!important` 强制覆盖,让浏览器根据 `max-width``max-height` 自动计算宽度,保持原始宽高比。
### 为什么使用 Flexbox
使用 Flexbox 可以轻松实现图表在容器中的居中显示,无论图表大小如何变化,都能保持居中对齐。
## 兼容性
- ✅ 现代浏览器Chrome, Firefox, Safari, Edge
- ✅ 移动端浏览器
- ✅ 夜间模式
- ✅ 响应式布局
## 注意事项
1. **滚动条**:当图表高度超过最大高度时,容器会出现垂直滚动条
2. **宽高比**:图表会保持原始宽高比,不会变形
3. **性能**:优化不会影响 Mermaid 的渲染性能
4. **兼容性**:与现有的 Mermaid 功能完全兼容
## 相关文件
- `style.css` - Mermaid 容器样式定义
- `tests/test-mermaid-size-optimization.html` - 尺寸优化测试文件
- `docs/mermaid-usage-guide.md` - Mermaid 使用指南
- `docs/mermaid-troubleshooting.md` - Mermaid 故障排除
## 更新日志
### 2026-01-24
- ✅ 添加 SVG 最大高度限制600px
- ✅ 使用 Flexbox 实现图表居中
- ✅ 添加响应式适配(平板 500px手机 400px
- ✅ 添加最小高度限制100px
- ✅ 创建尺寸优化测试文件

View File

@@ -1,347 +0,0 @@
# Mermaid 支持方案对比
## 概述
Argon 主题现在支持多种 Mermaid 图表标记方式,每种方式都有其优缺点和适用场景。
---
## 方案对比表
| 方案 | 语法 | 优点 | 缺点 | 推荐度 | 适用场景 |
|------|------|------|------|--------|----------|
| **Shortcode** | `[mermaid]...[/mermaid]` | ✅ 最可靠<br>✅ 支持参数<br>✅ 不被格式化<br>✅ 易于编辑 | ❌ 需要记住语法 | ⭐⭐⭐⭐⭐ | **所有场景(推荐)** |
| 容器语法 | `::: mermaid ... :::` | ✅ 符合 Markdown 规范<br>✅ 易于迁移 | ❌ 被 WP 格式化<br>❌ 换行符问题<br>❌ 特殊字符转换 | ⭐⭐⭐ | 简单图表 |
| 代码块 | ` ```mermaid ... ``` ` | ✅ 通用语法<br>✅ 编辑器高亮 | ❌ 被代码高亮干扰<br>❌ 嵌套问题 | ⭐⭐ | 不推荐 |
| HTML | `<div class="mermaid">...</div>` | ✅ 灵活<br>✅ 完全控制 | ❌ 编辑不便<br>❌ 可读性差 | ⭐⭐ | 特殊需求 |
---
## 详细对比
### 1. Shortcode 方式 ⭐⭐⭐⭐⭐(推荐)
#### 语法
```
[mermaid theme="default" width="100%" align="center"]
flowchart TD
A[开始] --> B[处理]
B --> C[结束]
[/mermaid]
```
#### 优点
-**最可靠**:不依赖 WP-Markdown 的处理方式
-**不被破坏**:不会被 WordPress 自动格式化
-**支持参数**theme、width、height、align
-**易于编辑**:在编辑器中清晰可见
-**完全兼容**:与其他 Shortcode 一样使用
#### 缺点
- ❌ 需要记住 Shortcode 语法
- ❌ 不是标准 Markdown 语法
#### 适用场景
-**所有场景**(强烈推荐)
- ✅ 复杂流程图
- ✅ 需要自定义样式
- ✅ 长期维护的文档
#### 示例
```
[mermaid theme="dark" width="80%"]
sequenceDiagram
Alice->>Bob: Hello
Bob-->>Alice: Hi
[/mermaid]
```
---
### 2. 容器语法 ⭐⭐⭐
#### 语法
```markdown
::: mermaid
flowchart TD
A --> B
:::
```
#### 优点
- ✅ 符合 Markdown 扩展规范
- ✅ VuePress、Docusaurus 等使用相同语法
- ✅ 易于迁移到其他平台
#### 缺点
-**被 WordPress 格式化破坏**
- `-->` 转换为 `>`
- 换行符可能丢失
- 需要复杂的 JavaScript 处理
- ❌ 不支持参数配置
- ❌ 依赖 WP-Markdown 的处理方式
#### 适用场景
- ✅ 简单的流程图
- ✅ 不包含特殊字符
- ✅ 需要跨平台兼容
#### 注意事项
- ⚠️ 避免使用 `-->` 箭头(可能被转换)
- ⚠️ 避免复杂的多行文本
- ⚠️ 需要开启主题的 Mermaid 支持
---
### 3. 代码块方式 ⭐⭐
#### 语法
````markdown
```mermaid
flowchart TD
A --> B
```
````
#### 优点
- ✅ 通用的 Markdown 语法
- ✅ 编辑器可能提供语法高亮
#### 缺点
- ❌ **被代码高亮干扰**
- Prism.js 会处理代码块
- 可能出现嵌套问题
- ❌ 被 WP-Markdown 包裹在 `document.write()` 中
- ❌ 需要特殊处理排除
#### 适用场景
- ⚠️ **不推荐使用**
- 仅在其他方式不可用时考虑
#### 注意事项
- ⚠️ 需要在代码高亮中排除 mermaid
- ⚠️ 可能出现渲染冲突
---
### 4. HTML 方式 ⭐⭐
#### 语法
```html
<div class="mermaid">
flowchart TD
A --> B
</div>
```
#### 优点
- ✅ 完全控制 HTML 结构
- ✅ 可以添加自定义属性
#### 缺点
- ❌ 编辑不便
- ❌ 可读性差
- ❌ 不符合 Markdown 风格
#### 适用场景
- ✅ 需要特殊的 HTML 结构
- ✅ 需要自定义 CSS 类
- ✅ 程序生成的内容
---
## 使用建议
### 新项目
**强烈推荐使用 Shortcode 方式**
```
[mermaid]
flowchart TD
A --> B
[/mermaid]
```
**理由**
1. 最可靠,不会被破坏
2. 支持参数配置
3. 易于维护
4. 长期稳定
### 现有项目迁移
如果你已经使用了容器语法 `::: mermaid ... :::`
#### 选项 1保持不变简单图表
- 如果图表简单,没有特殊字符
- 如果渲染正常,无需修改
#### 选项 2迁移到 Shortcode推荐
- 批量替换:`::: mermaid` → `[mermaid]`
- 批量替换:`:::` → `[/mermaid]`
- 测试验证
### 不同场景的选择
#### 场景 1技术文档
**推荐**Shortcode
- 需要长期维护
- 图表复杂
- 需要自定义样式
#### 场景 2博客文章
**推荐**Shortcode
- 简单易用
- 不需要记住复杂语法
- 稳定可靠
#### 场景 3跨平台内容
**推荐**:容器语法(如果目标平台支持)
- 需要在多个平台发布
- 目标平台支持容器语法
- 可以接受一些限制
#### 场景 4程序生成
**推荐**HTML 或 Shortcode
- 由程序自动生成
- 需要批量处理
- 可以使用模板
---
## 性能对比
| 方案 | 渲染速度 | 内存占用 | 兼容性 | 稳定性 |
|------|----------|----------|--------|--------|
| Shortcode | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 容器语法 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| 代码块 | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | ⭐⭐ |
| HTML | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
---
## 常见问题
### Q1: 为什么推荐 Shortcode 而不是容器语法?
**A**: 容器语法虽然符合 Markdown 规范,但在 WordPress 环境下会遇到以下问题:
1. WordPress 的 `wptexturize()` 会转换特殊字符
2. WP-Markdown 的处理方式不一致
3. 需要复杂的 JavaScript 来修复这些问题
Shortcode 方式完全避免了这些问题,更加可靠。
### Q2: 可以混用多种方式吗?
**A**: 可以,但不推荐。建议统一使用一种方式,便于维护。
### Q3: 如何批量迁移到 Shortcode
**A**: 使用 WordPress 的搜索替换功能:
1. 安装 "Better Search Replace" 插件
2. 搜索:`::: mermaid`,替换为:`[mermaid]`
3. 搜索:`:::`(在 mermaid 代码块后),替换为:`[/mermaid]`
4. 测试验证
### Q4: Shortcode 支持哪些参数?
**A**:
- `theme`: 主题default, dark, forest, neutral
- `width`: 宽度100%, 80%, 600px 等)
- `height`: 高度auto, 500px 等)
- `align`: 对齐left, center, right
### Q5: 容器语法还会继续支持吗?
**A**: 会继续支持,但不推荐新项目使用。我们会持续修复容器语法的问题,但 Shortcode 是更好的选择。
---
## 技术实现对比
### Shortcode 实现
**PHP 端**functions.php
```php
add_shortcode('mermaid','shortcode_mermaid');
function shortcode_mermaid($attr,$content=""){
$content = shortcode_content_preprocess($attr, $content);
$theme = isset( $attr['theme'] ) ? $attr['theme'] : 'default';
// ... 生成 HTML
return $out;
}
```
**JavaScript 端**argontheme.js
```javascript
const selectors = [
'div.mermaid-shortcode', // 直接检测
// ...
];
```
**优点**
- ✅ 简单直接
- ✅ 不需要复杂的解析
- ✅ 性能最优
### 容器语法实现
**JavaScript 端**argontheme.js
```javascript
// 1. 检测容器语法
detectContainerBlocks(blocks);
// 2. 提取内容
extractContainerContent(startElement, processedElements);
// 3. 转换 HTML
htmlToText(html);
// 4. 修复特殊字符
text.replace(//g, '-');
```
**缺点**
- ❌ 需要复杂的 DOM 遍历
- ❌ 需要处理多种 HTML 结构
- ❌ 需要修复 WordPress 的格式化
- ❌ 性能开销较大
---
## 总结
### 推荐方案
1. **首选**Shortcode 方式 `[mermaid]...[/mermaid]`
- 最可靠、最稳定
- 支持参数配置
- 易于维护
2. **备选**:容器语法 `::: mermaid ... :::`
- 简单图表可用
- 跨平台兼容
- 需要注意限制
3. **不推荐**:代码块和 HTML 方式
- 仅在特殊情况下使用
### 迁移建议
- ✅ 新项目:直接使用 Shortcode
- ✅ 现有项目:逐步迁移到 Shortcode
- ✅ 简单图表:可以保持容器语法
### 文档链接
- [Shortcode 使用指南](./mermaid-shortcode-guide.md)
- [容器语法使用指南](./mermaid-usage-guide.md)
- [修复总结](./mermaid-fix-summary.md)
- [开发者指南](./mermaid-developer-guide.md)
---
**最后更新**2026-01-24
**版本**Argon v2.x

View File

@@ -1,275 +0,0 @@
# Argon 主题 Mermaid 图表支持需求文档
## 需求背景
用户希望在 Argon WordPress 主题中添加 Mermaid 图表支持,以便在文章中使用流程图、时序图等可视化图表。
## 技术环境
- **WordPress 主题**: Argon
- **Markdown 编辑器**: WP-Markdown (wdmd) - 注意:这是编辑器,不是插件
- **Mermaid 版本**: 10.x 或更高
## 核心问题
### 1. WP-Markdown 渲染格式特殊
WP-Markdown 编辑器渲染 Mermaid 代码块时,生成的 HTML 格式为:
```html
<div class="mermaid">
<script>document.write("flowchart TD\n Start --> End")</script>
flowchart TD Start --> End
</div>
```
特点:
- 使用 `<div class="mermaid">` 包裹
- 内部包含 `<script>` 标签,使用 `document.write()` 输出转义后的代码
- 转义字符包括:`\n`(换行)、`\"`(引号)、`\'`(单引号)
- script 标签后还有原始文本(未转义)
### 2. Markdown 源文件格式问题
**关键问题**:用户在 WP-Markdown 编辑器中编辑时,虽然界面显示有换行(编辑器自动换行),但保存到 Markdown 源文件时,**整个 Mermaid 代码块是一整行,没有真正的换行符**。
示例:
```markdown
```mermaid
flowchart TD Start([用户提交评论]) --> PreProcess[preprocess_comment 钩子] PreProcess --> CheckEnabled{启用 AI 检测?} CheckEnabled -->|否| SaveComment[保存评论]
```
```
这导致:
- Mermaid 解析器要求代码有正确的换行格式
- 持续报错:`Parse error on line 1: Expecting 'NEWLINE', 'SPACE', 'GRAPH'`
- 所有尝试在 JavaScript 或 PHP 端添加换行的方案都失败
### 3. 已尝试的解决方案(均失败)
#### 方案 1: JavaScript 端解码
- 从 script 标签提取代码
- 解码 `\n` 为真正的换行符
- **失败原因**Markdown 源文件中就没有 `\n`,只是一整行
#### 方案 2: 使用 mermaid.render()
- 替代 `mermaid.init()` 方法
- 手动渲染并插入 SVG
- **失败原因**:解析器仍然要求正确的换行格式
#### 方案 3: PHP 端预处理
- 在 `functions.php` 中添加 `argon_format_mermaid_code()` 函数
- 使用正则表达式在箭头、关键字前添加换行
- **失败原因**:无法准确判断应该在哪里添加换行
#### 方案 4: 智能检测单行代码
- 检测前 100 个字符中的换行符数量
- 如果少于 2 个,自动格式化
- **失败原因**:格式化规则不完善,仍然解析失败
## 需求分析
### 必要条件
要成功支持 Mermaid需要满足以下条件之一
1. **修改 WP-Markdown 编辑器**
- 保存时保留真正的换行符
- 或者在保存前格式化 Mermaid 代码块
- **难度**:需要修改编辑器源码,可能影响其他功能
2. **使用支持 Mermaid 的 WordPress 插件**
- 例如WP Githuber MD、Markdown Block 等
- 这些插件原生支持 Mermaid
- **难度**:需要更换编辑器,用户可能不愿意
3. **开发完整的 Mermaid 代码格式化器**
- 能够智能识别 Mermaid 语法
- 自动添加正确的换行符
- **难度**:需要深入理解 Mermaid 语法规则,工作量大
### 功能需求
如果要实现 Mermaid 支持,应包含以下功能:
#### 1. 基础功能
- [ ] 支持 Mermaid 所有图表类型(流程图、时序图、类图等)
- [ ] 响应式设计,图表自适应容器宽度
- [ ] 支持夜间模式,自动切换图表主题
- [ ] 支持本地镜像和 CDN 两种加载方式
#### 2. 设置选项
- [ ] 启用/禁用 Mermaid 支持
- [ ] 选择 CDN 地址jsDelivr、unpkg、自定义
- [ ] 选择图表主题default、dark、forest、neutral 等)
- [ ] 是否使用本地镜像
#### 3. 样式优化
- [ ] 图表容器样式(背景、圆角、阴影)
- [ ] 夜间模式适配
- [ ] 移动端响应式
- [ ] 横向滚动支持(超宽图表)
#### 4. 错误处理
- [ ] 解析失败时显示友好的错误信息
- [ ] 在控制台输出详细的调试信息
- [ ] 提供代码预览功能
## 推荐方案
### 方案 A: 使用支持 Mermaid 的插件(推荐)
**优点**
- 开箱即用,无需开发
- 插件维护者会持续更新
- 兼容性好,经过大量用户测试
**推荐插件**
1. **WP Githuber MD** - 功能强大的 Markdown 编辑器
2. **Markdown Block** - Gutenberg 原生 Markdown 块
3. **Code Syntax Block** - 支持 Mermaid 的代码块插件
**实施步骤**
1. 安装并激活插件
2. 在插件设置中启用 Mermaid 支持
3. 测试各种图表类型
4. 根据需要调整样式
### 方案 B: 要求用户手动格式化代码
**优点**
- 无需修改主题代码
- 用户完全控制代码格式
**缺点**
- 用户体验差
- 容易出错
**实施步骤**
1. 在主题文档中说明 Mermaid 代码必须有正确的换行
2. 提供代码格式化工具或在线编辑器链接
3. 用户在外部工具中编辑好后,复制到 WordPress
### 方案 C: 开发完整的格式化器(不推荐)
**优点**
- 用户体验好,自动处理
**缺点**
- 开发工作量大
- 需要维护 Mermaid 语法规则
- 可能出现边缘情况无法处理
**实施步骤**
1. 研究 Mermaid 完整语法规则
2. 开发智能格式化算法
3. 处理各种边缘情况
4. 大量测试
## 技术实现参考
### 标准 Mermaid 集成方式
```html
<!-- 加载 Mermaid 库 -->
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
<!-- 初始化 -->
<script>
mermaid.initialize({
startOnLoad: true,
theme: 'default',
securityLevel: 'loose'
});
</script>
<!-- 使用 -->
<div class="mermaid">
flowchart TD
Start --> End
</div>
```
### WordPress 插件集成方式
```php
// 注册 Mermaid 脚本
function my_theme_enqueue_mermaid() {
if (is_single() || is_page()) {
wp_enqueue_script(
'mermaid',
'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js',
[],
'10.0.0',
true
);
}
}
add_action('wp_enqueue_scripts', 'my_theme_enqueue_mermaid');
// 初始化脚本
function my_theme_mermaid_init() {
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
mermaid.initialize({
startOnLoad: true,
theme: '<?php echo get_option('mermaid_theme', 'default'); ?>'
});
});
</script>
<?php
}
add_action('wp_footer', 'my_theme_mermaid_init');
```
## 测试用例
### 测试 1: 基础流程图
```mermaid
flowchart TD
Start([开始]) --> Process[处理]
Process --> Decision{判断}
Decision -->|是| End1([结束1])
Decision -->|否| End2([结束2])
```
### 测试 2: 时序图
```mermaid
sequenceDiagram
participant A as 用户
participant B as 服务器
A->>B: 发送请求
B->>A: 返回响应
```
### 测试 3: 类图
```mermaid
classDiagram
class Animal {
+String name
+int age
+makeSound()
}
class Dog {
+bark()
}
Animal <|-- Dog
```
## 相关资源
- [Mermaid 官方文档](https://mermaid.js.org/)
- [Mermaid Live Editor](https://mermaid.live/) - 在线编辑器
- [WP Githuber MD 插件](https://wordpress.org/plugins/wp-githuber-md/)
- [Markdown Block 插件](https://wordpress.org/plugins/markdown-block/)
## 结论
由于 WP-Markdown 编辑器的特殊渲染方式和 Markdown 源文件格式问题,在 Argon 主题中直接支持 Mermaid 存在技术障碍。
**建议**
1. **短期方案**:使用支持 Mermaid 的 WordPress 插件(如 WP Githuber MD
2. **长期方案**:等待 WP-Markdown 编辑器更新,或考虑更换为更现代的 Markdown 编辑器
如果必须在当前环境下实现,需要投入大量时间开发完整的 Mermaid 代码格式化器,且无法保证 100% 兼容所有语法。

643
novel.md
View File

@@ -1,643 +0,0 @@
# 时光编织者
## 第一章:觉醒
清晨的阳光透过窗帘的缝隙,在木质地板上投下细长的光影。林深从梦中醒来,额头上渗着细密的汗珠。又是那个梦——他站在一片虚无的空间里,无数条发光的丝线在他周围飘浮,每一根都连接着不同的时空。
"又做噩梦了?"室友张浩推门进来,手里端着两杯咖啡。
"不算噩梦。"林深接过咖啡,苦笑道,"只是很真实,真实到让我分不清梦境和现实。"
这已经是连续第七天了。自从那次在古董店买下那块奇怪的怀表后,这些梦就开始缠绕着他。那块怀表很特别——表盘上没有数字,只有不断变换的星图,指针永远指向一个他看不懂的方向。
"你该去看看心理医生。"张浩关切地说,"最近你的状态很不对劲。"
林深没有回答。他知道这不是心理问题。昨晚,当他再次握住那块怀表时,整个房间的时间都停止了——墙上的钟表静止,窗外的树叶悬在半空,连张浩倒水的动作都定格在那一瞬间。只有他,还能自由活动。
这不是幻觉。他能感觉到一种力量在体内觉醒,一种与时间本身产生共鸣的能力。
## 第二章:古董店的秘密
下午,林深独自回到了那家古董店。店面很小,夹在两栋高楼之间,招牌上写着"时光典当行"。奇怪的是,他明明记得这家店在这条街上开了很多年,但当他问起周围的人时,没有人记得它的存在。
推门而入,铜铃发出清脆的响声。店内昏暗,到处堆满了各个年代的物品——生锈的钟表、泛黄的照片、古旧的书籍。空气中弥漫着时间的气息。
"你回来了。"一个苍老的声音从柜台后传来。
店主是个白发苍苍的老人,穿着民国时期的长衫,眼神深邃得像是能看穿时间本身。
"你知道我会回来?"林深问。
"当然。"老人微笑,"因为你已经觉醒了。那块怀表选择了你,就像它曾经选择过我,选择过无数个时光编织者。"
"时光编织者?"
老人站起身,走到一面巨大的挂钟前。那座钟很古老,表盘上刻满了复杂的符文。
"这个世界的时间,并非如人们想象的那样线性流动。它更像是一张巨大的网,由无数个时间节点编织而成。而时光编织者,就是那些能够感知、触碰,甚至改变这张网的人。"
老人转过身,目光灼灼地看着林深:"你现在拥有的,只是最基础的能力——暂停时间。但这只是开始。如果你愿意学习,你将能够回溯过去,窥探未来,甚至在不同的时间线之间穿梭。"
林深感到一阵眩晕。这听起来太不可思议了。
"但是,"老人的语气变得严肃,"这种能力也伴随着巨大的责任和危险。时间是宇宙最根本的法则之一,任何对它的干涉都可能造成无法预料的后果。更重要的是,你不是唯一的时光编织者。"
"还有其他人?"
"有些人用这种能力行善,有些人用它作恶。还有一些人,他们试图彻底改写时间的规则,让整个世界按照他们的意志运转。"老人顿了顿,"而你,将不可避免地卷入这场跨越时空的战争。"
## 第三章:第一次穿越
接下来的一周,林深每天都会来到古董店,跟随老人学习如何控制自己的能力。老人告诉他,他的真名叫做时墨,已经活了两百多年。作为上一代的时光编织者,时墨见证了太多因为滥用时间能力而导致的悲剧。
"时间不是玩具。"时墨反复强调,"每一次干涉,都会在时间之网上留下痕迹。改变得越多,产生的涟漪就越大。"
林深学会了如何延长时间暂停的持续时间,如何在暂停的时间里移动物体,甚至如何让特定的人在时停中保持意识。但他最想学的,是时间旅行。
"你还没准备好。"时墨拒绝了他的请求,"穿越时间需要极其精确的控制。一个微小的失误,你可能会永远困在时间的夹层中,或者更糟——改变了不该改变的历史。"
但命运没有给林深更多准备的时间。
那天晚上,当林深走出古董店时,街道上突然出现了一个穿着黑色风衣的男人。他的眼睛是诡异的银色,周围的空气扭曲着,仿佛时间在他身边流速不同。
"新人?"男人冷笑,"时墨那个老家伙还在培养接班人吗?可惜,你没有机会成长了。"
话音未落,男人抬起手。林深感到周围的时间开始加速——街上的行人像快进的录像带一样飞速移动,汽车呼啸而过,天空的云层疯狂翻滚。而他自己,却被困在正常的时间流速中,眼睁睁看着世界在他周围疾驰。
这是时间武器化的攻击。如果不能摆脱,他会在几秒钟内经历数十年的衰老。
绝望中,林深本能地握紧了怀表。一股力量从表中涌出,与他体内的能力共鸣。下一刻,整个世界的色彩褪去,变成了黑白的静止画面。
但这次不同。林深感到自己在向后坠落,穿过一层又一层的时间帷幕。周围的景象飞速倒退——夜晚变成白天,白天变成夜晚,建筑物拔地而起又消失,街道不断变换着样貌。
当一切停止时,林深发现自己站在同一条街道上,但周围的一切都变了。老旧的建筑,穿着长袍的行人,街边的报纸上印着"民国二十三年"的字样。
他穿越到了1934年。
## 第四章:民国往事
林深站在街头,震惊地看着周围的一切。这不是电影,不是梦境,而是真实的过去。空气中弥漫着煤烟的味道,街边小贩的叫卖声此起彼伏,黄包车夫拉着客人从他身边经过。
"小心!"一个声音突然响起。
林深还没反应过来,就被人拉到了路边。一辆老式汽车从刚才他站的位置呼啸而过。
"谢谢。"林深转头看向救他的人,然后愣住了。
那是一个二十多岁的年轻人,穿着长衫,戴着圆框眼镜,脸上带着温和的笑容。但最让林深震惊的是——这个人和时墨年轻时的照片一模一样。
"你是...时墨?"
年轻人惊讶地看着他:"你认识我?我们见过吗?"
林深意识到,他遇到了年轻时代的时墨。此时的时墨还不是时光编织者,只是一个普通的大学生。
"抱歉,认错人了。"林深连忙说。他记得时墨的警告——不要随意改变历史。
但年轻的时墨似乎对他很感兴趣:"你的衣服很特别,不像是本地人。从外地来的?"
林深低头看了看自己的现代服装,在这个年代确实显得格格不入。他正想找个借口离开,却听到远处传来枪声。
街道上瞬间陷入混乱,人们四散奔逃。几个穿着军装的人正在追捕一个抱着包裹的女子。
"站住!把东西交出来!"
女子跌跌撞撞地跑着,突然在林深面前摔倒。包裹散开,里面滚出一个精致的木盒。
林深本能地捡起木盒,然后他感到一阵强烈的时间波动。这个盒子里装着某种与时间有关的物品,而且力量极其强大。
"把盒子给我。"追兵已经赶到,为首的军官冷冷地说。
林深看了看手中的盒子,又看了看倒在地上的女子。她的眼神中充满了绝望和恳求。
"这是她的东西。"林深说。
"找死!"军官拔出枪。
就在这时,年轻的时墨突然挡在了林深面前:"等等!这位先生是我的朋友,有什么误会我们可以谈谈。"
"让开!"
枪声响起。
林深本能地启动了时间暂停。世界再次静止,子弹悬停在半空中,距离时墨的胸口只有几厘米。
他的心跳如雷。如果不是他的出现,时墨不会卷入这件事。如果时墨在这里死去,未来会变成什么样?他自己还会存在吗?
但现在不是思考这些的时候。林深抱起昏迷的女子,拉着时墨,在时停的世界中快速离开了现场。
当时间恢复流动时,他们已经躲进了一条小巷。军官们面面相觑,不明白目标怎么突然消失了。
"刚才...发生了什么?"时墨震惊地看着林深,"我明明看到子弹射过来,然后...然后我们就在这里了?"
林深知道,他无法再隐瞒了。
"你相信时间旅行吗?"他问。
## 第五章:命运的交汇
在一间简陋的茶馆里,林深向年轻的时墨讲述了一切——关于未来,关于时光编织者,关于他们之间跨越时空的师徒关系。
时墨静静地听着,脸上的表情从怀疑逐渐变成了沉思。
"所以,你是说我在未来会成为你的老师,教你如何操控时间?"
"是的。而且你会活很久,至少两百年。"
时墨苦笑:"这听起来像是天方夜谭。但是...刚才发生的事情,确实无法用常理解释。"他看着林深手中的怀表,"这个东西,是不是很重要?"
"这是时光编织者的信物。在未来,是你把它卖给我的。"
"我把它卖给你?"时墨摇头,"可我从来没有见过这块表。"
林深愣住了。他突然意识到一个可能——也许正是因为这次穿越,怀表才会落入时墨手中。时间形成了一个闭环。
这时,那个被救的女子醒了过来。她警惕地看着两人,手摸向腰间。
"别紧张,我们不是敌人。"林深说,"那些军人为什么追你?盒子里是什么?"
女子犹豫了一会儿,最终还是打开了木盒。里面躺着一块古老的玉佩,上面刻满了复杂的纹路。当林深的目光落在玉佩上时,他感到一阵强烈的眩晕,脑海中闪过无数破碎的画面。
"这是...时间碎片?"林深惊呼。
"你知道它?"女子惊讶地看着他,"你也是守护者?"
"守护者?"
女子深吸一口气:"我叫苏婉,是时间守护者组织的成员。我们的职责是保护这些时间碎片,防止它们落入错误的人手中。"
她解释说,时间碎片是时间本身的结晶,蕴含着改写历史的力量。如果被滥用,可能会导致整个时间线崩溃。而那些追捕她的人,是一个叫做"永恒会"的组织,他们试图收集所有的时间碎片,以获得控制时间的绝对权力。
"永恒会..."林深想起了那个攻击他的黑衣男人,"他们在未来也存在。"
"当然。"苏婉苦涩地说,"他们存在于所有的时代。这是一场跨越时空的战争,已经持续了数千年。"
时墨听得目瞪口呆:"所以,我们现在卷入了一场时空战争?"
"恐怕是的。"林深说,"而且我必须回到未来。那个攻击我的人还在等着。"
"等等。"苏婉拦住他,"你说你来自未来?那你知道这场战争的结局吗?我们能赢吗?"
林深沉默了。在他的时代,时墨从未提起过什么时间守护者组织。这意味着什么?组织失败了?还是...
"我不知道。"他诚实地说,"但我知道,如果我不回去,未来可能会变得更糟。"
苏婉点点头:"我理解。但在你离开之前,我需要你帮个忙。"
## 第六章:时间的代价
苏婉带着林深和时墨来到了城市边缘的一座废弃工厂。这里是时间守护者的秘密基地。
工厂内部别有洞天,墙上挂满了各个时代的地图和照片,中央是一个巨大的沙盘,上面标注着无数个时间节点。
"这是时间之网的物理映射。"苏婉解释,"每一个光点都代表一个重要的历史事件。红色的是已经被永恒会干涉的节点,蓝色的是我们守护的节点。"
林深看到,红色的光点正在不断增加。
"他们在改写历史。"苏婉说,"三天后永恒会将在这座城市发动一次大规模行动试图夺取我们守护的所有时间碎片。如果他们成功了整个20世纪的历史都会被改写。"
"你想让我帮你阻止他们?"
"是的。你是时光编织者,拥有操控时间的能力。而且你来自未来,知道很多我们不知道的事情。"
林深陷入了沉思。时墨曾警告过他,不要随意干涉历史。但如果他不帮忙,历史可能会被永恒会改得更糟。
"我需要知道更多。"他说,"永恒会的目的到底是什么?他们想把历史改成什么样?"
苏婉带他来到一个密室,墙上挂着一幅巨大的画像。画中是一个穿着古代服饰的男人,眼神冷酷而深邃。
"这是永恒会的创始人,自称'时间之主'。没人知道他的真名,也没人知道他来自哪个时代。他相信,时间的自然流动是混乱和低效的,只有通过人为的控制,才能让历史朝着'正确'的方向发展。"
"所谓的'正确',就是他认为正确的方向。"林深冷笑,"这不是守护时间,是奴役时间。"
"没错。而且更可怕的是,他已经成功改写了很多历史。"苏婉指着沙盘上的一些暗淡的光点,"这些都是被抹除的时间线。在那些时间线里,可能有更好的未来,但都被他毁掉了。"
时墨突然开口:"如果我们阻止了永恒会,会不会也改变历史?"
这是个好问题。林深想起了时墨的教导——每一次干涉都会留下痕迹。
"会的。"苏婉坦诚地说,"但我们的改变是为了保护时间的自然流动,而不是强行扭曲它。这是有本质区别的。"
"真的有区别吗?"时墨质疑,"在我看来,你们和永恒会做的是同一件事——干涉历史。只是你们认为自己的理由更正当。"
苏婉沉默了。这个问题她也思考过无数次。
林深打破了沉默:"也许你说得对。但至少,我们可以选择不让一个疯子独自决定人类的命运。"
他转向苏婉:"我会帮你。但有一个条件——你要教我如何精确地穿越时间。我需要回到未来,回到我离开的那一刻。"
## 第七章:三天的准备
接下来的三天,林深接受了苏婉的训练。她虽然不是时光编织者,但作为守护者,她对时间的理解远超常人。
"时间旅行的关键,不是力量的大小,而是精确度。"苏婉在沙盘前讲解,"你需要在脑海中构建一个精确的时空坐标——不仅包括时间,还包括空间位置、甚至是量子态。差一点,你可能就会出现在墙里,或者半空中。"
林深努力练习着。他发现,怀表似乎能帮助他稳定时空坐标。每当他集中精神时,表盘上的星图就会变化,指向不同的时空节点。
时墨也没有闲着。虽然他还不是时光编织者,但他展现出了惊人的学习能力。苏婉教给他一些基础的时间感知技巧,他很快就能察觉到周围微小的时间波动。
"你有成为时光编织者的潜质。"苏婉评价道,"也许在未来,你真的会成为林深的老师。"
"如果我们能活到未来的话。"时墨苦笑。
第三天傍晚,守护者们开始集结。林深惊讶地发现,组织的成员远比他想象的多——有学者、商人、军人,甚至还有几个外国人。他们来自不同的阶层,但都有一个共同点:能够感知时间的异常。
"我们不是超能力者。"一个老学者对林深说,"我们只是比普通人更敏感一些。当历史被改写时,我们能感觉到那种违和感,就像看到一幅画上有一笔不协调的颜色。"
"那你们怎么对抗永恒会?他们可是有真正的时光编织者。"
"我们有这个。"老学者拿出一个小装置,看起来像是某种古老的罗盘,"时间锚。它能暂时稳定一个区域的时间流,让时光编织者的能力失效。当然,只能维持很短的时间。"
林深接过时间锚,感受着它散发出的能量。这是一种与他的能力完全相反的力量——如果说时光编织者是在时间之网上编织,那么时间锚就是把网固定住,让它无法被改变。
夜幕降临,行动开始了。
## 第八章:时空战场
永恒会选择的攻击地点是城市中心的一座钟楼。这里是这座城市的时间中心,也是时间之网的一个重要节点。如果他们在这里打开时空裂缝,就能直接接触到时间碎片的源头。
林深和守护者们提前埋伏在钟楼周围。午夜时分,永恒会的人出现了。
为首的是一个穿着黑色长袍的女人,她的头发是银白色的,眼睛闪烁着诡异的光芒。当她走过时,周围的时间流速明显变慢了。
"时间行者。"苏婉低声说,"永恒会的高级成员,能力仅次于时间之主。"
女人走到钟楼下,抬起手。空气开始扭曲,一道裂缝在她面前缓缓打开。裂缝的另一边,可以看到无数条时间线交织在一起,形成一个璀璨的漩涡。
"就是现在!"苏婉下令。
守护者们同时启动了时间锚。一个巨大的能量场展开,将整个钟楼笼罩其中。时间行者的动作突然停滞,裂缝也停止了扩张。
"什么?"女人惊怒地看向四周,"时间守护者?你们还没放弃吗?"
"永远不会。"苏婉走出来,手中握着一把古老的剑,"这是我们的职责。"
战斗爆发了。永恒会的成员们试图突破时间锚的限制,而守护者们拼死抵抗。林深看到,即使在时间锚的压制下,那些永恒会成员仍然能使用一些时间能力——他们的动作时快时慢,有时甚至能短暂地倒退几秒钟。
"他们的能力比我们想象的强。"时墨在林深身边说,"时间锚撑不了多久。"
果然,能量场开始闪烁。时间行者冷笑着,周围的时间流速再次开始变化。
"你们的抵抗毫无意义。"她说,"时间之主已经看到了未来。在所有的时间线里,我们都会赢。"
"那可不一定。"林深站了出来。
他握紧怀表,感受着体内涌动的力量。这一次,他不是要暂停时间,而是要做一件更困难的事——在时间锚的限制下,创造一个局部的时间循环。
怀表开始发光,表盘上的星图疯狂旋转。林深感到一股巨大的压力,仿佛要把他的意识撕碎。但他咬牙坚持着,将力量集中在时间行者身上。
突然,时间行者的动作开始重复——她抬手,放下,再抬手,再放下,陷入了一个三秒钟的时间循环。
"不可能!"她惊恐地喊道,"你怎么能在时间锚的范围内使用能力?"
"因为我不是在改变时间,而是在编织它。"林深说。这是他刚刚领悟到的——时间锚只能阻止对时间的暴力干涉,但无法阻止精细的编织。
趁着时间行者被困,守护者们迅速行动,关闭了时空裂缝,并夺回了几块时间碎片。
但就在这时,天空突然裂开了。
一个巨大的身影从裂缝中走出,周围的时间完全静止了。不是林深的时间暂停,而是一种更高层次的时间控制——连林深自己都无法动弹。
"时间之主..."苏婉艰难地说出这个名字。
那个身影俯视着下方,声音如同雷鸣:"愚蠢的守护者,你们以为能阻止我?我已经看过了所有的未来,在每一条时间线上,我都是胜利者。"
他抬起手,整个城市开始倒退。建筑物回到建造之前的状态,街道恢复成荒地,人们消失了。他在抹除这座城市的历史。
林深拼尽全力想要挣脱束缚,但完全无法动弹。他只能眼睁睁看着一切被改写。
就在这时,怀表突然剧烈震动起来。一道光芒从表中射出,击中了时间之主。
"什么?"时间之主第一次露出惊讶的表情,"这是...初始之钥?不可能,它应该已经失落了!"
光芒越来越强,林深感到自己的意识被拉入了一个奇异的空间。在那里,他看到了时间的本质——不是线性的流动,不是可以被控制的工具,而是一个活着的、有意识的存在。
而怀表,正是时间本身赋予时光编织者的信物。它不是用来控制时间的,而是用来与时间沟通的。
林深明白了。他一直在用错误的方式使用能力。时光编织者不应该是时间的主人,而应该是时间的伙伴。
## 第九章:时间的真相
在那个奇异的空间里,林深看到了时间的记忆。
他看到了宇宙诞生之初,时间从虚无中诞生,开始编织万物的命运。他看到了第一个时光编织者的出现——那是一个古老文明的智者,他无意中触碰到了时间的本质,获得了与时间对话的能力。
时间赋予他这种能力,不是为了让他改变历史,而是为了让他守护历史的多样性。因为时间知道,宇宙的美丽在于无限的可能性,而不是单一的"正确"道路。
但随着时光编织者越来越多,有些人开始滥用这种能力。他们试图按照自己的意志改写历史,创造"完美"的世界。其中最极端的,就是时间之主。
他原本也是一个时光编织者,但他在无数次穿越时空后,看到了太多的苦难和悲剧。他开始相信,只有通过绝对的控制,才能消除这些痛苦。于是他背叛了时间,试图成为时间的主人。
为了阻止他,时间将自己的核心力量封印在了初始之钥中——也就是林深手中的怀表。只有真正理解时间本质的人,才能唤醒它的力量。
"你明白了吗?"一个声音在林深的意识中响起,那是时间本身在说话,"我不需要主人,也不需要奴仆。我需要的是伙伴——那些能够理解我、尊重我,与我一起守护万物可能性的人。"
"我明白了。"林深说,"但我该怎么做?时间之主太强大了。"
"他的强大来自于对我的掠夺。但他忘记了一件事——被掠夺的力量,永远不如被赋予的力量。"
光芒包裹了林深,他感到自己的能力发生了质的变化。他不再是在"控制"时间,而是在与时间共舞。
当他睁开眼睛时,发现自己回到了钟楼前。时间之主的攻击被怀表的光芒挡住了,周围的时间流速恢复了正常。
"初始之钥选择了你?"时间之主的声音中带着难以置信,"不可能!我才是最强的时光编织者!"
"你不是。"林深平静地说,"因为你从一开始就走错了路。时光编织者不是时间的主人,而是时间的守护者。"
"荒谬!"时间之主怒吼,"没有控制,就没有秩序!我见过太多因为时间的随机性而导致的悲剧!只有我,才能创造真正完美的世界!"
他再次发动攻击,这次是全力以赴。整个城市的时间开始崩溃,过去、现在、未来的景象同时出现,相互重叠。林深看到同一个地方同时存在着不同时代的建筑,街上走着不同时代的人。
这是时间的混沌状态,如果不能阻止,整个时间线都会崩溃。
林深深吸一口气,握紧怀表。他不再试图对抗时间之主的力量,而是呼唤时间本身。
"请帮助我。"他在心中说。
怀表发出柔和的光芒,时间回应了他的呼唤。混乱的时间流开始平复,不同时代的景象逐渐分离。林深感到自己与时间之网建立了前所未有的连接——他能感受到每一个时间节点,每一条时间线,甚至能听到时间本身的呼吸。
"这不可能..."时间之主后退了一步,"你才刚刚觉醒,怎么可能达到这种程度?"
"因为我不是一个人在战斗。"林深说。
他看向周围的守护者们,看向苏婉,看向时墨。他们虽然没有强大的能力,但他们的信念和决心,也是时间之网的一部分。
"时间不是一个人的,而是所有人的。每个人的选择,每个人的行动,都在编织着时间之网。你想要独自控制时间,本身就是对时间的亵渎。"
林深抬起手,但他没有发动攻击。相反,他打开了一道门——一道通往时间本源的门。
"去看看吧。"他对时间之主说,"去看看真正的时间是什么样的。"
时间之主被一股无形的力量拉向那道门。他试图反抗,但在时间本身的意志面前,他的力量显得如此渺小。
"不!我不能失败!我是为了创造完美的世界!"他绝望地喊道。
"完美的世界不需要创造。"林深说,"因为每一个世界,都已经是完美的了。"
时间之主消失在门后。林深知道,他没有死,而是被送回了时间的起点,去重新学习时间的真谛。也许有一天,他会明白自己的错误。
## 第十章:归途
战斗结束了。永恒会的成员们失去了领袖,纷纷逃散。时间行者也挣脱了时间循环,但她没有继续战斗,而是深深地看了林深一眼,然后消失在了时空裂缝中。
"我们赢了?"时墨难以置信地问。
"暂时赢了。"苏婉说,"但永恒会不会就此消失。他们会重整旗鼓,在其他的时代继续他们的计划。"
"那我们该怎么办?"
"继续守护。"苏婉微笑,"这就是守护者的宿命。"
林深看着手中的怀表。现在他明白了,这块表不仅是时光编织者的信物,更是时间本身的礼物。它会在不同的时代选择不同的守护者,将他们联系在一起,共同守护时间的多样性。
"我该回去了。"他对苏婉说。
"我知道。"苏婉点头,"谢谢你的帮助。虽然我们可能不会再见面,但我相信,在时间之网的某个节点上,我们的命运会再次交汇。"
林深转向时墨:"在未来,你会成为一个伟大的时光编织者。记住今天发生的一切,记住时间的真谛。"
时墨认真地点头:"我会的。还有...这个给你。"
他递过来一个小包裹:"这是我这几天写的笔记,记录了我对时间的一些思考。也许对你有用。"
林深接过包裹,心中涌起一股暖流。他突然明白了,为什么未来的时墨会成为他的老师——因为这份师徒关系,从一开始就是一个时间循环。他来到过去,遇到了年轻的时墨,而时墨因为这次相遇,走上了成为时光编织者的道路。
"再见。"林深说,"或者说,未来见。"
他握紧怀表,集中精神,在脑海中构建起精确的时空坐标——2026年,那条街道,他离开的那一刻。
怀表发出光芒,时间之网在他周围展开。他看到无数条时间线交织在一起,每一条都通向不同的未来。他选择了属于自己的那一条,纵身跃入。
世界再次开始旋转。
## 第十一章:回归与新生
当林深睁开眼睛时,他发现自己站在那条熟悉的街道上。周围的一切都恢复了色彩,时间重新流动起来。
黑衣男人还保持着攻击的姿势,但他的表情变了——从冷笑变成了震惊。
"你...你去哪了?"他难以置信地问,"刚才那一瞬间,你从时间之网上消失了!"
"我去了一趟过去。"林深平静地说,"学到了一些东西。"
他不再感到恐惧。在1934年的经历让他真正理解了时光编织者的力量和责任。他现在知道自己不是在和一个敌人战斗而是在守护时间本身。
"你以为学了点皮毛就能对抗我?"黑衣男人冷笑,"我可是永恒会的..."
他的话没说完因为林深已经消失了。不是时间暂停而是时间跳跃——林深在时间流中前进了0.5秒,出现在了黑衣男人的身后。
"永恒会已经失败了。"林深说,"你们的领袖被时间本身放逐了。"
黑衣男人猛地转身,眼中闪过一丝慌乱:"不可能!时间之主是不朽的!"
"没有什么是不朽的。"林深抬起手,怀表在他掌心发光,"包括你们的野心。"
他没有攻击,而是打开了一道时空之门。门的另一边,可以看到永恒会的总部——一座漂浮在时间夹层中的堡垒。但现在,那座堡垒正在崩塌,因为失去了时间之主的力量支撑。
黑衣男人看到这一幕,脸色变得苍白。他知道,永恒会真的完了。
"你...你到底做了什么?"
"我只是让时间回到了它应有的样子。"林深说,"现在,你有两个选择:继续为一个已经不存在的组织效力,或者放弃这条错误的道路。"
黑衣男人沉默了很久,最终叹了口气:"我...我需要时间思考。"
"时间,我有的是。"林深微笑。
黑衣男人深深地看了他一眼,然后消失在了夜色中。林深知道,他不会再来找麻烦了。永恒会虽然还有残余势力,但失去了领袖和信仰,他们已经不再是威胁。
林深转身,准备回到古董店。他有太多问题要问时墨——关于过去,关于未来,关于他们之间跨越时空的师徒关系。
但当他走到古董店门口时,却发现店门紧闭,窗户上贴着"暂停营业"的告示。
"时墨?"林深推门,发现门没锁。
店内一片漆黑,只有那座巨大的挂钟还在滴答作响。林深打开灯,看到柜台上放着一封信。
信封上写着他的名字,笔迹苍老而熟悉。
林深打开信,开始阅读:
"林深,当你读到这封信时,我可能已经离开了。不要担心,我没有死,只是去了另一个时代。作为时光编织者,我们注定要在不同的时空中游走,守护时间的平衡。
你在1934年的出现改变了我的人生。在那之前我只是一个普通的大学生对未来充满迷茫。但遇到你之后我明白了自己的使命。
这些年来,我一直在等待这一天——等待你觉醒,等待你成长,等待你真正理解时光编织者的意义。现在,你已经准备好了。
怀表是我留给你的礼物,也是时间留给你的责任。它会指引你找到其他的时光编织者,找到那些需要守护的时间节点。记住,你不是一个人在战斗。在时间之网的每一个角落,都有守护者在默默坚守。
还有一件事我必须告诉你:时间之主虽然被放逐了,但他并没有被消灭。总有一天,他会回来,带着更强大的力量和更疯狂的计划。那时候,你需要做好准备。
但不要害怕。因为你拥有我没有的东西——你理解时间的真谛,你知道时光编织者不是时间的主人,而是时间的伙伴。这份理解,会让你比任何人都强大。
最后,谢谢你来到我的生命中。虽然对你来说,我是你的老师。但对我来说,你是改变我命运的人。我们的相遇,本身就是时间最美丽的编织。
愿时间与你同在。
——时墨"
林深读完信,眼眶有些湿润。他明白了,时墨的离开是必然的。作为时光编织者,他们注定要在不同的时空中履行自己的使命。也许有一天,他们会再次相遇,在某个需要守护的时间节点上。
他小心地收起信,环顾四周。古董店里的每一件物品,都承载着不同时代的记忆。他突然意识到,这家店本身就是一个时间节点,连接着过去、现在和未来。
"我会守护好这里的。"林深对着空荡荡的店铺说,"直到你回来。"
他走到那座巨大的挂钟前,伸手触摸表盘。钟面上的符文开始发光,向他展示着时间之网的全貌。他看到无数个时间节点在闪烁,有些明亮,有些暗淡,有些正在遭受威胁。
这就是他的使命——守护这些节点,保护时间的多样性,让每一个可能的未来都有机会实现。
林深深吸一口气,握紧了怀表。他知道,这只是开始。前方还有无数的挑战在等待着他,还有无数的时空需要他去守护。
但他已经准备好了。
## 第十二章:新的守护者
第二天,林深回到了大学。室友张浩惊讶地看着他:"你去哪了?我找了你一整晚!"
"抱歉,有些事情要处理。"林深歉意地说。
"什么事这么重要?"张浩关切地问,"你最近真的很不对劲。要不要我陪你去看医生?"
林深摇头:"不用,我很好。比以往任何时候都好。"
这不是谎言。虽然他现在肩负着守护时间的重任,但他感到前所未有的充实。他终于找到了自己存在的意义。
接下来的几天,林深一边继续上课,一边开始熟悉自己的能力。他发现,在理解了时间的真谛之后,他的能力变得更加精确和强大。他不仅能暂停时间,还能加速、减速,甚至能在小范围内创造时间循环。
更重要的是,他学会了如何感知时间的异常。当有人试图改变历史时,他能感觉到时间之网的震动,就像蜘蛛能感觉到网上的猎物一样。
一周后的晚上,林深正在图书馆自习,突然感到一阵强烈的时间波动。他抬起头,看到图书馆的灯光开始闪烁,周围的人动作变得迟缓。
有人在这里使用时间能力。
林深迅速启动时间暂停,世界陷入静止。他在静止的人群中搜索,很快发现了异常——一个女孩坐在角落里,她是唯一一个还在动的人。
女孩大约二十岁,穿着普通的学生装,但她的眼睛是淡金色的,正专注地看着一本古老的书。当她翻页时,书页上的文字会发光,然后消失。
"你在偷取知识?"林深走过去问。
女孩吓了一跳,猛地抬头。当她看到林深时,眼中闪过惊讶:"你...你也是时光编织者?"
"是的。"林深说,"你在做什么?"
女孩犹豫了一下,最终还是老实回答:"我在复制这本书的内容。这是一本孤本,记载着失传的古代医术。我想把它带回过去,救我的母亲。"
"你母亲生病了?"
"不,她已经死了。"女孩的声音颤抖,"三年前死于一场瘟疫。但如果我能把这些医术带回去,也许就能救她。"
林深沉默了。他理解女孩的心情,但他也知道,改变过去会带来什么后果。
"我知道你想说什么。"女孩苦笑,"改变过去会产生蝴蝶效应,可能会导致更糟糕的结果。但是...但是我不能眼睁睁看着她死去。我有这个能力,为什么不能用它来救我最爱的人?"
"因为时间不是为了满足个人愿望而存在的。"林深说,"如果每个时光编织者都用能力去改变自己的过去,时间之网会变成什么样?"
"我不在乎!"女孩激动地站起来,"那些大道理我都懂,但那是我的母亲!你有失去过最重要的人吗?你知道那种痛苦吗?"
林深想起了时墨的离开。虽然不是死亡,但那种失落感他能理解。
"我理解你的痛苦。"他轻声说,"但正因为理解,我才要阻止你。因为我知道,即使你救了你的母亲,你也不会快乐。"
"为什么?"
"因为你会一直活在恐惧中——恐惧你的改变会带来什么后果,恐惧你创造的新时间线会不会更糟,恐惧你是不是剥夺了其他人的幸福。"林深看着她的眼睛,"而且,你救回来的那个人,真的还是你的母亲吗?在新的时间线里,她可能会变成完全不同的人。"
女孩的眼泪流了下来:"那我该怎么办?就这样接受她的死亡?"
"不是接受,是铭记。"林深说,"你母亲的死亡是悲剧,但她的生命不是。她活过,爱过,留下了你。这些都是真实的,是任何时间线都无法改变的。"
他想起了时墨信中的话——时间最美丽的地方,就在于它的不可逆转。正因为每一刻都是独一无二的,所以每一刻都值得珍惜。
女孩沉默了很久,最终放下了那本书。
"你说得对。"她擦干眼泪,"我只是...太想念她了。"
"我知道。"林深说,"但你可以用其他方式纪念她。比如,用你的能力去帮助其他人,让更多的母亲能够活下来。"
女孩抬起头,眼中重新燃起了光芒:"你是说...我可以成为医生?用现代医学去救人?"
"为什么不呢?你有时间能力,可以在时停中学习更多的知识,可以在关键时刻延缓病人的生命。你能做的,比你想象的多得多。"
女孩认真地点了点头:"谢谢你。我叫苏晴,是...是苏婉的后代。"
林深震惊了:"苏婉?时间守护者的苏婉?"
"是的。我们家族世代都是守护者,但我是第一个觉醒了时光编织者能力的人。"苏晴说,"我奶奶在临终前告诉我,总有一天,我会遇到一个持有初始之钥的人。那个人会指引我走上正确的道路。"
林深拿出怀表,表盘上的星图正在发光。
"原来如此。"他明白了,"时间把我们联系在一起,不是偶然,而是必然。"
"那么,你愿意教我吗?"苏晴问,"教我如何成为一个真正的时光编织者?"
林深想起了时墨,想起了他们跨越时空的师徒关系。现在,轮到他成为老师了。
"我愿意。"他说,"但首先,你要明白一件事——时光编织者不是时间的主人,而是时间的守护者。我们的职责,是保护时间的多样性,而不是按照自己的意志改变它。"
"我明白了。"苏晴认真地说,"我会成为一个合格的守护者。"
林深微笑。他感到,时间之网又多了一个守护者,而他也不再是孤独的了。
## 第十三章:时间守护者联盟
在接下来的几个月里,林深一边教导苏晴,一边继续守护着这座城市的时间节点。他发现,虽然永恒会的主力被击败了,但仍然有一些残余势力在暗中活动,试图改变历史。
有一次,他阻止了一个试图回到过去阻止某项科技发明的时光编织者。那个人认为,那项发明最终导致了环境污染,所以应该被抹除。
"但你有没有想过,"林深对他说,"那项发明也拯救了无数人的生命?如果你抹除它,那些被救的人会怎样?"
"那就让他们死。"那人冷酷地说,"为了更大的利益,必须有所牺牲。"
"这就是你和时间之主的区别吗?"林深讽刺道,"他想创造完美的世界,你想创造干净的世界。但本质上,你们都是在把自己的意志强加给时间。"
最终林深击败了那个人并将他送到了时间守护者组织。是的那个组织在现代仍然存在虽然规模比1934年小得多但仍然在默默守护着时间。
组织的现任领导者是一个叫做陈默的老人。当林深第一次见到他时,陈默正在研究一张巨大的时间地图。
"你就是持有初始之钥的人?"陈默打量着林深,"比我想象的年轻。"
"年龄不重要。"林深说,"重要的是责任。"
陈默笑了:"说得好。苏婉在她的日记里提到过你,说你是她见过最有潜力的时光编织者。看来她没有看错。"
"你见过苏婉?"
"当然。她是我的曾祖母。"陈默说,"我们家族世代守护着她留下的遗产——关于时间的知识,关于永恒会的情报,还有对未来的预言。"
"预言?"
陈默拿出一本古老的笔记本,翻到某一页:"苏婉在晚年时写下了这段话:'在遥远的未来,当时间之主再次归来时,会有一个持有初始之钥的年轻人站出来对抗他。那个人会集结所有的时光编织者,建立一个真正的守护者联盟。他会证明,时间不需要主人,只需要守护者。'"
林深感到一阵寒意。时间之主会再次归来?
"什么时候?"他问。
"不知道。"陈默摇头,"时间预言总是模糊的。可能是明天,可能是一百年后。但有一件事是确定的——你需要做好准备。"
从那天起,林深开始着手建立守护者联盟。他通过怀表的指引,找到了散落在世界各地的时光编织者。
有的人像他一样,刚刚觉醒能力,对未来充满迷茫。有的人已经使用能力多年,但一直独自行动。还有的人曾经误入歧途,但在林深的劝说下,愿意改过自新。
其中最特别的是一个叫做艾莉的女孩。她来自未来——2156年一个人类已经开始殖民其他星球的时代。
"在我的时代,时间旅行已经成为现实。"艾莉说,"但也因此,时间犯罪变得猖獗。有人回到过去改变股市,有人试图暗杀历史人物,还有人想要阻止某些科技的发明。时间管理局应运而生,但他们的方法太过强硬,几乎要把时间变成一个监狱。"
"所以你逃到了这个时代?"
"不是逃,是寻找。"艾莉说,"我在档案中看到了关于初始之钥的记录,看到了关于你的预言。我相信,只有你能阻止未来的灾难。"
"什么灾难?"
艾莉的表情变得严肃:"在2156年时间之主回来了。他带着一支由时光编织者组成的军队发动了一场时空战争。那场战争波及了所有的时代无数的时间线被摧毁。最终人类不得不封锁了所有的时间通道把自己困在了单一的时间线上。"
林深倒吸一口冷气。这就是未来的结局吗?
"但是,"艾莉继续说,"在战争开始之前有一个传说流传开来。传说在21世纪初有一个持有初始之钥的守护者建立了一个联盟成功阻止了时间之主的第一次回归。如果那个传说是真的也许未来就能被改变。"
"你是说,我必须在时间之主回来之前,就把他阻止?"
"是的。"艾莉说,"而且时间不多了。根据我的计算,时间之主会在三个月后回归。他被放逐的地方是时间的起点,而从起点回到现在,需要穿越整个时间之网。这个过程需要时间,但一旦他完成穿越,就没有人能阻止他了。"
林深感到压力山大。三个月,他要集结所有的时光编织者,建立一个能够对抗时间之主的联盟,还要找到击败他的方法。
但他没有退缩。因为他知道,这就是他的使命。

View File

@@ -1,141 +0,0 @@
**更新日期2026年1月22日**
**生效日期2025年3月27日**
## 导言
欢迎访问 **编程小记**blog.cartol.top以下简称“本站”。本站深知个人信息对您的重要性并承诺依据《中华人民共和国个人信息保护法》《中华人民共和国网络安全法》等法律法规采取合理、有效的安全保护措施保障您的个人信息安全。
本政策旨在向您说明我们在您使用服务时如何收集、使用、保存及保护这些信息。**请您在使用本站评论功能前,仔细阅读并充分理解本政策。**
---
## 第一条 个人信息的收集与使用
本站仅基于以下目的收集和使用您的个人信息:
### 1.1 评论与互动
当您在本站发表评论时,需主动勾选同意本政策。为展示评论内容及回复通知,我们将收集您主动提供的:
* **昵称**(用于展示)
* **电子邮箱地址**(用于头像匹配、回复通知,**不公开显示**
* **个人网站地址**(选填)
### 1.2 运行安全与日志
为履行《网络安全法》规定的日志留存义务,及防范恶意攻击,服务器会自动记录:
* IP 地址
* 浏览器的 User-Agent 信息
* 访问时间与请求记录
### 1.3 Gravatar 头像服务
本站使用 Gravatar 展示用户头像。为了获取您的头像,我们会将您的电子邮箱地址进行 **MD5 哈希加密处理**(生成的字符串无法逆向还原出邮箱),然后发送给 Gravatar 服务器进行匹配。
* Gravatar 隐私政策:[https://automattic.com/privacy/](https://automattic.com/privacy/)
---
## 第二条 Cookies 及同类技术
为提升访问体验,本站使用 Cookies 技术:
1. **评论记忆**:在您的浏览器中保存昵称、邮箱及网址(有效期一年),以便下次自动填充。
2. **偏好设置**:记录您的夜间模式、字体大小等设置。
您可通过浏览器设置拒绝 Cookies但这可能导致您每次评论时都需要重新输入信息。
---
## 第三条 第三方服务与数据处理
为了提供更优质的服务(如加速访问、渲染公式),本站接入了部分第三方服务。
### 3.1 基础设施与 CDN受托处理
本站使用以下内容分发网络CDN服务以提升加载速度。您的 IP 地址及访问请求可能会经过这些服务商的节点,它们仅作为**受托处理者**提供传输服务:
* **多吉云 CDN**:静态资源加速
* **阿里云 ESA**图床服务img.api.cartol.top
### 3.2 嵌入内容
文章中可能包含来自第三方平台(如 Bilibili、GitHub 等)的嵌入内容。
**特别提示**:访问此类嵌入内容等同于直接访问第三方网站,这些第三方平台可能会独立收集您的数据或使用 Cookies请参阅其各自的隐私政策。
---
## 第四条 信息的存储与保护
### 4.1 存储地点
本站服务器位于**中国大陆**,由阿里云提供基础设施支持。本站已依法完成工信部 ICP 备案及公安联网备案,数据存储符合境内合规要求。
### 4.2 存储期限
* **评论数据**:为保留互动的上下文,评论内容将在本站运营期间长期保存,除非您请求删除。
* **日志数据**:网络日志将按法律规定留存不少于 6 个月。
### 4.3 安全措施
本站采用 HTTPS 加密传输、Web 应用防火墙WAF等技术措施防止个人信息被非法访问、泄露或篡改。
---
## 第五条 信息的共享与披露
**本站不会将您的个人信息出售或出租给任何第三方。** 仅在以下情形下,我们可能会披露您的信息:
1. **反垃圾检测**:评论内容及 IP 可能被发送至自动化的反垃圾评论服务进行扫描。
2. **法律要求**:根据法律法规、司法机关或行政机关的强制性要求。
---
## 第六条 您的权利
根据《个人信息保护法》,您享有以下权利:
* **查阅与复制**:您可以查阅您发表的评论内容。
* **删除权**:如您希望删除已发布的评论,或发现我们违规处理您的信息,请联系我们要删除。
* **撤回同意**:您可以随时停止使用评论功能。
**行使方式**:请通过邮件发送请求至 [admin@mail.cartol.top](mailto:admin@mail.cartol.top)。为验证身份,我们可能需要您使用发表评论时的邮箱发送请求。我们将在 **15 个工作日** 内回复。
---
## 第七条 未成年人保护
本站主要面向成年用户。
1. **未成年人使用**:若您未满 18 周岁,请在监护人指导下访问本站。
2. **儿童保护**:本站**不主动收集**不满 14 周岁儿童的个人信息。若您是 14 周岁以下的儿童,请勿在评论中使用真实姓名或提供详细联系方式。
3. **监护人权利**:若监护人发现我们在未获同意的情况下收集了儿童信息,请联系我们,我们将尽快删除相关数据。
---
## 第八条 政策更新与联系方式
### 8.1 政策更新
本政策可能随业务调整而更新,重大变更将通过网站公告通知。
### 8.2 联系我们
如有任何疑问或投诉,请联系:
* **电子邮箱**[admin@mail.cartol.top](mailto:admin@mail.cartol.top)
### 8.3 外部监管投诉
如您认为本站侵犯了您的合法权益,可向以下机构举报:
* **中国互联网违法和不良信息举报中心**[www.12377.cn](https://www.12377.cn)
* **广东省通信管理局**020-12300
---
## 网站备案信息公示
* **ICP 备案号**[粤ICP备2025392543号-1](https://beian.miit.gov.cn/)
* **公安联网备案号**[粤公网安备44180202000805号](http://www.beian.gov.cn/portal/registerSystemInfo%3Frecordcode%3D44180202000805)

View File

@@ -1,187 +0,0 @@
# AI 评论审核流程说明
## 流程概述
本文档通过 Mermaid 流程图详细展示用户提交评论后,系统进行 AI 审核的全流程涵盖预处理、规则判断、AI 检测、结果处理、数据学习等核心环节。
## 流程图
::: mermaid
flowchart TD
Start([用户提交评论]) --> PreProcess[预处理评论<br/>钩子preprocess_comment/post_comment_preprocessing]
PreProcess --> CheckEnabled{启用 AI 检测?}
CheckEnabled -->|否| SaveComment[保存评论]
CheckEnabled -->|是| CheckMode{检测模式?}
CheckMode -->|manual关闭实时检测| SaveComment
CheckMode -->|keyword/sample/all| CheckLoggedIn{已登录用户?}
CheckLoggedIn -->|是且跳过登录用户| SaveComment
CheckLoggedIn -->|否或不跳过| CheckWhitelist{在白名单?}
CheckWhitelist -->|是| SaveComment
CheckWhitelist -->|否| CheckKeyword[检查关键字触发]
CheckKeyword --> DecideCheck{决定是否检测}
DecideCheck -->|keyword模式且触发关键字| NeedCheck[需要检测]
DecideCheck -->|sample模式且触发关键字| NeedCheck
DecideCheck -->|sample模式且随机抽中| NeedCheck
DecideCheck -->|all模式| NeedCheck
DecideCheck -->|其他情况| SaveComment
NeedCheck --> SetPending[强制设置 comment_approved=0<br/>待审核状态]
SetPending --> SetFlag[设置 _argon_needs_spam_check=true]
SetFlag --> SaveKeywords[保存触发的关键字]
SaveKeywords --> SaveComment
SaveComment --> CommentPost[评论入库后处理<br/>钩子comment_post/post_comment_updatemetas]
CommentPost --> SaveMeta[保存评论元数据]
SaveMeta --> CheckNeedFlag{有 _argon_needs_spam_check 标记?}
CheckNeedFlag -->|否| ShowPending[显示审核中状态]
CheckNeedFlag -->|是| SaveCheckMeta[保存检测标记到数据库]
SaveCheckMeta --> AutoDetect[触发自动检测<br/>钩子comment_post/argon_auto_detect_spam_on_comment]
AutoDetect --> CheckSaved{有检测标记?}
CheckSaved -->|否| End1([结束])
CheckSaved -->|是| CheckDetected{已检测过?}
CheckDetected -->|是| End2([结束])
CheckDetected -->|否| CheckReason{检测原因?}
CheckReason -->|关键字触发| SyncDetect[立即同步检测]
CheckReason -->|其他| AsyncDetect[异步检测延迟1秒]
SyncDetect --> DetectHandler[检测处理函数<br/>argon_async_spam_detection_handler]
AsyncDetect --> DetectHandler
DetectHandler --> CallAI[调用 AI API<br/>argon_detect_spam_comment]
CallAI --> GetPrompt[根据 Prompt 模式获取提示词]
CallAI --> BuildContext[构建评论上下文(用户名+内容)]
BuildContext --> SendAPI[发送到 AI API]
SendAPI --> ParseResult[解析 AI 返回结果]
ParseResult --> SaveTime[记录检测时间]
SaveTime --> GenCode[生成识别码]
GenCode --> UpdateStats[更新用户统计]
UpdateStats --> CheckResult{检测结果?}
CheckResult -->|内容违规(高置信度)| CheckAction1{自动处理方式?}
CheckResult -->|内容违规(低置信度)| MarkLowConf[标记为疑似垃圾<br/>等待人工审核]
CheckResult -->|内容正常/用户名违规/无邮箱| TrashNoEmail[移入回收站]
CheckResult -->|内容正常/用户名违规/有邮箱| ChangeUsername[修改用户名]
CheckResult -->|都正常| MarkNormal[标记为正常评论]
CheckAction1 -->|trash| TrashComment[移入回收站]
CheckAction1 -->|hold| HoldComment[标记为待审核]
CheckAction1 -->|mark| MarkOnly[仅标记不处理]
TrashComment --> SendSpamEmail[发送垃圾评论通知]
HoldComment --> SendSpamEmail
MarkOnly --> SaveResult1[保存检测结果]
SendSpamEmail --> SaveResult1
MarkLowConf --> SaveResult2[保存检测结果]
TrashNoEmail --> SaveResult3[保存检测结果]
ChangeUsername --> GenNewName[生成新用户名]
GenNewName --> UpdateDB[更新数据库]
UpdateDB --> SendChangeEmail[发送用户名变更通知]
SendChangeEmail --> SaveResult4[保存检测结果]
MarkNormal --> SaveResult5[保存检测结果]
SaveResult1 --> AILearn{启用 AI 学习?}
SaveResult2 --> AILearn
SaveResult3 --> AILearn
SaveResult4 --> AILearn
SaveResult5 --> AILearn
AILearn -->|是| ExtractKeywords[提取关键词]
AILearn -->|否| DetectEnd([检测完成])
ExtractKeywords --> UpdateKeywords[更新学习关键词库]
UpdateKeywords --> DetectEnd
ShowPending --> UserView([用户看到:审核中状态])
DetectEnd --> AdminReview([管理员审核])
style Start fill:#e1f5e1,stroke:#2e7d32,stroke-width:2px
style End1 fill:#ffe1e1,stroke:#c62828,stroke-width:2px
style End2 fill:#ffe1e1,stroke:#c62828,stroke-width:2px
style DetectEnd fill:#e1f5e1,stroke:#2e7d32,stroke-width:2px
style UserView fill:#fff4e1,stroke:#ff8f00,stroke-width:2px
style AdminReview fill:#e1e8ff,stroke:#1565c0,stroke-width:2px
style NeedCheck fill:#ffcccc,stroke:#c62828,stroke-width:1px
style SetPending fill:#ffcccc,stroke:#c62828,stroke-width:1px
style TrashComment fill:#ff6b6b,stroke:#c62828,stroke-width:1px
style TrashNoEmail fill:#ff6b6b,stroke:#c62828,stroke-width:1px
style HoldComment fill:#ffa500,stroke:#ff8f00,stroke-width:1px
style MarkLowConf fill:#ffa500,stroke:#ff8f00,stroke-width:1px
style ChangeUsername fill:#4ecdc4,stroke:#00897b,stroke-width:1px
style MarkNormal fill:#95e1d3,stroke:#2e7d32,stroke-width:1px
:::
## 流程关键节点说明
### 1. 预处理阶段
- 触发 `preprocess_comment`/`post_comment_preprocessing` 钩子,完成评论基础清洗。
- 优先判断是否启用 AI 检测,未启用则直接保存评论。
### 2. 检测规则判断
- 检测模式分 4 类manual关闭、keyword关键字、sample抽样、all全量
- 白名单用户、已登录且配置跳过的用户,直接跳过检测。
- keyword/sample 模式下,仅关键字触发/随机抽中时才启动检测。
### 3. AI 检测执行
- 关键字触发:立即同步检测;其他情况:延迟 1 秒异步检测。
- 调用 `argon_detect_spam_comment` 接口,拼接用户名+评论内容作为上下文发送 AI。
### 4. 结果处理逻辑
| 检测结果 | 处理方式 |
|-------------------------|-----------------------------------|
| 内容违规(高置信度)| 按配置自动处理(移入回收站/待审核/仅标记) |
| 内容违规(低置信度)| 标记疑似垃圾,等待人工审核 |
| 用户名违规(无邮箱)| 直接移入回收站 |
| 用户名违规(有邮箱)| 自动生成新用户名并通知用户 |
| 内容+用户名均正常 | 标记为正常评论 |
### 5. 后续环节
- 所有检测结果均保存至数据库,支持 AI 学习功能(提取关键词更新词库)。
- 用户侧仅展示"审核中"状态,最终结果需管理员复核。
## 技术说明
### 使用的钩子
- `preprocess_comment` - 评论预处理
- `post_comment_preprocessing` - 评论预处理(备用)
- `comment_post` - 评论入库后
- `post_comment_updatemetas` - 更新评论元数据
- `argon_auto_detect_spam_on_comment` - 自动检测触发
### 关键函数
- `argon_detect_spam_comment()` - AI 检测主函数
- `argon_async_spam_detection_handler()` - 异步检测处理
- `argon_get_spam_detection_prompt()` - 获取检测 Prompt
- `argon_build_comment_context()` - 构建评论上下文
### 数据库字段
- `comment_approved` - 评论审核状态0=待审核1=已通过)
- `_argon_needs_spam_check` - 是否需要 AI 检测标记
- `_argon_spam_detection_result` - AI 检测结果
- `_argon_spam_detection_time` - 检测时间戳
- `_argon_spam_keywords` - 触发的关键字
## 配置建议
### 小型博客(评论量 < 100/天)
- 检测模式keyword关键字触发
- Prompt 模式:标准模式
- 自动处理阈值:置信度 > 0.9
### 中型博客(评论量 100-500/天)
- 检测模式sample智能抽样
- Prompt 模式:标准模式
- 自动处理阈值:置信度 > 0.85
### 大型博客(评论量 > 500/天)
- 检测模式sample智能抽样 30-40%
- Prompt 模式:极简模式
- 自动处理阈值:置信度 > 0.8
- 定期批量扫描待审核评论
## 注意事项
1. **API 成本控制**:合理设置检测模式和抽样比例
2. **误判处理**:始终保留人工审核入口
3. **隐私保护**:不要将敏感信息发送给 AI
4. **性能优化**:关键字触发使用同步检测,其他使用异步
5. **定期优化**:根据误判率调整 Prompt 和阈值

View File

@@ -1,297 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mermaid 代码块魔改测试</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.test-case {
background: white;
padding: 20px;
margin-bottom: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.test-case h3 {
margin-top: 0;
color: #333;
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
}
.test-case .description {
color: #666;
margin-bottom: 15px;
font-size: 14px;
}
.mermaid-from-codeblock {
background: #f8f9fa;
padding: 15px;
border-radius: 4px;
border: 1px solid #dee2e6;
}
pre {
background: #f8f9fa;
padding: 15px;
border-radius: 4px;
border: 1px solid #dee2e6;
overflow-x: auto;
}
code {
font-family: 'Courier New', Courier, monospace;
}
</style>
<!-- Mermaid 库 -->
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
</head>
<body>
<h1>Mermaid 代码块魔改测试</h1>
<p>本页面用于测试标准 Markdown 代码块 (<code>```mermaid</code>) 的 Mermaid 图表渲染功能。</p>
<!-- 测试用例 1标准格式 -->
<div class="test-case">
<h3>测试用例 1标准 Markdown 格式</h3>
<div class="description">
格式:<code>&lt;pre&gt;&lt;code class="language-mermaid"&gt;</code>
</div>
<pre><code class="language-mermaid">
flowchart TD
A[开始] --> B{判断}
B -->|是| C[执行操作]
B -->|否| D[跳过]
C --> E[结束]
D --> E
</code></pre>
</div>
<!-- 测试用例 2多个代码块 -->
<div class="test-case">
<h3>测试用例 2多个代码块批量处理</h3>
<div class="description">
测试同一页面中多个 Mermaid 代码块的处理
</div>
<pre><code class="language-mermaid">
graph LR
A[客户端] --> B[服务器]
B --> C[数据库]
C --> B
B --> A
</code></pre>
<br>
<pre><code class="language-mermaid">
sequenceDiagram
participant 用户
participant 系统
用户->>系统: 发送请求
系统->>用户: 返回响应
</code></pre>
</div>
<!-- 测试用例 3特殊字符 -->
<div class="test-case">
<h3>测试用例 3特殊字符保留</h3>
<div class="description">
测试箭头符号 <code>--&gt;</code>、双横线 <code>--</code>、等号 <code>==</code> 等特殊字符是否正确保留
</div>
<pre><code class="language-mermaid">
flowchart TD
A --> B
B -- 文本 --> C
C ==> D
D -.-> E
E ~~~ F
</code></pre>
</div>
<!-- 测试用例 4空代码块 -->
<div class="test-case">
<h3>测试用例 4空代码块边界情况</h3>
<div class="description">
测试空代码块是否会导致错误
</div>
<pre><code class="language-mermaid"></code></pre>
<p style="color: #666; font-size: 14px;">空代码块应该被跳过,不会创建容器</p>
</div>
<!-- 测试用例 5多行代码块 -->
<div class="test-case">
<h3>测试用例 5多行代码块换行符保留</h3>
<div class="description">
测试多行代码块中的换行符是否正确保留
</div>
<pre><code class="language-mermaid">
graph TB
subgraph 子图1
A1[节点A1]
A2[节点A2]
end
subgraph 子图2
B1[节点B1]
B2[节点B2]
end
A1 --> B1
A2 --> B2
</code></pre>
</div>
<!-- 测试用例 6简化格式 -->
<div class="test-case">
<h3>测试用例 6简化格式</h3>
<div class="description">
格式:<code>&lt;pre&gt;&lt;code class="mermaid"&gt;</code>
</div>
<pre><code class="mermaid">
pie title 数据分布
"类别A" : 45
"类别B" : 30
"类别C" : 25
</code></pre>
</div>
<!-- 测试用例 7无 pre 包裹 -->
<div class="test-case">
<h3>测试用例 7无 pre 包裹</h3>
<div class="description">
格式:<code>&lt;code class="language-mermaid"&gt;</code>(无 pre 标签)
</div>
<code class="language-mermaid">
flowchart LR
Start --> Stop
</code>
</div>
<!-- 测试用例 8自定义属性格式 -->
<div class="test-case">
<h3>测试用例 8自定义属性格式</h3>
<div class="description">
格式:<code>&lt;pre data-lang="mermaid"&gt;</code>
</div>
<pre data-lang="mermaid">
stateDiagram-v2
[*] --> 状态1
状态1 --> 状态2
状态2 --> [*]
</pre>
</div>
<!-- 测试用例 9复杂图表 -->
<div class="test-case">
<h3>测试用例 9复杂图表</h3>
<div class="description">
测试复杂的 Mermaid 图表渲染
</div>
<pre><code class="language-mermaid">
classDiagram
Animal <|-- Duck
Animal <|-- Fish
Animal <|-- Zebra
Animal : +int age
Animal : +String gender
Animal: +isMammal()
Animal: +mate()
class Duck{
+String beakColor
+swim()
+quack()
}
class Fish{
-int sizeInFeet
-canEat()
}
class Zebra{
+bool is_wild
+run()
}
</code></pre>
</div>
<script>
// 初始化 Mermaid
mermaid.initialize({
startOnLoad: false,
theme: 'default',
securityLevel: 'loose'
});
// 模拟主题的 convertMermaidCodeblocks 函数
function convertMermaidCodeblocks(){
const selectors = [
'pre > code.language-mermaid',
'pre > code.mermaid',
'code.language-mermaid',
'pre[data-lang="mermaid"]'
];
let processedCount = 0;
selectors.forEach(selector => {
document.querySelectorAll(selector).forEach(element => {
if (element.dataset.mermaidProcessed) {
return;
}
let code = element.textContent.trim();
if (!code) {
return;
}
const container = document.createElement('div');
container.className = 'mermaid-from-codeblock';
container.textContent = code;
container.dataset.processed = 'true';
const targetElement = element.closest('pre') || element;
if (targetElement.parentNode) {
targetElement.parentNode.replaceChild(container, targetElement);
processedCount++;
}
element.dataset.mermaidProcessed = 'true';
});
});
console.log('[Mermaid] 转换了 ' + processedCount + ' 个代码块');
}
// 渲染 Mermaid 图表
function renderMermaidCharts(){
const blocks = document.querySelectorAll('.mermaid-from-codeblock');
console.log('[Mermaid] 找到 ' + blocks.length + ' 个待渲染的图表');
blocks.forEach((block, index) => {
const id = 'mermaid-' + Date.now() + '-' + index;
const code = block.textContent;
try {
// Mermaid 10.x 使用 render 方法,返回 Promise
mermaid.render(id, code).then(function(result) {
block.innerHTML = result.svg;
console.log('[Mermaid] 成功渲染图表 ' + (index + 1));
}).catch(function(error) {
console.error('[Mermaid] 渲染失败:', error);
block.innerHTML = '<div style="color: red; padding: 10px; border: 1px solid red; border-radius: 4px;">渲染失败: ' + error.message + '</div>';
});
} catch (error) {
console.error('[Mermaid] 渲染异常:', error);
block.innerHTML = '<div style="color: red; padding: 10px; border: 1px solid red; border-radius: 4px;">渲染异常: ' + error.message + '</div>';
}
});
}
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function(){
console.log('[测试] 开始执行代码块转换');
convertMermaidCodeblocks();
console.log('[测试] 开始渲染 Mermaid 图表');
setTimeout(renderMermaidCharts, 100);
});
</script>
</body>
</html>

View File

@@ -1,374 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mermaid 库加载失败降级处理测试</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.test-section {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
color: #333;
border-bottom: 2px solid #5e72e4;
padding-bottom: 10px;
}
h2 {
color: #5e72e4;
margin-top: 0;
}
.mermaid {
background: #f8f9fa;
padding: 20px;
border-radius: 4px;
margin: 10px 0;
}
.mermaid-error-container {
background: #fff5f5;
border: 1px solid #fc8181;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
}
.mermaid-error-header {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.mermaid-error-icon {
font-size: 24px;
margin-right: 10px;
}
.mermaid-error-title {
font-size: 18px;
font-weight: bold;
color: #c53030;
}
.mermaid-error-body {
margin-bottom: 15px;
}
.mermaid-error-type {
font-weight: bold;
color: #742a2a;
margin: 5px 0;
}
.mermaid-error-message {
color: #742a2a;
margin: 5px 0;
}
.mermaid-error-code {
margin-top: 10px;
}
.mermaid-error-code summary {
cursor: pointer;
color: #5e72e4;
font-weight: bold;
}
.mermaid-error-code pre {
background: #2d3748;
color: #e2e8f0;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
margin-top: 10px;
}
.test-log {
background: #2d3748;
color: #e2e8f0;
padding: 15px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 14px;
max-height: 300px;
overflow-y: auto;
margin-top: 10px;
}
.test-log div {
margin: 5px 0;
}
.log-info {
color: #63b3ed;
}
.log-warn {
color: #f6ad55;
}
.log-error {
color: #fc8181;
}
.log-success {
color: #68d391;
}
.test-controls {
margin: 20px 0;
}
.test-controls button {
background: #5e72e4;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
margin-right: 10px;
font-size: 14px;
}
.test-controls button:hover {
background: #4c63d2;
}
.test-controls button:disabled {
background: #cbd5e0;
cursor: not-allowed;
}
</style>
</head>
<body>
<h1>🧪 Mermaid 库加载失败降级处理测试</h1>
<div class="test-section">
<h2>测试说明</h2>
<p>本测试页面用于验证 Mermaid 库加载失败时的降级处理机制。</p>
<ul>
<li><strong>测试 1</strong>: 主 CDN 加载失败,自动尝试备用 CDN</li>
<li><strong>测试 2</strong>: 所有 CDN 都失败,显示友好的错误提示</li>
<li><strong>测试 3</strong>: 备用 CDN 加载成功,正常渲染图表</li>
</ul>
</div>
<div class="test-section">
<h2>测试控制</h2>
<div class="test-controls">
<button onclick="testFailedMainCDN()">测试 1: 主 CDN 失败</button>
<button onclick="testAllCDNsFailed()">测试 2: 所有 CDN 失败</button>
<button onclick="testSuccessfulFallback()">测试 3: 备用 CDN 成功</button>
<button onclick="clearLog()">清空日志</button>
</div>
<div id="testLog" class="test-log"></div>
</div>
<div class="test-section">
<h2>测试图表</h2>
<div class="mermaid">
flowchart TD
A[开始] --> B{主 CDN 加载}
B -->|成功| C[渲染图表]
B -->|失败| D[尝试备用 CDN 1]
D -->|成功| C
D -->|失败| E[尝试备用 CDN 2]
E -->|成功| C
E -->|失败| F[显示错误提示]
</div>
</div>
<script>
// 模拟降级处理脚本
(function() {
'use strict';
// 备用 CDN URL 列表
const fallbackUrls = [
'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js',
'https://unpkg.com/mermaid@10/dist/mermaid.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.0.0/mermaid.min.js'
];
let loadAttempted = false;
// 日志函数
function log(message, type = 'info') {
const logDiv = document.getElementById('testLog');
const timestamp = new Date().toLocaleTimeString();
const logEntry = document.createElement('div');
logEntry.className = `log-${type}`;
logEntry.textContent = `[${timestamp}] ${message}`;
logDiv.appendChild(logEntry);
logDiv.scrollTop = logDiv.scrollHeight;
// 同时输出到控制台
console.log(`[Argon Mermaid] ${message}`);
}
/**
* 尝试从备用 CDN 加载 Mermaid 库
*/
window.argonMermaidLoadFallback = function() {
// 避免重复调用
if (loadAttempted) {
return;
}
loadAttempted = true;
log('主 CDN 加载失败,尝试备用 CDN', 'warn');
// 尝试加载备用 CDN
loadMermaidWithFallback(fallbackUrls, 0);
};
/**
* 递归加载备用 CDN
* @param {Array} urls - CDN URL 列表
* @param {number} index - 当前尝试的索引
*/
function loadMermaidWithFallback(urls, index) {
// 如果所有 CDN 都失败了
if (index >= urls.length) {
log('所有 CDN 加载失败', 'error');
showGlobalError();
return;
}
const url = urls[index];
log(`尝试从备用 CDN 加载: ${url}`, 'info');
// 创建 script 标签
const script = document.createElement('script');
script.src = url;
script.async = true;
// 加载失败,尝试下一个 CDN
script.onerror = function() {
log(`CDN ${url} 加载失败`, 'warn');
loadMermaidWithFallback(urls, index + 1);
};
// 加载成功,初始化 Mermaid
script.onload = function() {
log(`成功从备用 CDN 加载: ${url}`, 'success');
// 初始化 Mermaid
if (typeof window.mermaid !== 'undefined') {
window.mermaid.initialize({
startOnLoad: true,
theme: 'default'
});
log('Mermaid 初始化成功', 'success');
}
};
// 添加到页面
document.head.appendChild(script);
}
/**
* 显示全局错误提示
*/
function showGlobalError() {
// 查找所有 Mermaid 代码块
const selectors = [
'div.mermaid',
'pre code.language-mermaid',
'pre[data-lang="mermaid"]',
'code.mermaid'
];
let blocks = [];
selectors.forEach(function(selector) {
const elements = document.querySelectorAll(selector);
elements.forEach(function(element) {
if (!blocks.includes(element)) {
blocks.push(element);
}
});
});
log(`找到 ${blocks.length} 个 Mermaid 代码块,显示错误提示`, 'info');
// 在每个代码块位置显示错误提示
blocks.forEach(function(block) {
const errorContainer = document.createElement('div');
errorContainer.className = 'mermaid-error-container';
errorContainer.innerHTML = `
<div class="mermaid-error-header">
<span class="mermaid-error-icon">⚠️</span>
<span class="mermaid-error-title">Mermaid 库加载失败</span>
</div>
<div class="mermaid-error-body">
<p class="mermaid-error-type">错误类型: 网络错误</p>
<p class="mermaid-error-message">无法从任何 CDN 加载 Mermaid 库,请检查网络连接或联系管理员。</p>
</div>
<details class="mermaid-error-code">
<summary>查看原始代码</summary>
<pre><code class="language-mermaid">${escapeHtml(block.textContent)}</code></pre>
</details>
`;
block.parentNode.replaceChild(errorContainer, block);
});
}
/**
* HTML 转义
* @param {string} text - 要转义的文本
* @returns {string} 转义后的文本
*/
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 测试函数
window.testFailedMainCDN = function() {
log('=== 开始测试 1: 主 CDN 失败 ===', 'info');
loadAttempted = false;
argonMermaidLoadFallback();
};
window.testAllCDNsFailed = function() {
log('=== 开始测试 2: 所有 CDN 失败 ===', 'info');
loadAttempted = false;
// 使用无效的 URL 列表
loadMermaidWithFallback(['https://invalid-cdn-1.com/mermaid.js', 'https://invalid-cdn-2.com/mermaid.js'], 0);
};
window.testSuccessfulFallback = function() {
log('=== 开始测试 3: 备用 CDN 成功 ===', 'info');
loadAttempted = false;
// 直接加载有效的 CDN
loadMermaidWithFallback(fallbackUrls, 0);
};
window.clearLog = function() {
document.getElementById('testLog').innerHTML = '';
log('日志已清空', 'info');
};
// 页面加载完成后的初始化
log('测试页面加载完成', 'success');
log('点击上方按钮开始测试', 'info');
})();
</script>
</body>
</html>

View File

@@ -1,276 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mermaid 修复测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.test-section {
background: white;
padding: 20px;
margin-bottom: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.test-section h2 {
color: #5e72e4;
border-bottom: 2px solid #5e72e4;
padding-bottom: 10px;
}
.test-case {
margin: 15px 0;
padding: 15px;
background: #f8f9fa;
border-left: 4px solid #5e72e4;
}
.test-case h3 {
margin-top: 0;
color: #333;
}
.expected {
color: #28a745;
font-weight: bold;
}
.code-block {
background: #2d2d2d;
color: #d4d4d4;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 14px;
}
.mermaid {
background: white;
padding: 20px;
border: 1px solid #ddd;
border-radius: 4px;
margin: 10px 0;
}
pre code.language-mermaid {
display: block;
background: #f8f9fa;
padding: 15px;
border: 2px dashed #5e72e4;
border-radius: 4px;
}
</style>
</head>
<body>
<h1>🧪 Mermaid 修复测试页面</h1>
<p>本页面用于测试两个关键修复:</p>
<ol>
<li><strong>代码高亮排除 mermaid</strong>mermaid 代码块不应被代码高亮处理</li>
<li><strong>容器语法处理空行</strong>:容器语法中的空行应被正确保留</li>
</ol>
<!-- 测试 1: 代码高亮排除 -->
<div class="test-section">
<h2>测试 1: 代码高亮排除 Mermaid</h2>
<div class="test-case">
<h3>测试 1.1: 标准 Markdown 格式(应该被排除)</h3>
<p class="expected">✅ 预期:不应出现代码高亮窗口,直接渲染为 Mermaid 图表</p>
<pre><code class="language-mermaid">
flowchart TD
A[开始] --> B[处理]
B --> C[结束]
</code></pre>
</div>
<div class="test-case">
<h3>测试 1.2: 标准 div.mermaid 格式(应该被排除)</h3>
<p class="expected">✅ 预期:不应出现代码高亮窗口,直接渲染为 Mermaid 图表</p>
<div class="mermaid">
flowchart LR
A[用户] --> B{登录?}
B -->|是| C[显示首页]
B -->|否| D[跳转登录页]
</div>
</div>
<div class="test-case">
<h3>测试 1.3: 普通代码块(应该被高亮)</h3>
<p class="expected">✅ 预期:应该出现代码高亮窗口</p>
<pre><code class="language-javascript">
function hello() {
console.log('Hello World');
}
</code></pre>
</div>
</div>
<!-- 测试 2: 容器语法空行处理 -->
<div class="test-section">
<h2>测试 2: 容器语法空行处理</h2>
<div class="test-case">
<h3>测试 2.1: 包含空行的流程图</h3>
<p class="expected">✅ 预期:空行应被保留,图表正常渲染</p>
<div class="code-block">
::: mermaid
flowchart TD
A[开始]
B[处理步骤1]
C[处理步骤2]
D[结束]
A --> B
B --> C
C --> D
:::
</div>
<p>实际渲染:</p>
<p>
::: mermaid
flowchart TD
A[开始]
B[处理步骤1]
C[处理步骤2]
D[结束]
A --> B
B --> C
C --> D
:::
</p>
</div>
<div class="test-case">
<h3>测试 2.2: 包含空行的时序图</h3>
<p class="expected">✅ 预期:空行应被保留,图表正常渲染</p>
<div class="code-block">
::: mermaid
sequenceDiagram
participant A as Alice
participant B as Bob
A->>B: Hello Bob!
Note over A,B: 这是一个注释
B-->>A: Hi Alice!
:::
</div>
<p>实际渲染:</p>
<p>
::: mermaid
sequenceDiagram
participant A as Alice
participant B as Bob
A->>B: Hello Bob!
Note over A,B: 这是一个注释
B-->>A: Hi Alice!
:::
</p>
</div>
<div class="test-case">
<h3>测试 2.3: 包含多个连续空行</h3>
<p class="expected">✅ 预期:多个空行应被保留,图表正常渲染</p>
<div class="code-block">
::: mermaid
flowchart LR
A[步骤A]
B[步骤B]
C[步骤C]
A --> B --> C
:::
</div>
<p>实际渲染:</p>
<p>
::: mermaid
flowchart LR
A[步骤A]
B[步骤B]
C[步骤C]
A --> B --> C
:::
</p>
</div>
</div>
<!-- 测试 3: WP-Markdown 格式 -->
<div class="test-section">
<h2>测试 3: WP-Markdown 格式兼容性</h2>
<div class="test-case">
<h3>测试 3.1: document.write 格式</h3>
<p class="expected">✅ 预期:正确提取代码并渲染</p>
<div class="mermaid">
<script>document.write("flowchart TD\n A[开始] --> B[结束]")</script>
</div>
</div>
</div>
<!-- 测试结果说明 -->
<div class="test-section">
<h2>📋 测试检查清单</h2>
<ul>
<li>
<input type="checkbox" id="check1">
<label for="check1">测试 1.1: Markdown 格式的 mermaid 代码块没有代码高亮窗口</label>
</li>
<li>
<input type="checkbox" id="check2">
<label for="check2">测试 1.2: div.mermaid 格式没有代码高亮窗口</label>
</li>
<li>
<input type="checkbox" id="check3">
<label for="check3">测试 1.3: 普通 JavaScript 代码有代码高亮窗口</label>
</li>
<li>
<input type="checkbox" id="check4">
<label for="check4">测试 2.1: 包含空行的流程图正常渲染</label>
</li>
<li>
<input type="checkbox" id="check5">
<label for="check5">测试 2.2: 包含空行的时序图正常渲染</label>
</li>
<li>
<input type="checkbox" id="check6">
<label for="check6">测试 2.3: 包含多个连续空行的图表正常渲染</label>
</li>
<li>
<input type="checkbox" id="check7">
<label for="check7">测试 3.1: WP-Markdown 格式正常渲染</label>
</li>
</ul>
</div>
<script>
// 简单的日志输出
console.log('[Mermaid Test] 测试页面已加载');
console.log('[Mermaid Test] 请检查:');
console.log('[Mermaid Test] 1. mermaid 代码块是否被代码高亮处理');
console.log('[Mermaid Test] 2. 容器语法中的空行是否被正确保留');
console.log('[Mermaid Test] 3. 所有图表是否正常渲染');
</script>
</body>
</html>

View File

@@ -1,186 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mermaid 换行符测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.test-case {
background: white;
padding: 20px;
margin: 20px 0;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h2 {
color: #333;
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
}
.success {
color: green;
font-weight: bold;
}
.error {
color: red;
font-weight: bold;
}
pre {
background: #f4f4f4;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
}
</style>
</head>
<body>
<h1>Mermaid 换行符测试</h1>
<p>测试 WP-Markdown 生成的 HTML 中 &lt;br&gt; 标签是否正确转换为换行符</p>
<!-- 测试用例 1: 模拟 WP-Markdown 生成的 HTML -->
<div class="test-case">
<h2>测试 1: 模拟 WP-Markdown 格式(带 &lt;br&gt; 标签)</h2>
<p>::: mermaid<br>
flowchart TD<br>
Start([开始]) --> Process[处理]<br>
Process --> End([结束])<br>
:::</p>
<div id="result1"></div>
</div>
<!-- 测试用例 2: 复杂流程图 -->
<div class="test-case">
<h2>测试 2: 复杂流程图(多行文本)</h2>
<p>::: mermaid<br>
flowchart TD<br>
A[用户提交评论] --> B{启用 AI 检测?}<br>
B -->|是| C[AI 检测]<br>
B -->|否| D[直接保存]<br>
C --> E{检测结果?}<br>
E -->|垃圾评论| F[移入回收站]<br>
E -->|正常评论| G[保存评论]<br>
:::</p>
<div id="result2"></div>
</div>
<!-- 测试用例 3: 带样式的流程图 -->
<div class="test-case">
<h2>测试 3: 带样式定义</h2>
<p>::: mermaid<br>
flowchart LR<br>
A[开始] --> B[处理]<br>
B --> C[结束]<br>
style A fill:#e1f5e1,stroke:#2e7d32<br>
style C fill:#ffe1e1,stroke:#c62828<br>
:::</p>
<div id="result3"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
<script>
// 模拟 Argon 主题的 htmlToText 函数
function htmlToText(html) {
// 将 <br> 和 <br/> 转换为换行符
let text = html.replace(/<br\s*\/?>/gi, '\n');
// 移除其他 HTML 标签
text = text.replace(/<[^>]+>/g, '');
// 解码 HTML 实体
text = text
.replace(/&nbsp;/g, ' ')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&amp;/g, '&')
.replace(/&quot;/g, '"')
.replace(/&#039;/g, "'");
return text;
}
// 提取容器语法内容
function extractContainerContent(element) {
let html = element.innerHTML;
let text = element.textContent.trim();
console.log('[测试] 原始 HTML:', html);
console.log('[测试] 原始文本:', text);
if (!text.startsWith('::: mermaid')) {
return null;
}
// 使用 htmlToText 转换
let content = htmlToText(html);
console.log('[测试] 转换后文本:', content);
// 移除开始和结束标记
content = content.replace(/^:::\s*mermaid\s*/i, '').trim();
content = content.replace(/:::\s*$/, '').trim();
console.log('[测试] 最终代码:', content);
return content;
}
// 测试函数
function testMermaid(testId, resultId) {
const element = document.querySelector(`#${testId}`).previousElementSibling;
const resultDiv = document.getElementById(resultId);
try {
const code = extractContainerContent(element);
if (!code) {
resultDiv.innerHTML = '<p class="error">❌ 提取失败:未找到代码</p>';
return;
}
// 检查换行符
const lines = code.split('\n');
console.log(`[测试 ${testId}] 提取到 ${lines.length} 行代码`);
// 渲染图表
mermaid.render(`mermaid-${testId}`, code).then(result => {
resultDiv.innerHTML = `
<p class="success">✅ 渲染成功</p>
<p>提取到 ${lines.length} 行代码</p>
<details>
<summary>查看提取的代码</summary>
<pre>${code}</pre>
</details>
${result.svg}
`;
}).catch(error => {
resultDiv.innerHTML = `
<p class="error">❌ 渲染失败: ${error.message}</p>
<details>
<summary>查看提取的代码</summary>
<pre>${code}</pre>
</details>
`;
});
} catch (error) {
resultDiv.innerHTML = `<p class="error">❌ 错误: ${error.message}</p>`;
}
}
// 初始化
document.addEventListener('DOMContentLoaded', function() {
mermaid.initialize({
startOnLoad: false,
theme: 'default',
securityLevel: 'loose'
});
// 运行测试
testMermaid('test1', 'result1');
testMermaid('test2', 'result2');
testMermaid('test3', 'result3');
});
</script>
</body>
</html>

View File

@@ -1,383 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mermaid 图表尺寸优化测试</title>
<link rel="stylesheet" href="../style.css">
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.test-section {
background: white;
padding: 20px;
margin-bottom: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.test-section h2 {
color: #5e72e4;
border-bottom: 2px solid #5e72e4;
padding-bottom: 10px;
margin-top: 0;
}
.test-case {
margin: 20px 0;
padding: 15px;
background: #f8f9fa;
border-left: 4px solid #5e72e4;
}
.test-case h3 {
margin-top: 0;
color: #333;
font-size: 16px;
}
.expected {
color: #28a745;
font-weight: bold;
margin: 10px 0;
}
.info {
color: #666;
font-size: 14px;
margin: 5px 0;
}
.toggle-theme {
position: fixed;
top: 20px;
right: 20px;
padding: 10px 20px;
background: #5e72e4;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
z-index: 1000;
}
.toggle-theme:hover {
background: #4c63d2;
}
</style>
</head>
<body>
<button class="toggle-theme" onclick="toggleTheme()">🌓 切换夜间模式</button>
<h1>🎨 Mermaid 图表尺寸优化测试</h1>
<p>本页面用于测试 Mermaid 图表的尺寸优化,确保低内容图表不会显示过大。</p>
<!-- 测试 1: 极简图表 -->
<div class="test-section">
<h2>测试 1: 极简图表2-3个节点</h2>
<div class="test-case">
<h3>1.1 简单流程图2个节点</h3>
<p class="expected">✅ 预期:图表高度不超过 600px居中显示</p>
<p class="info">优化前:可能显示超大,占据整个屏幕</p>
<div class="mermaid-container">
<div class="mermaid">
flowchart TD
A[开始] --> B[结束]
</div>
</div>
</div>
<div class="test-case">
<h3>1.2 简单流程图3个节点</h3>
<p class="expected">✅ 预期:图表高度不超过 600px居中显示</p>
<div class="mermaid-container">
<div class="mermaid">
flowchart LR
A[输入] --> B[处理] --> C[输出]
</div>
</div>
</div>
<div class="test-case">
<h3>1.3 简单判断流程</h3>
<p class="expected">✅ 预期:图表高度不超过 600px居中显示</p>
<div class="mermaid-container">
<div class="mermaid">
flowchart TD
A[用户] --> B{登录?}
B -->|是| C[首页]
B -->|否| D[登录页]
</div>
</div>
</div>
</div>
<!-- 测试 2: 中等复杂度图表 -->
<div class="test-section">
<h2>测试 2: 中等复杂度图表5-10个节点</h2>
<div class="test-case">
<h3>2.1 标准流程图</h3>
<p class="expected">✅ 预期:图表高度不超过 600px自适应内容</p>
<div class="mermaid-container">
<div class="mermaid">
flowchart TD
A[开始] --> B[输入数据]
B --> C{数据有效?}
C -->|是| D[处理数据]
C -->|否| E[显示错误]
D --> F[保存结果]
E --> G[结束]
F --> G
</div>
</div>
</div>
<div class="test-case">
<h3>2.2 时序图</h3>
<p class="expected">✅ 预期:图表高度不超过 600px</p>
<div class="mermaid-container">
<div class="mermaid">
sequenceDiagram
participant U as 用户
participant F as 前端
participant B as 后端
participant D as 数据库
U->>F: 提交表单
F->>B: 发送请求
B->>D: 查询数据
D-->>B: 返回结果
B-->>F: 返回响应
F-->>U: 显示结果
</div>
</div>
</div>
</div>
<!-- 测试 3: 复杂图表 -->
<div class="test-section">
<h2>测试 3: 复杂图表10+个节点)</h2>
<div class="test-case">
<h3>3.1 复杂流程图</h3>
<p class="expected">✅ 预期:图表高度限制在 600px出现滚动条</p>
<p class="info">优化:超过最大高度时,容器会出现垂直滚动条</p>
<div class="mermaid-container">
<div class="mermaid">
flowchart TD
A[开始] --> B[初始化]
B --> C{检查权限}
C -->|有权限| D[加载配置]
C -->|无权限| E[显示错误]
D --> F{配置有效?}
F -->|是| G[连接数据库]
F -->|否| H[使用默认配置]
G --> I{连接成功?}
I -->|是| J[加载数据]
I -->|否| K[重试连接]
K --> L{重试次数<3?}
L -->|是| G
L -->|否| M[连接失败]
H --> J
J --> N[处理数据]
N --> O{处理成功?}
O -->|是| P[显示结果]
O -->|否| Q[显示错误]
P --> R[记录日志]
Q --> R
E --> R
M --> R
R --> S[结束]
</div>
</div>
</div>
<div class="test-case">
<h3>3.2 类图</h3>
<p class="expected">✅ 预期:图表高度限制在 600px</p>
<div class="mermaid-container">
<div class="mermaid">
classDiagram
class Animal {
+String name
+int age
+makeSound()
+eat()
}
class Dog {
+String breed
+bark()
+fetch()
}
class Cat {
+String color
+meow()
+scratch()
}
class Bird {
+boolean canFly
+sing()
+fly()
}
Animal <|-- Dog
Animal <|-- Cat
Animal <|-- Bird
</div>
</div>
</div>
</div>
<!-- 测试 4: 横向图表 -->
<div class="test-section">
<h2>测试 4: 横向图表(宽度测试)</h2>
<div class="test-case">
<h3>4.1 横向流程图</h3>
<p class="expected">✅ 预期:宽度自适应,出现横向滚动条</p>
<div class="mermaid-container">
<div class="mermaid">
flowchart LR
A[步骤1] --> B[步骤2] --> C[步骤3] --> D[步骤4] --> E[步骤5]
E --> F[步骤6] --> G[步骤7] --> H[步骤8] --> I[步骤9] --> J[步骤10]
</div>
</div>
</div>
</div>
<!-- 测试 5: 响应式测试 -->
<div class="test-section">
<h2>测试 5: 响应式适配</h2>
<div class="test-case">
<h3>5.1 移动端适配</h3>
<p class="expected">✅ 预期:在移动端(<768px高度限制为 500px</p>
<p class="info">请调整浏览器窗口大小测试</p>
<div class="mermaid-container">
<div class="mermaid">
flowchart TD
A[移动端] --> B[平板]
B --> C[桌面端]
A --> D[响应式]
B --> D
C --> D
</div>
</div>
</div>
</div>
<!-- 测试结果检查清单 -->
<div class="test-section">
<h2>📋 测试检查清单</h2>
<ul style="list-style: none; padding: 0;">
<li style="margin: 10px 0;">
<input type="checkbox" id="check1">
<label for="check1">✅ 极简图表2-3节点不会显示过大高度合理</label>
</li>
<li style="margin: 10px 0;">
<input type="checkbox" id="check2">
<label for="check2">✅ 图表在容器中居中显示</label>
</li>
<li style="margin: 10px 0;">
<input type="checkbox" id="check3">
<label for="check3">✅ 中等复杂度图表显示正常,高度不超过 600px</label>
</li>
<li style="margin: 10px 0;">
<input type="checkbox" id="check4">
<label for="check4">✅ 复杂图表高度限制在 600px出现滚动条</label>
</li>
<li style="margin: 10px 0;">
<input type="checkbox" id="check5">
<label for="check5">✅ 横向图表宽度自适应,出现横向滚动条</label>
</li>
<li style="margin: 10px 0;">
<input type="checkbox" id="check6">
<label for="check6">✅ 夜间模式下图表显示正常</label>
</li>
<li style="margin: 10px 0;">
<input type="checkbox" id="check7">
<label for="check7">✅ 移动端(<768px高度限制为 500px</label>
</li>
<li style="margin: 10px 0;">
<input type="checkbox" id="check8">
<label for="check8">✅ 小屏幕(<480px高度限制为 400px</label>
</li>
</ul>
</div>
<!-- 优化说明 -->
<div class="test-section">
<h2>🎯 优化说明</h2>
<h3>主要改进:</h3>
<ol>
<li><strong>最大高度限制</strong>SVG 最大高度设置为 600px桌面端</li>
<li><strong>居中显示</strong>:使用 flexbox 让图表在容器中居中</li>
<li><strong>宽度自适应</strong>:使用 <code>width: auto !important</code> 让图表保持原始宽高比</li>
<li><strong>响应式适配</strong>
<ul>
<li>平板(<768px最大高度 500px</li>
<li>手机(<480px最大高度 400px</li>
</ul>
</li>
<li><strong>最小高度</strong>:容器最小高度 100px避免空白过小</li>
</ol>
<h3>CSS 关键属性:</h3>
<pre style="background: #f8f9fa; padding: 15px; border-radius: 4px; overflow-x: auto;"><code>.mermaid-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100px;
}
.mermaid-container svg {
max-width: 100%;
max-height: 600px;
height: auto;
width: auto !important;
}</code></pre>
</div>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
<script>
// 初始化 Mermaid
mermaid.initialize({
startOnLoad: true,
theme: 'default',
securityLevel: 'loose',
flowchart: {
useMaxWidth: false,
htmlLabels: true
}
});
// 切换夜间模式
function toggleTheme() {
document.documentElement.classList.toggle('darkmode');
const isDark = document.documentElement.classList.contains('darkmode');
// 重新初始化 Mermaid 以应用新主题
mermaid.initialize({
startOnLoad: false,
theme: isDark ? 'dark' : 'default',
securityLevel: 'loose',
flowchart: {
useMaxWidth: false,
htmlLabels: true
}
});
console.log(`[Theme] 切换到 ${isDark ? '夜间' : '日间'} 模式`);
}
// 页面加载完成后的日志
window.addEventListener('load', function() {
console.log('[Mermaid Size Test] 测试页面已加载');
console.log('[Mermaid Size Test] 请检查:');
console.log('[Mermaid Size Test] 1. 极简图表是否显示合理大小');
console.log('[Mermaid Size Test] 2. 图表是否在容器中居中');
console.log('[Mermaid Size Test] 3. 复杂图表是否有高度限制');
console.log('[Mermaid Size Test] 4. 响应式适配是否正常');
});
</script>
</body>
</html>

View File

@@ -1,365 +0,0 @@
# AI 垃圾评论检测系统架构
## 系统概述
本文档展示 Argon 主题的 AI 垃圾评论检测系统的完整架构和流程。
## 主流程图
[mermaid theme="default" width="100%" align="center"]
flowchart TD
Start([用户提交评论]) --> PreProcess[预处理评论]
PreProcess --> CheckEnabled{启用 AI 检测?}
CheckEnabled -->|否| DirectSave[直接保存评论]
CheckEnabled -->|是| CheckMode{检测模式?}
CheckMode -->|manual| DirectSave
CheckMode -->|keyword| CheckKeyword[检查关键字]
CheckMode -->|sample| CheckSample[智能抽样]
CheckMode -->|all| NeedCheck[需要检测]
CheckKeyword --> KeywordMatch{匹配关键字?}
KeywordMatch -->|是| NeedCheck
KeywordMatch -->|否| DirectSave
CheckSample --> SampleDecision{抽中?}
SampleDecision -->|是| NeedCheck
SampleDecision -->|否| DirectSave
NeedCheck --> SetPending[设置待审核状态]
SetPending --> SaveWithFlag[保存评论并标记]
SaveWithFlag --> TriggerDetect[触发 AI 检测]
TriggerDetect --> CallAPI[调用 AI API]
CallAPI --> ParseResult[解析结果]
ParseResult --> CheckConfidence{置信度?}
CheckConfidence -->|高| AutoHandle[自动处理]
CheckConfidence -->|中| ManualReview[人工审核]
CheckConfidence -->|低| MarkNormal[标记正常]
AutoHandle --> CheckAction{处理方式?}
CheckAction -->|trash| MoveTrash[移入回收站]
CheckAction -->|hold| KeepPending[保持待审核]
CheckAction -->|mark| JustMark[仅标记]
MoveTrash --> NotifyAdmin[通知管理员]
KeepPending --> NotifyAdmin
JustMark --> SaveResult[保存结果]
ManualReview --> SaveResult
MarkNormal --> SaveResult
NotifyAdmin --> SaveResult
SaveResult --> AILearn{启用学习?}
AILearn -->|是| ExtractKeywords[提取关键词]
AILearn -->|否| End([结束])
ExtractKeywords --> UpdateDB[更新词库]
UpdateDB --> End
DirectSave --> End
style Start fill:#e1f5e1,stroke:#2e7d32,stroke-width:2px
style End fill:#e1f5e1,stroke:#2e7d32,stroke-width:2px
style MoveTrash fill:#ff6b6b,stroke:#c62828,stroke-width:2px
style MarkNormal fill:#95e1d3,stroke:#2e7d32,stroke-width:2px
style ManualReview fill:#ffa500,stroke:#ff8f00,stroke-width:2px
[/mermaid]
## 检测模式详解
[mermaid theme="default" width="80%" align="center"]
flowchart LR
subgraph Manual[Manual 模式]
M1[关闭实时检测]
M2[仅手动扫描]
end
subgraph Keyword[Keyword 模式]
K1[关键字触发]
K2[精准检测]
K3[低成本]
end
subgraph Sample[Sample 模式]
S1[智能抽样]
S2[平衡准确性]
S3[控制成本]
end
subgraph All[All 模式]
A1[全量检测]
A2[最高准确性]
A3[高成本]
end
style Manual fill:#e3f2fd
style Keyword fill:#fff3e0
style Sample fill:#f3e5f5
style All fill:#fce4ec
[/mermaid]
## AI 检测流程
[mermaid theme="default" width="90%"]
sequenceDiagram
participant User as 用户
participant WP as WordPress
participant Argon as Argon 主题
participant AI as AI API
participant DB as 数据库
User->>WP: 提交评论
WP->>Argon: 触发 preprocess_comment
Argon->>Argon: 检查检测规则
alt 需要检测
Argon->>DB: 保存评论(待审核)
Argon->>AI: 发送检测请求
AI-->>Argon: 返回检测结果
Argon->>Argon: 解析结果和置信度
alt 高置信度垃圾评论
Argon->>DB: 移入回收站
Argon->>WP: 发送通知邮件
else 中等置信度
Argon->>DB: 标记待人工审核
else 正常评论
Argon->>DB: 标记为正常
end
Argon->>DB: 保存检测记录
opt 启用 AI 学习
Argon->>AI: 提取关键词
AI-->>Argon: 返回关键词
Argon->>DB: 更新学习词库
end
else 跳过检测
Argon->>DB: 直接保存评论
end
WP-->>User: 显示提交结果
[/mermaid]
## 数据库结构
[mermaid theme="default" width="85%"]
erDiagram
COMMENT ||--o{ COMMENT_META : has
COMMENT {
bigint comment_ID PK
bigint comment_post_ID FK
text comment_content
varchar comment_author
varchar comment_author_email
varchar comment_author_IP
datetime comment_date
varchar comment_approved
}
COMMENT_META {
bigint meta_id PK
bigint comment_id FK
varchar meta_key
longtext meta_value
}
SPAM_DETECTION ||--|| COMMENT : detects
SPAM_DETECTION {
bigint id PK
bigint comment_id FK
varchar result
float confidence
text reason
text keywords
datetime detected_at
varchar detection_code
}
LEARNED_KEYWORDS ||--o{ SPAM_DETECTION : learns_from
LEARNED_KEYWORDS {
bigint id PK
varchar keyword
int weight
varchar category
datetime created_at
datetime updated_at
}
[/mermaid]
## 系统状态机
[mermaid theme="default" width="70%"]
stateDiagram-v2
[*] --> Submitted: 用户提交
Submitted --> Pending: 需要检测
Submitted --> Approved: 跳过检测
Pending --> Detecting: 开始检测
Detecting --> Analyzed: 检测完成
Analyzed --> Spam: 高置信度垃圾
Analyzed --> Suspicious: 中等置信度
Analyzed --> Clean: 低置信度/正常
Spam --> Trash: 自动处理
Spam --> Hold: 保持待审核
Spam --> Marked: 仅标记
Suspicious --> ManualReview: 等待人工审核
Clean --> Approved: 自动通过
ManualReview --> Approved: 管理员批准
ManualReview --> Trash: 管理员拒绝
Hold --> Approved: 管理员批准
Hold --> Trash: 管理员拒绝
Marked --> Approved: 管理员批准
Marked --> Trash: 管理员拒绝
Trash --> [*]
Approved --> [*]
note right of Detecting
调用 AI API
解析返回结果
计算置信度
end note
note right of ManualReview
显示在后台
等待管理员操作
end note
[/mermaid]
## 性能优化策略
[mermaid theme="default" width="95%"]
flowchart TD
subgraph Input[输入优化]
I1[关键字预过滤]
I2[白名单用户]
I3[智能抽样]
end
subgraph Process[处理优化]
P1[异步检测]
P2[批量处理]
P3[缓存结果]
end
subgraph API[API 优化]
A1[Prompt 优化]
A2[Token 控制]
A3[超时处理]
end
subgraph Storage[存储优化]
S1[索引优化]
S2[定期清理]
S3[归档历史]
end
Input --> Process
Process --> API
API --> Storage
style Input fill:#e8f5e9
style Process fill:#e3f2fd
style API fill:#fff3e0
style Storage fill:#f3e5f5
[/mermaid]
## 配置建议矩阵
[mermaid theme="default" width="100%"]
graph TB
subgraph Small[小型博客 < 100评论/天]
S1[检测模式: keyword]
S2[Prompt: 标准模式]
S3[置信度阈值: > 0.9]
S4[自动处理: trash]
end
subgraph Medium[中型博客 100-500评论/天]
M1[检测模式: sample 30%]
M2[Prompt: 标准模式]
M3[置信度阈值: > 0.85]
M4[自动处理: hold]
end
subgraph Large[大型博客 > 500评论/天]
L1[检测模式: sample 40%]
L2[Prompt: 极简模式]
L3[置信度阈值: > 0.8]
L4[自动处理: mark]
L5[定期批量扫描]
end
style Small fill:#c8e6c9
style Medium fill:#fff9c4
style Large fill:#ffccbc
[/mermaid]
## 错误处理流程
[mermaid theme="default" width="85%"]
flowchart TD
Start([检测开始]) --> CallAPI[调用 AI API]
CallAPI --> CheckResponse{响应状态?}
CheckResponse -->|成功| ParseJSON[解析 JSON]
CheckResponse -->|超时| Timeout[超时处理]
CheckResponse -->|错误| APIError[API 错误]
ParseJSON --> ValidateData{数据有效?}
ValidateData -->|是| ProcessResult[处理结果]
ValidateData -->|否| DataError[数据错误]
Timeout --> RetryCheck{重试次数?}
RetryCheck -->|< 3| Retry[重试请求]
RetryCheck -->|>= 3| FallbackPending[降级:待审核]
Retry --> CallAPI
APIError --> LogError[记录错误日志]
DataError --> LogError
LogError --> NotifyAdmin[通知管理员]
NotifyAdmin --> FallbackPending
ProcessResult --> SaveResult[保存结果]
FallbackPending --> SaveResult
SaveResult --> End([结束])
style Timeout fill:#ffccbc
style APIError fill:#ffccbc
style DataError fill:#ffccbc
style FallbackPending fill:#fff9c4
style ProcessResult fill:#c8e6c9
[/mermaid]
## 总结
通过以上流程图,我们可以清晰地看到:
1. **主流程**:从用户提交到最终处理的完整流程
2. **检测模式**:四种模式的特点和适用场景
3. **时序交互**:各组件之间的交互顺序
4. **数据结构**:数据库表关系和字段定义
5. **状态机**:评论的各种状态转换
6. **性能优化**:多层次的优化策略
7. **配置建议**:不同规模博客的推荐配置
8. **错误处理**:完善的异常处理机制
这个系统设计充分考虑了:
- ✅ 准确性:多级置信度判断
- ✅ 性能:异步处理、智能抽样
- ✅ 成本:灵活的检测模式
- ✅ 可靠性:完善的错误处理
- ✅ 可维护性:清晰的架构设计

View File

@@ -1,197 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>单元素容器语法测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.test-case {
background: white;
padding: 20px;
margin: 20px 0;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h2 {
color: #333;
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
}
.success {
color: green;
font-weight: bold;
}
.error {
color: red;
font-weight: bold;
}
pre {
background: #f4f4f4;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
white-space: pre-wrap;
}
</style>
</head>
<body>
<h1>单元素容器语法测试</h1>
<p>测试 WP-Markdown 将整个容器语法放在一个 &lt;p&gt; 元素中的情况</p>
<!-- 测试用例 1: 简单流程图(单元素) -->
<div class="test-case">
<h2>测试 1: 简单流程图(单元素,带全角箭头)</h2>
<p>::: mermaid<br>flowchart TD<br> Start([开始]) > Process[处理]<br> Process > End([结束])<br>:::</p>
<div id="result1"></div>
</div>
<!-- 测试用例 2: 带样式的流程图(单元素) -->
<div class="test-case">
<h2>测试 2: 带样式定义(单元素)</h2>
<p>::: mermaid<br>flowchart LR<br> A[开始] > B[处理]<br> B > C[结束]<br> style A fill:#e1f5e1,stroke:#2e7d32<br> style C fill:#ffe1e1,stroke:#c62828<br>:::</p>
<div id="result2"></div>
</div>
<!-- 测试用例 3: 复杂流程图(单元素,模拟 AI 评论审核) -->
<div class="test-case">
<h2>测试 3: 复杂流程图(单元素,多个节点)</h2>
<p>::: mermaid<br>flowchart TD<br> Start([用户提交评论]) > PreProcess[预处理]<br> PreProcess > CheckEnabled{启用 AI 检测?}<br> CheckEnabled >|否| SaveComment[保存评论]<br> CheckEnabled >|是| CheckMode{检测模式?}<br> CheckMode >|manual| SaveComment<br> CheckMode >|keyword/sample/all| AICheck[AI 检测]<br> AICheck > CheckResult{检测结果?}<br> CheckResult >|垃圾评论| Trash[移入回收站]<br> CheckResult >|正常评论| SaveComment<br> style Start fill:#e1f5e1,stroke:#2e7d32<br> style Trash fill:#ff6b6b,stroke:#c62828<br>:::</p>
<div id="result3"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
<script>
// 模拟 Argon 主题的 htmlToText 函数(增强版)
function htmlToText(html) {
// 将 <br> 和 <br/> 转换为换行符
let text = html.replace(/<br\s*\/?>/gi, '\n');
// 移除其他 HTML 标签
text = text.replace(/<[^>]+>/g, '');
// 解码 HTML 实体
text = text
.replace(/&nbsp;/g, ' ')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&amp;/g, '&')
.replace(/&quot;/g, '"')
.replace(/&#039;/g, "'")
.replace(/&#8211;/g, '-') // EN DASH
.replace(/&#8212;/g, '--') // EM DASH
.replace(/&#8594;/g, '->') // RIGHTWARDS ARROW
.replace(/&ndash;/g, '-') // EN DASH (named entity)
.replace(/&mdash;/g, '--') // EM DASH (named entity)
.replace(/&rarr;/g, '->'); // RIGHTWARDS ARROW (named entity)
// 转换 Unicode 字符WordPress 可能直接输出 Unicode
text = text
.replace(//g, '-') // U+2013 EN DASH
.replace(/—/g, '--') // U+2014 EM DASH
.replace(/→/g, '->'); // U+2192 RIGHTWARDS ARROW
return text;
}
// 提取单元素容器语法内容
function extractSingleElementContainer(element) {
let html = element.innerHTML;
let text = element.textContent.trim();
console.log('[测试] 原始 HTML:', html.substring(0, 200));
console.log('[测试] 原始文本:', text.substring(0, 200));
if (!text.startsWith('::: mermaid')) {
return null;
}
// 检查是否整个内容都在一个元素中
if (text.includes(':::') && text.lastIndexOf(':::') > 10) {
console.log('[测试] 检测到单元素容器语法');
// 使用 htmlToText 转换
let fullText = htmlToText(html);
console.log('[测试] 转换后文本:', fullText.substring(0, 200));
// 移除开始和结束标记
fullText = fullText.replace(/^:::\s*mermaid\s*/i, '').trim();
fullText = fullText.replace(/:::\s*$/, '').trim();
console.log('[测试] 最终代码:', fullText);
return fullText;
}
return null;
}
// 测试函数
function testMermaid(testId, resultId) {
const element = document.querySelector(`#${testId}`).previousElementSibling;
const resultDiv = document.getElementById(resultId);
try {
const code = extractSingleElementContainer(element);
if (!code) {
resultDiv.innerHTML = '<p class="error">❌ 提取失败:未找到代码</p>';
return;
}
// 检查换行符
const lines = code.split('\n');
console.log(`[测试 ${testId}] 提取到 ${lines.length} 行代码`);
// 检查箭头符号
const hasCorrectArrows = code.includes('-->') || code.includes('->');
const hasWrongArrows = code.includes('>') || code.includes('→');
// 渲染图表
mermaid.render(`mermaid-${testId}`, code).then(result => {
resultDiv.innerHTML = `
<p class="success">✅ 渲染成功</p>
<p>提取到 ${lines.length} 行代码</p>
<p>箭头符号: ${hasCorrectArrows ? '✅ 正确 (-->)' : '❌ 错误'}</p>
<p>全角箭头: ${hasWrongArrows ? '❌ 存在 (>)' : '✅ 不存在'}</p>
<details>
<summary>查看提取的代码</summary>
<pre>${code}</pre>
</details>
${result.svg}
`;
}).catch(error => {
resultDiv.innerHTML = `
<p class="error">❌ 渲染失败: ${error.message}</p>
<p>箭头符号: ${hasCorrectArrows ? '✅ 正确 (-->)' : '❌ 错误'}</p>
<p>全角箭头: ${hasWrongArrows ? '❌ 存在 (>)' : '✅ 不存在'}</p>
<details>
<summary>查看提取的代码</summary>
<pre>${code}</pre>
</details>
`;
});
} catch (error) {
resultDiv.innerHTML = `<p class="error">❌ 错误: ${error.message}</p>`;
}
}
// 初始化
document.addEventListener('DOMContentLoaded', function() {
mermaid.initialize({
startOnLoad: false,
theme: 'default',
securityLevel: 'loose'
});
// 运行测试
testMermaid('test1', 'result1');
testMermaid('test2', 'result2');
testMermaid('test3', 'result3');
});
</script>
</body>
</html>

View File

@@ -1,214 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WP-Markdown Mermaid 格式测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.test-case {
background: white;
padding: 20px;
margin: 20px 0;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h2 {
color: #333;
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
}
.mermaid {
background: #f9f9f9;
padding: 15px;
border: 1px solid #ddd;
border-radius: 4px;
margin: 10px 0;
}
pre {
background: #f4f4f4;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
}
</style>
</head>
<body>
<h1>WP-Markdown Mermaid 格式测试</h1>
<p>本页面测试 WP-Markdown 插件生成的各种 Mermaid 代码块格式</p>
<!-- 测试用例 1: 标准 document.write 格式(单行) -->
<div class="test-case">
<h2>测试 1: 标准 document.write 格式(单行)</h2>
<div class="mermaid">
<script>document.write("flowchart TD\nA[开始] --> B[处理]\nB --> C[结束]")</script>
</div>
</div>
<!-- 测试用例 2: document.write 格式(多行,带转义) -->
<div class="test-case">
<h2>测试 2: document.write 格式(多行,带转义)</h2>
<div class="mermaid">
<script>document.write("graph LR\n A[\"用户输入\"] --> B[\"数据验证\"]\n B --> C{\"是否有效?\"}\n C -->|是| D[\"保存数据\"]\n C -->|否| E[\"显示错误\"]")</script>
</div>
</div>
<!-- 测试用例 3: document.write 格式(包含 HTML 实体) -->
<div class="test-case">
<h2>测试 3: document.write 格式(包含 HTML 实体)</h2>
<div class="mermaid">
<script>document.write("sequenceDiagram\n Alice-&gt;&gt;Bob: Hello Bob!\n Bob--&gt;&gt;Alice: Hi Alice!")</script>
</div>
</div>
<!-- 测试用例 4: 纯文本格式(无 script 标签) -->
<div class="test-case">
<h2>测试 4: 纯文本格式(无 script 标签)</h2>
<div class="mermaid">
flowchart TD
Start --> Stop
</div>
</div>
<!-- 测试用例 5: pre code 格式 -->
<div class="test-case">
<h2>测试 5: pre code 格式</h2>
<pre><code class="language-mermaid">graph TD
A --> B
B --> C</code></pre>
</div>
<!-- 测试用例 6: 复杂图表(类图) -->
<div class="test-case">
<h2>测试 6: 复杂图表(类图)</h2>
<div class="mermaid">
<script>document.write("classDiagram\n class Animal {\n +String name\n +int age\n +makeSound()\n }\n class Dog {\n +String breed\n +bark()\n }\n Animal <|-- Dog")</script>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
<script>
// 模拟 Argon 主题的 Mermaid 初始化逻辑
console.log('[测试] 开始初始化 Mermaid');
// 检测所有 Mermaid 代码块
function detectMermaidBlocks() {
const blocks = [];
const selectors = [
'div.mermaid',
'pre code.language-mermaid',
'pre[data-lang="mermaid"]',
'code.mermaid'
];
selectors.forEach(selector => {
const elements = document.querySelectorAll(selector);
elements.forEach(element => {
if (!blocks.includes(element)) {
blocks.push(element);
}
});
});
console.log(`[测试] 检测到 ${blocks.length} 个 Mermaid 代码块`);
return blocks;
}
// 提取代码块内容(使用改进后的正则)
function extractMermaidCode(element) {
let code = '';
if (element.tagName === 'DIV' && element.classList.contains('mermaid')) {
const scriptTag = element.querySelector('script');
if (scriptTag) {
const scriptContent = scriptTag.textContent || scriptTag.innerText;
console.log('[测试] 原始 script 内容:', scriptContent.substring(0, 100));
// 使用改进后的正则:[\s\S]*? 匹配包括换行在内的所有字符
let match = scriptContent.match(/document\.write\s*\(\s*["']([\s\S]*?)["']\s*\)/);
if (match && match[1]) {
code = match[1];
console.log('[测试] 从 document.write() 提取到代码,长度:', code.length);
} else {
// 降级方案:直接提取引号内容
match = scriptContent.match(/["']([\s\S]*?)["']/);
if (match && match[1]) {
code = match[1];
console.log('[测试] 从引号内提取到代码,长度:', code.length);
} else {
const clonedElement = element.cloneNode(true);
const scripts = clonedElement.querySelectorAll('script');
scripts.forEach(script => script.remove());
code = clonedElement.textContent;
console.log('[测试] 使用降级方案提取代码');
}
}
} else {
code = element.textContent;
console.log('[测试] 从纯文本提取代码');
}
} else if (element.tagName === 'CODE') {
code = element.textContent;
} else if (element.tagName === 'PRE') {
const codeElement = element.querySelector('code');
code = codeElement ? codeElement.textContent : element.textContent;
}
// 解码 HTML 实体
code = code
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&amp;/g, '&')
.replace(/&quot;/g, '"')
.replace(/&#039;/g, "'");
// 解码转义字符
code = code
.replace(/\\n/g, '\n')
.replace(/\\t/g, '\t')
.replace(/\\r/g, '\r')
.replace(/\\"/g, '"')
.replace(/\\'/g, "'")
.replace(/\\\\/g, '\\');
console.log('[测试] 最终提取的代码:', code.substring(0, 100) + (code.length > 100 ? '...' : ''));
return code.trim();
}
// 初始化并渲染
document.addEventListener('DOMContentLoaded', function() {
mermaid.initialize({
startOnLoad: false,
theme: 'default',
securityLevel: 'loose'
});
const blocks = detectMermaidBlocks();
blocks.forEach((block, index) => {
const code = extractMermaidCode(block);
const chartId = `mermaid-chart-${index}`;
console.log(`[测试] 渲染图表 ${index}:`, code);
mermaid.render(chartId, code).then(result => {
const container = document.createElement('div');
container.innerHTML = result.svg;
block.parentNode.replaceChild(container, block);
console.log(`[测试] 图表 ${index} 渲染成功`);
}).catch(error => {
console.error(`[测试] 图表 ${index} 渲染失败:`, error);
block.style.border = '2px solid red';
block.innerHTML = `<strong style="color: red;">渲染失败: ${error.message}</strong><br><pre>${code}</pre>`;
});
});
});
</script>
</body>
</html>