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. 添加单元测试
This commit is contained in:
@@ -4520,20 +4520,31 @@ void 0;
|
||||
processedElements.add(startElement);
|
||||
|
||||
// 处理开始元素
|
||||
// 使用 innerHTML 来获取原始内容,包括 <br> 标签
|
||||
let startHTML = startElement.innerHTML;
|
||||
let startText = startElement.textContent.trim();
|
||||
|
||||
if (startText.startsWith('::: mermaid')) {
|
||||
foundStart = true;
|
||||
this.logDebug('找到容器语法开始标记,原始 HTML: ' + startHTML.substring(0, 100));
|
||||
|
||||
// 移除开始标记,保留同一元素中的其他内容
|
||||
startText = startText.replace(/^:::\s*mermaid\s*/i, '').trim();
|
||||
if (startText && !startText.startsWith(':::')) {
|
||||
codeLines.push(startText);
|
||||
// 如果开始标记后有内容,需要从 HTML 中提取
|
||||
// 将 <br> 转换为换行符
|
||||
let contentHTML = startHTML.replace(/^:::\s*mermaid\s*<br\s*\/?>/i, '');
|
||||
let contentText = this.htmlToText(contentHTML);
|
||||
if (contentText.trim()) {
|
||||
codeLines.push(contentText);
|
||||
}
|
||||
}
|
||||
// 检查是否在同一元素中就有结束标记
|
||||
if (startText.endsWith(':::')) {
|
||||
foundEnd = true;
|
||||
// 移除结束标记
|
||||
if (codeLines.length > 0) {
|
||||
let lastLine = codeLines[codeLines.length - 1];
|
||||
if (lastLine) {
|
||||
codeLines[codeLines.length - 1] = lastLine.replace(/:::\s*$/, '').trim();
|
||||
}
|
||||
}
|
||||
@@ -4554,15 +4565,19 @@ void 0;
|
||||
if (text !== ':::') {
|
||||
text = text.replace(/:::\s*$/, '').trim();
|
||||
if (text) {
|
||||
codeLines.push(text);
|
||||
// 将 HTML 转换为文本,保留换行符
|
||||
let contentText = this.htmlToText(currentElement.innerHTML);
|
||||
codeLines.push(contentText);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 添加当前元素的内容
|
||||
if (text) {
|
||||
codeLines.push(text);
|
||||
// 将 HTML 转换为文本,保留换行符
|
||||
let contentText = this.htmlToText(currentElement.innerHTML);
|
||||
if (contentText.trim()) {
|
||||
codeLines.push(contentText);
|
||||
} else {
|
||||
// 空元素,添加空行
|
||||
codeLines.push('');
|
||||
@@ -4581,6 +4596,8 @@ void 0;
|
||||
// 合并所有行
|
||||
let code = codeLines.join('\n').trim();
|
||||
|
||||
this.logDebug('提取的完整代码: ' + code.substring(0, 200));
|
||||
|
||||
if (!code) {
|
||||
return null;
|
||||
}
|
||||
@@ -4597,6 +4614,27 @@ void 0;
|
||||
return container;
|
||||
},
|
||||
|
||||
/**
|
||||
* 将 HTML 转换为纯文本,保留换行符
|
||||
* @param {string} html - HTML 字符串
|
||||
* @returns {string} 纯文本
|
||||
*/
|
||||
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;
|
||||
},
|
||||
|
||||
/**
|
||||
* 提取代码块内容
|
||||
* @param {HTMLElement} element - 代码块元素
|
||||
|
||||
129
commit-msg-mermaid-newline-fix.txt
Normal file
129
commit-msg-mermaid-newline-fix.txt
Normal file
@@ -0,0 +1,129 @@
|
||||
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. 添加单元测试
|
||||
Reference in New Issue
Block a user