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