diff --git a/.kiro/specs/mermaid-codeblock-magic/design.md b/.kiro/specs/mermaid-codeblock-magic/design.md index 8029b94..dc9fade 100644 --- a/.kiro/specs/mermaid-codeblock-magic/design.md +++ b/.kiro/specs/mermaid-codeblock-magic/design.md @@ -1,821 +1,790 @@ -# Mermaid 代码块魔改支持 - 设计文档 +# Design Document: Mermaid 代码块渲染修复 -## 1. 设计概述 +## Overview -### 1.1 设计目标 -实现标准 Markdown 代码块 (` ```mermaid `) 的 Mermaid 图表渲染,通过在代码高亮之前拦截并转换代码块,完全绕过 WordPress 和代码高亮的干扰。 +本设计旨在修复 Argon WordPress 主题中 Mermaid 图表渲染的关键问题。当前实现存在以下核心问题: -### 1.2 核心设计理念 -**提前拦截,转换容器**:在代码高亮处理之前,将 `
` 转换为 ``,使其不被代码高亮处理,同时能被 Mermaid 渲染系统识别。
+1. **PJAX 加载后显示原始文本**:页面切换后 Mermaid 不渲染
+2. **语法解析错误**:flowchart、erDiagram 等语法解析失败
+3. **功能缺失**:缺少导出、全屏查看等功能
+4. **交互体验差**:工具栏遮挡内容、缩放不流畅
-### 1.3 设计参考
-参考主题中数学公式的实现方式:
-- 数学公式使用特殊分隔符(`$...$`),不会被代码高亮处理
-- Mermaid 代码块通过提前转换,达到类似效果
-- 两者都在 PJAX 加载后重新处理
+设计方案采用模块化架构,确保代码质量和可维护性。
-### 1.4 技术栈
-- **JavaScript**:原生 JavaScript + jQuery(主题现有技术栈)
-- **Mermaid.js**:主题已集成的图表渲染库
-- **执行时机**:代码高亮之前(`highlightJsRender()` 函数开始处)
+## Architecture
-## 2. 架构设计
+### 核心架构原则
-### 2.1 整体流程
+1. **模块化设计**:将功能拆分为独立模块,易于维护和扩展
+2. **错误隔离**:每个模块独立的错误处理,互不影响
+3. **性能优先**:使用批量渲染、延迟加载等优化技术
+4. **用户体验**:提供流畅的交互和友好的错误提示
-```mermaid
-flowchart TD
- A[页面加载/PJAX切换] --> B[highlightJsRender 调用]
- B --> C[convertMermaidCodeblocks 执行]
- C --> D[查找 mermaid 代码块]
- D --> E{找到代码块?}
- E -->|是| F[提取纯文本代码]
- E -->|否| G[继续代码高亮]
- F --> H[创建 mermaid-from-codeblock 容器]
- H --> I[替换原始代码块]
- I --> J[标记已处理]
- J --> G
- G --> K[代码高亮处理其他代码块]
- K --> L[detectMermaidBlocks 检测]
- L --> M[提取 Mermaid 代码]
- M --> N[mermaid.init 渲染]
-```
-
-### 2.2 模块设计
-
-#### 模块 1:代码块转换器 (convertMermaidCodeblocks)
-**职责**:在代码高亮前拦截并转换 mermaid 代码块
-
-**输入**:DOM 树(包含未处理的代码块)
-**输出**:转换后的 DOM 树(mermaid 代码块已替换为容器)
-
-**核心逻辑**:
-1. 使用多个选择器查找代码块
-2. 提取纯文本代码
-3. 创建新容器
-4. 替换原始元素
-
-#### 模块 2:代码提取器 (extractMermaidCode)
-**职责**:从不同格式的容器中提取 Mermaid 代码
-
-**输入**:DOM 元素(可能是 div、pre、code)
-**输出**:纯文本 Mermaid 代码
-
-**支持格式**:
-- ``(新增)
-- ``
-- ``
-- ``(降级)
-
-#### 模块 3:Mermaid 检测器 (detectMermaidBlocks)
-**职责**:检测页面中所有需要渲染的 Mermaid 容器
-
-**输入**:DOM 树
-**输出**:需要渲染的元素列表
-
-**检测优先级**:
-1. `div.mermaid-shortcode`(Shortcode 格式)
-2. `div.mermaid-from-codeblock`(代码块魔改格式)
-3. `div.mermaid`(标准格式)
-4. `pre code.language-mermaid`(降级格式)
-
-### 2.3 数据流设计
+### 架构图
```
-原始 HTML:
-
-flowchart TD
- A --> B
-
-
-↓ convertMermaidCodeblocks()
-
-转换后 HTML:
-
-flowchart TD
- A --> B
-
-
-↓ detectMermaidBlocks()
-
-检测到的代码:
-"flowchart TD\n A --> B"
-
-↓ mermaid.init()
-
-渲染后 HTML:
-
-
-
+Mermaid 渲染系统
+├── 检测模块 (MermaidDetector)
+│ ├── 扫描代码块
+│ ├── 识别 Mermaid 语法
+│ └── 过滤已渲染的块
+├── 渲染模块 (MermaidRenderer)
+│ ├── 初始化 Mermaid
+│ ├── 批量渲染
+│ ├── 错误处理
+│ └── 降级方案
+├── 交互模块 (MermaidInteraction)
+│ ├── 工具栏管理
+│ ├── 缩放控制
+│ ├── 拖拽控制
+│ └── 全屏查看
+├── 导出模块 (MermaidExporter)
+│ ├── PNG 导出
+│ ├── SVG 导出
+│ └── 错误处理
+└── 生命周期模块 (MermaidLifecycle)
+ ├── PJAX 集成
+ ├── 资源清理
+ └── 主题同步
```
-## 3. 详细设计
+## Components and Interfaces
-### 3.1 代码块转换函数
+### 1. Mermaid Detector (检测模块)
-#### 函数签名
-```javascript
-function convertMermaidCodeblocks()
-```
+**职责**:检测页面中的 Mermaid 代码块
-#### 实现位置
-`argontheme.js` 第 3942 行之前(`highlightJsRender()` 函数开始处)
-
-#### 选择器设计
-
-```javascript
-const selectors = [
- 'pre > code.language-mermaid', // 标准 Markdown 格式(最常见)
- 'pre > code.mermaid', // 简化格式
- 'code.language-mermaid', // 无 pre 包裹
- 'pre[data-lang="mermaid"]' // 自定义属性格式
-];
-```
-
-**设计理由**:
-- 支持多种插件生成的 HTML 结构
-- 优先匹配最常见的格式
-- 提供降级支持
-
-#### 重复处理防护
-```javascript
-if (element.dataset.mermaidProcessed) {
- return; // 跳过已处理的元素
-}
-```
-
-**设计理由**:
-- 避免 PJAX 切换时重复处理
-- 防止多次调用导致的错误
-- 使用 data 属性标记状态
-
-#### 代码提取逻辑
-```javascript
-let code = element.textContent.trim();
-```
-
-**设计理由**:
-- `textContent` 获取纯文本,避免 HTML 实体
-- `trim()` 移除前后空白
-- 不进行任何字符转换,保持原始内容
-
-#### 容器创建逻辑
-```javascript
-const container = document.createElement('div');
-container.className = 'mermaid-from-codeblock';
-container.textContent = code;
-container.dataset.processed = 'true';
-```
-
-**设计理由**:
-- 使用 `textContent` 而非 `innerHTML`,避免 XSS
-- 添加特定类名,便于识别来源
-- 标记已处理状态
-
-#### 元素替换逻辑
-```javascript
-const targetElement = element.closest('pre') || element;
-targetElement.parentNode.replaceChild(container, targetElement);
-```
-
-**设计理由**:
-- 优先替换整个 `` 元素
-- 如果没有 `` 包裹,替换 `` 元素
-- 保留原始位置和上下文
-
-### 3.2 集成点设计
-
-#### 集成点 1:highlightJsRender() 函数
-**位置**:`argontheme.js` 第 3942 行
-
-**修改前**:
-```javascript
-function highlightJsRender(){
- if (typeof(hljs) == "undefined"){
- return;
- }
- // ... 代码高亮逻辑
-}
-```
-
-**修改后**:
-```javascript
-function highlightJsRender(){
- // 在代码高亮之前,先处理 Mermaid 代码块
- convertMermaidCodeblocks();
-
- if (typeof(hljs) == "undefined"){
- return;
- }
- // ... 代码高亮逻辑
-}
-```
-
-**设计理由**:
-- 在代码高亮之前执行,确保 mermaid 代码块不被处理
-- 不影响其他代码块的高亮
-- 执行顺序:转换 → 高亮 → 渲染
-
-#### 集成点 2:detectMermaidBlocks() 函数
-**位置**:`argontheme.js` 第 4430 行
-
-**修改前**:
-```javascript
-const selectors = [
- 'div.mermaid-shortcode',
- 'div.mermaid',
- 'pre code.language-mermaid',
- // ...
-];
-```
-
-**修改后**:
-```javascript
-const selectors = [
- 'div.mermaid-shortcode', // Shortcode 格式
- 'div.mermaid-from-codeblock', // 代码块魔改格式(新增)
- 'div.mermaid', // 标准格式
- 'pre code.language-mermaid', // Markdown 格式(降级)
- 'pre[data-lang="mermaid"]', // 自定义属性格式
- 'code.mermaid' // 简化格式
-];
-```
-
-**设计理由**:
-- 添加新的容器类型到检测列表
-- 优先级高于标准 `mermaid` 类
-- 保留降级选择器,确保兼容性
-
-#### 集成点 3:extractMermaidCode() 函数
-**位置**:`argontheme.js` 第 4650 行
-
-**修改前**:
-```javascript
-// 处理 Shortcode 格式
-if (element.classList.contains('mermaid-shortcode')) {
- code = element.textContent;
- this.logDebug('从 Shortcode 格式提取代码');
-}
-```
-
-**修改后**:
-```javascript
-// 处理 Shortcode 格式
-if (element.classList.contains('mermaid-shortcode')) {
- code = element.textContent;
- this.logDebug('从 Shortcode 格式提取代码');
-}
-// 处理代码块魔改格式
-else if (element.classList.contains('mermaid-from-codeblock')) {
- code = element.textContent;
- this.logDebug('从代码块魔改格式提取代码');
-}
-```
-
-**设计理由**:
-- 与 Shortcode 格式使用相同的提取方式
-- 添加调试日志,便于追踪
-- 保持代码一致性
-
-### 3.3 PJAX 兼容设计
-
-#### 执行时机
-PJAX 加载完成后的回调链(`argontheme.js` 第 2862-2890 行):
-```javascript
-$(document).on('pjax:complete', function() {
- // ... 其他初始化
- try { highlightJsRender(); } catch (err) { ... } // 包含代码块转换
- // ... 其他初始化
-});
-```
-
-**设计理由**:
-- `highlightJsRender()` 已在 PJAX 回调中调用
-- 代码块转换自动在每次 PJAX 切换后执行
-- 无需额外修改 PJAX 逻辑
-
-#### 重复处理防护
-使用 `data-processed` 属性标记已处理的元素:
-```javascript
-if (element.dataset.mermaidProcessed) {
- return;
-}
-// ... 处理逻辑
-element.dataset.mermaidProcessed = 'true';
-```
-
-**设计理由**:
-- 避免同一元素被多次转换
-- 支持 PJAX 页面切换
-- 轻量级标记,不影响性能
-
-### 3.4 错误处理设计
-
-#### 空代码检查
-```javascript
-let code = element.textContent.trim();
-if (!code) {
- return; // 跳过空代码块
-}
-```
-
-**设计理由**:
-- 避免创建空容器
-- 减少不必要的 DOM 操作
-- 提高性能
-
-#### Try-Catch 包裹
-```javascript
-try {
- convertMermaidCodeblocks();
-} catch (err) {
- console.error('Mermaid 代码块转换失败:', err);
-}
-```
-
-**设计理由**:
-- 捕获异常,不中断其他代码块的处理
-- 记录错误日志,便于调试
-- 提供降级方案(代码块仍可通过降级选择器检测)
-
-#### 降级支持
-如果代码块转换失败,仍可通过降级选择器检测:
-```javascript
-'pre code.language-mermaid' // 降级选择器
-```
-
-**设计理由**:
-- 确保即使转换失败,仍能渲染
-- 提供多层保障
-- 增强系统健壮性
-
-## 4. 接口设计
-
-### 4.1 公共函数
-
-#### convertMermaidCodeblocks()
+**接口**:
```javascript
/**
- * 在代码高亮之前转换 Mermaid 代码块
- * 将 转换为
- *
+ * 检测页面中的 Mermaid 代码块
+ * @returns {HTMLElement[]} Mermaid 代码块数组
+ */
+function detectMermaidBlocks() {
+ const blocks = [];
+
+ // 检测
+ document.querySelectorAll('pre code.language-mermaid').forEach(code => {
+ if (!isRendered(code)) {
+ blocks.push(code);
+ }
+ });
+
+ // 检测
+ document.querySelectorAll('pre code.mermaid').forEach(code => {
+ if (!isRendered(code)) {
+ blocks.push(code);
+ }
+ });
+
+ return blocks;
+}
+
+/**
+ * 检查代码块是否已渲染
+ * @param {HTMLElement} element - 代码块元素
+ * @returns {boolean} 是否已渲染
+ */
+function isRendered(element) {
+ return element.hasAttribute('data-mermaid-rendered') ||
+ element.closest('.mermaid-container') !== null;
+}
+```
+
+
+### 2. Mermaid Renderer (渲染模块)
+
+**职责**:渲染 Mermaid 图表
+
+**接口**:
+```javascript
+/**
+ * 渲染所有 Mermaid 图表
+ * @returns {Promise}
+ */
+async function renderAllMermaidCharts() {
+ const blocks = detectMermaidBlocks();
+ if (blocks.length === 0) return;
+
+ // 等待 Mermaid 库加载
+ if (!await waitForMermaid()) {
+ ArgonDebug.error('[Argon Mermaid] Mermaid 库加载失败');
+ return;
+ }
+
+ // 初始化 Mermaid
+ if (!initMermaid()) {
+ ArgonDebug.error('[Argon Mermaid] Mermaid 初始化失败');
+ return;
+ }
+
+ // 批量渲染
+ for (let i = 0; i < blocks.length; i++) {
+ await renderMermaidChart(blocks[i], i);
+ }
+}
+
+/**
+ * 等待 Mermaid 库加载
+ * @param {number} timeout - 超时时间(毫秒)
+ * @returns {Promise} 是否加载成功
+ */
+function waitForMermaid(timeout = 5000) {
+ return new Promise((resolve) => {
+ if (typeof window.mermaid !== 'undefined') {
+ resolve(true);
+ return;
+ }
+
+ const startTime = Date.now();
+ const checkInterval = setInterval(() => {
+ if (typeof window.mermaid !== 'undefined') {
+ clearInterval(checkInterval);
+ resolve(true);
+ } else if (Date.now() - startTime > timeout) {
+ clearInterval(checkInterval);
+ resolve(false);
+ }
+ }, 100);
+ });
+}
+
+/**
+ * 初始化 Mermaid
+ * @returns {boolean} 是否初始化成功
+ */
+function initMermaid() {
+ if (typeof window.mermaid === 'undefined') {
+ return false;
+ }
+
+ try {
+ const theme = getMermaidTheme();
+ window.mermaid.initialize({
+ startOnLoad: false,
+ theme: theme,
+ securityLevel: 'loose',
+ flowchart: {
+ useMaxWidth: true,
+ htmlLabels: true
+ }
+ });
+ return true;
+ } catch (e) {
+ ArgonDebug.error('[Argon Mermaid] 初始化失败:', e);
+ return false;
+ }
+}
+
+/**
+ * 渲染单个图表
+ * @param {HTMLElement} element - 代码块元素
+ * @param {number} index - 图表索引
+ * @returns {Promise}
+ */
+async function renderMermaidChart(element, index) {
+ const chartId = `mermaid-chart-${Date.now()}-${index}`;
+ const code = element.textContent.trim();
+
+ ArgonDebug.log(`[Argon Mermaid] 开始渲染: ${chartId}`);
+
+ try {
+ // 使用 mermaid.render API
+ const result = await window.mermaid.render(`mermaid-svg-${chartId}`, code);
+
+ // 创建容器
+ const container = createMermaidContainer(chartId, result.svg, code);
+
+ // 替换原始代码块
+ element.closest('pre').replaceWith(container);
+
+ // 标记已渲染
+ element.setAttribute('data-mermaid-rendered', 'true');
+
+ ArgonDebug.log(`[Argon Mermaid] 渲染成功: ${chartId}`);
+ } catch (error) {
+ ArgonDebug.error(`[Argon Mermaid] 渲染失败: ${chartId}`, error);
+
+ // 显示错误
+ showMermaidError(element, error, code);
+ }
+}
+```
+
+### 3. Mermaid Interaction (交互模块)
+
+**职责**:管理图表交互功能
+
+**接口**:
+```javascript
+/**
+ * 创建 Mermaid 容器
+ * @param {string} chartId - 图表 ID
+ * @param {string} svg - SVG 代码
+ * @param {string} code - 原始代码
+ * @returns {HTMLElement} 容器元素
+ */
+function createMermaidContainer(chartId, svg, code) {
+ const container = document.createElement('div');
+ container.className = 'mermaid-container';
+ container.setAttribute('data-chart-id', chartId);
+ container.setAttribute('data-original-code', code);
+
+ // 创建图表包装器
+ const wrapper = document.createElement('div');
+ wrapper.className = 'mermaid-wrapper';
+ wrapper.innerHTML = svg;
+
+ // 创建工具栏
+ const toolbar = createMermaidToolbar(chartId);
+
+ container.appendChild(toolbar);
+ container.appendChild(wrapper);
+
+ // 绑定交互事件
+ bindMermaidInteraction(container);
+
+ return container;
+}
+
+/**
+ * 创建工具栏
+ * @param {string} chartId - 图表 ID
+ * @returns {HTMLElement} 工具栏元素
+ */
+function createMermaidToolbar(chartId) {
+ const toolbar = document.createElement('div');
+ toolbar.className = 'mermaid-toolbar';
+ toolbar.innerHTML = `
+
+
+
+
+
+ `;
+ return toolbar;
+}
+
+/**
+ * 绑定交互事件
+ * @param {HTMLElement} container - 容器元素
* @returns {void}
*/
-function convertMermaidCodeblocks() {
- // 实现逻辑
+function bindMermaidInteraction(container) {
+ const wrapper = container.querySelector('.mermaid-wrapper');
+ const svg = wrapper.querySelector('svg');
+
+ let scale = 1;
+ let translateX = 0;
+ let translateY = 0;
+ let isDragging = false;
+ let startX = 0;
+ let startY = 0;
+
+ // 缩放功能
+ container.querySelector('.mermaid-zoom-in').addEventListener('click', () => {
+ scale = Math.min(scale * 1.2, 5);
+ updateTransform();
+ });
+
+ container.querySelector('.mermaid-zoom-out').addEventListener('click', () => {
+ scale = Math.max(scale / 1.2, 0.5);
+ updateTransform();
+ });
+
+ container.querySelector('.mermaid-reset').addEventListener('click', () => {
+ scale = 1;
+ translateX = 0;
+ translateY = 0;
+ updateTransform();
+ });
+
+ // 鼠标滚轮缩放
+ wrapper.addEventListener('wheel', (e) => {
+ e.preventDefault();
+ const delta = e.deltaY > 0 ? 0.9 : 1.1;
+ scale = Math.max(0.5, Math.min(5, scale * delta));
+ updateTransform();
+ }, { passive: false });
+
+ // 拖拽功能
+ wrapper.addEventListener('mousedown', (e) => {
+ if (scale > 1) {
+ isDragging = true;
+ startX = e.clientX - translateX;
+ startY = e.clientY - translateY;
+ wrapper.style.cursor = 'grabbing';
+ }
+ });
+
+ document.addEventListener('mousemove', (e) => {
+ if (isDragging) {
+ translateX = e.clientX - startX;
+ translateY = e.clientY - startY;
+ updateTransform();
+ }
+ });
+
+ document.addEventListener('mouseup', () => {
+ if (isDragging) {
+ isDragging = false;
+ wrapper.style.cursor = scale > 1 ? 'grab' : 'default';
+ }
+ });
+
+ // 更新变换
+ function updateTransform() {
+ svg.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
+ svg.style.transformOrigin = 'center';
+ svg.style.transition = 'transform 0.2s ease';
+ wrapper.style.cursor = scale > 1 ? 'grab' : 'default';
+ }
+
+ // 全屏查看
+ container.querySelector('.mermaid-fullscreen').addEventListener('click', () => {
+ openMermaidFullscreen(container);
+ });
+
+ // 导出功能
+ container.querySelector('.mermaid-export').addEventListener('click', () => {
+ showExportMenu(container);
+ });
}
```
-### 4.2 数据结构
-#### 容器元素结构
-```html
-
- flowchart TD
- A --> B
-
-```
+### 4. Mermaid Exporter (导出模块)
-**属性说明**:
-- `class="mermaid-from-codeblock"`:标识来源于代码块
-- `data-processed="true"`:标记已处理
-- 内容:纯文本 Mermaid 代码
+**职责**:导出图表为图片文件
-### 4.3 选择器优先级
-
-| 优先级 | 选择器 | 用途 |
-|--------|--------|------|
-| 1 | `pre > code.language-mermaid` | 标准 Markdown 格式 |
-| 2 | `pre > code.mermaid` | 简化格式 |
-| 3 | `code.language-mermaid` | 无 pre 包裹 |
-| 4 | `pre[data-lang="mermaid"]` | 自定义属性格式 |
-
-## 5. 性能设计
-
-### 5.1 性能目标
-- 单个代码块处理时间 < 10ms
-- 不影响页面加载速度
-- 不增加额外的 HTTP 请求
-
-### 5.2 性能优化策略
-
-#### 优化 1:使用原生 JavaScript
+**接口**:
```javascript
-document.querySelectorAll(selector) // 而非 $(selector)
-```
+/**
+ * 显示导出菜单
+ * @param {HTMLElement} container - 容器元素
+ * @returns {void}
+ */
+function showExportMenu(container) {
+ const menu = document.createElement('div');
+ menu.className = 'mermaid-export-menu';
+ menu.innerHTML = `
+
+
+ `;
+
+ menu.querySelector('.export-png').addEventListener('click', () => {
+ exportMermaidAsPNG(container);
+ menu.remove();
+ });
+
+ menu.querySelector('.export-svg').addEventListener('click', () => {
+ exportMermaidAsSVG(container);
+ menu.remove();
+ });
+
+ container.appendChild(menu);
+
+ // 点击外部关闭菜单
+ setTimeout(() => {
+ document.addEventListener('click', function closeMenu(e) {
+ if (!menu.contains(e.target)) {
+ menu.remove();
+ document.removeEventListener('click', closeMenu);
+ }
+ });
+ }, 0);
+}
-**理由**:原生方法性能更好,减少 jQuery 开销
+/**
+ * 导出为 PNG
+ * @param {HTMLElement} container - 容器元素
+ * @returns {void}
+ */
+function exportMermaidAsPNG(container) {
+ const svg = container.querySelector('svg');
+ const chartId = container.getAttribute('data-chart-id');
+
+ try {
+ // 创建 canvas
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+
+ // 获取 SVG 尺寸
+ const bbox = svg.getBBox();
+ canvas.width = bbox.width;
+ canvas.height = bbox.height;
+
+ // 将 SVG 转换为图片
+ const svgData = new XMLSerializer().serializeToString(svg);
+ const img = new Image();
+ const blob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
+ const url = URL.createObjectURL(blob);
+
+ img.onload = () => {
+ ctx.drawImage(img, 0, 0);
+ URL.revokeObjectURL(url);
+
+ // 下载
+ canvas.toBlob((blob) => {
+ const link = document.createElement('a');
+ link.download = `${chartId}.png`;
+ link.href = URL.createObjectURL(blob);
+ link.click();
+ URL.revokeObjectURL(link.href);
+ });
+ };
+
+ img.src = url;
+ } catch (error) {
+ ArgonDebug.error('[Argon Mermaid] PNG 导出失败:', error);
+ alert('导出失败,请重试');
+ }
+}
-#### 优化 2:提前返回
-```javascript
-if (element.dataset.mermaidProcessed) {
- return; // 提前返回,避免不必要的处理
+/**
+ * 导出为 SVG
+ * @param {HTMLElement} container - 容器元素
+ * @returns {void}
+ */
+function exportMermaidAsSVG(container) {
+ const svg = container.querySelector('svg');
+ const chartId = container.getAttribute('data-chart-id');
+
+ try {
+ const svgData = new XMLSerializer().serializeToString(svg);
+ const blob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
+ const url = URL.createObjectURL(blob);
+
+ const link = document.createElement('a');
+ link.download = `${chartId}.svg`;
+ link.href = url;
+ link.click();
+
+ URL.revokeObjectURL(url);
+ } catch (error) {
+ ArgonDebug.error('[Argon Mermaid] SVG 导出失败:', error);
+ alert('导出失败,请重试');
+ }
}
```
-**理由**:减少重复处理,提高效率
+### 5. Mermaid Lifecycle (生命周期模块)
-#### 优化 3:批量处理
+**职责**:管理 Mermaid 的生命周期
+
+**接口**:
```javascript
-selectors.forEach(selector => {
- document.querySelectorAll(selector).forEach(element => {
- // 处理逻辑
+/**
+ * 清理 Mermaid 实例
+ * @returns {void}
+ */
+function cleanupMermaidInstances() {
+ // 移除所有容器
+ document.querySelectorAll('.mermaid-container').forEach(container => {
+ // 移除事件监听器
+ const toolbar = container.querySelector('.mermaid-toolbar');
+ if (toolbar) {
+ toolbar.querySelectorAll('button').forEach(btn => {
+ btn.replaceWith(btn.cloneNode(true));
+ });
+ }
+
+ // 移除容器
+ container.remove();
+ });
+
+ ArgonDebug.log('[Argon Mermaid] 实例已清理');
+}
+
+/**
+ * PJAX 集成
+ * @returns {void}
+ */
+function initMermaidPjaxIntegration() {
+ // PJAX 开始前清理
+ $(document).on('pjax:beforeReplace', function() {
+ cleanupMermaidInstances();
+ });
+
+ // PJAX 完成后渲染
+ $(document).on('pjax:complete', function() {
+ renderAllMermaidCharts();
+ });
+}
+
+/**
+ * 主题同步
+ * @returns {void}
+ */
+function syncMermaidTheme() {
+ const isDark = document.documentElement.classList.contains('darkmode');
+ const theme = isDark ? 'dark' : 'default';
+
+ if (typeof window.mermaid !== 'undefined') {
+ window.mermaid.initialize({
+ theme: theme
+ });
+
+ // 重新渲染所有图表
+ renderAllMermaidCharts();
+ }
+}
+
+/**
+ * 获取 Mermaid 主题
+ * @returns {string} 主题名称
+ */
+function getMermaidTheme() {
+ const isDark = document.documentElement.classList.contains('darkmode');
+ return isDark ? 'dark' : 'default';
+}
+```
+
+### 6. Error Handler (错误处理模块)
+
+**职责**:处理渲染错误
+
+**接口**:
+```javascript
+/**
+ * 显示 Mermaid 错误
+ * @param {HTMLElement} element - 代码块元素
+ * @param {Error} error - 错误对象
+ * @param {string} code - 原始代码
+ * @returns {void}
+ */
+function showMermaidError(element, error, code) {
+ const errorContainer = document.createElement('div');
+ errorContainer.className = 'mermaid-error';
+ errorContainer.innerHTML = `
+
+
+ Mermaid 图表渲染失败
+
+
+
+ 查看原始代码
+ ${escapeHtml(code)}
+
+ `;
+
+ element.closest('pre').replaceWith(errorContainer);
+}
+
+/**
+ * 转义 HTML
+ * @param {string} html - HTML 字符串
+ * @returns {string} 转义后的字符串
+ */
+function escapeHtml(html) {
+ const div = document.createElement('div');
+ div.textContent = html;
+ return div.innerHTML;
+}
+```
+
+## Data Models
+
+### MermaidChart (图表数据模型)
+
+```javascript
+/**
+ * Mermaid 图表数据模型
+ */
+class MermaidChart {
+ constructor(id, code, element) {
+ this.id = id; // 图表 ID
+ this.code = code; // 原始代码
+ this.element = element; // DOM 元素
+ this.rendered = false; // 是否已渲染
+ this.scale = 1; // 缩放级别
+ this.translateX = 0; // X 轴偏移
+ this.translateY = 0; // Y 轴偏移
+ this.error = null; // 错误信息
+ }
+
+ /**
+ * 标记为已渲染
+ */
+ markRendered() {
+ this.rendered = true;
+ this.element.setAttribute('data-mermaid-rendered', 'true');
+ }
+
+ /**
+ * 设置错误
+ */
+ setError(error) {
+ this.error = error;
+ }
+
+ /**
+ * 重置变换
+ */
+ resetTransform() {
+ this.scale = 1;
+ this.translateX = 0;
+ this.translateY = 0;
+ }
+}
+```
+
+### MermaidConfig (配置模型)
+
+```javascript
+/**
+ * Mermaid 配置模型
+ */
+class MermaidConfig {
+ constructor() {
+ this.theme = 'default'; // 主题
+ this.startOnLoad = false; // 自动加载
+ this.securityLevel = 'loose'; // 安全级别
+ this.flowchart = {
+ useMaxWidth: true,
+ htmlLabels: true
+ };
+ }
+
+ /**
+ * 从全局配置加载
+ */
+ loadFromGlobal() {
+ this.theme = getMermaidTheme();
+ }
+
+ /**
+ * 转换为 Mermaid 配置对象
+ */
+ toMermaidConfig() {
+ return {
+ theme: this.theme,
+ startOnLoad: this.startOnLoad,
+ securityLevel: this.securityLevel,
+ flowchart: this.flowchart
+ };
+ }
+}
+```
+
+## Error Handling
+
+### 错误处理策略
+
+1. **渲染错误**:显示友好的错误提示,保留原始代码
+2. **库加载失败**:显示加载失败提示,提供重试选项
+3. **导出失败**:显示导出失败提示,记录错误日志
+4. **语法错误**:显示语法错误位置和错误信息
+
+### 错误处理实现
+
+```javascript
+/**
+ * 安全执行函数
+ * @param {Function} fn - 要执行的函数
+ * @param {string} moduleName - 模块名称
+ * @returns {boolean} 是否执行成功
+ */
+function safeExecute(fn, moduleName) {
+ try {
+ fn();
+ ArgonDebug.log(`[Argon Mermaid] ${moduleName} 执行成功`);
+ return true;
+ } catch (error) {
+ ArgonDebug.error(`[Argon Mermaid] ${moduleName} 执行失败:`, error);
+ return false;
+ }
+}
+```
+
+## Performance Optimization
+
+### 优化策略
+
+1. **批量渲染**:使用 requestAnimationFrame 批量渲染图表
+2. **延迟加载**:优先渲染可见图表,延迟渲染视口外的图表
+3. **缓存优化**:缓存已渲染的图表,避免重复渲染
+4. **硬件加速**:使用 CSS transform 实现缩放和拖拽
+
+### 性能优化实现
+
+```javascript
+/**
+ * 批量渲染图表
+ * @param {HTMLElement[]} blocks - 代码块数组
+ * @returns {Promise}
+ */
+async function batchRenderCharts(blocks) {
+ for (let i = 0; i < blocks.length; i++) {
+ await new Promise(resolve => {
+ requestAnimationFrame(async () => {
+ await renderMermaidChart(blocks[i], i);
+ resolve();
+ });
+ });
+ }
+}
+```
+
+## Testing Strategy
+
+### 测试方法
+
+1. **单元测试**:测试各个模块的功能
+2. **集成测试**:测试模块之间的集成
+3. **端到端测试**:测试完整的渲染流程
+4. **性能测试**:测试渲染性能和内存占用
+
+### 测试用例
+
+```javascript
+describe('Mermaid Renderer', () => {
+ it('should detect mermaid blocks', () => {
+ const blocks = detectMermaidBlocks();
+ expect(blocks.length).toBeGreaterThan(0);
+ });
+
+ it('should render mermaid chart', async () => {
+ const block = document.querySelector('code.language-mermaid');
+ await renderMermaidChart(block, 0);
+ expect(block.hasAttribute('data-mermaid-rendered')).toBe(true);
+ });
+
+ it('should handle render error', async () => {
+ const block = createMockBlock('invalid syntax');
+ await renderMermaidChart(block, 0);
+ expect(document.querySelector('.mermaid-error')).not.toBeNull();
});
});
```
-**理由**:一次性查找所有元素,减少 DOM 查询次数
+## Code Quality Standards
-#### 优化 4:最小化 DOM 操作
-```javascript
-const container = document.createElement('div');
-container.className = 'mermaid-from-codeblock';
-container.textContent = code;
-container.dataset.processed = 'true';
-targetElement.parentNode.replaceChild(container, targetElement);
-```
+### 代码规范
-**理由**:一次性创建和替换,减少重排和重绘
+1. **命名规范**:使用驼峰命名法,函数名动词开头
+2. **注释规范**:使用 JSDoc 注释,说明参数和返回值
+3. **错误处理**:所有异步操作都要有错误处理
+4. **性能优化**:避免频繁的 DOM 操作,使用批量处理
-### 5.3 性能监控
+### 代码审查清单
-#### 调试日志
-```javascript
-this.logDebug('处理了 ' + count + ' 个 Mermaid 代码块');
-this.logDebug('代码内容(前100字符): ' + code.substring(0, 100));
-```
-
-**理由**:便于追踪性能问题,不影响生产环境
-
-## 6. 安全设计
-
-### 6.1 XSS 防护
-
-#### 使用 textContent 而非 innerHTML
-```javascript
-container.textContent = code; // 安全
-// container.innerHTML = code; // 不安全
-```
-
-**理由**:`textContent` 会自动转义 HTML,防止 XSS 攻击
-
-#### 代码来源验证
-```javascript
-let code = element.textContent.trim();
-if (!code) {
- return; // 拒绝空代码
-}
-```
-
-**理由**:避免处理恶意或无效的代码块
-
-### 6.2 DOM 操作安全
-
-#### 安全的元素替换
-```javascript
-const targetElement = element.closest('pre') || element;
-if (targetElement.parentNode) {
- targetElement.parentNode.replaceChild(container, targetElement);
-}
-```
-
-**理由**:检查父节点存在,避免 null 引用错误
-
-## 7. 测试设计
-
-### 7.1 单元测试
-
-#### 测试用例 1:标准代码块转换
-```javascript
-// 输入
-
-flowchart TD
- A --> B
-
-
-// 预期输出
-
-flowchart TD
- A --> B
-
-```
-
-#### 测试用例 2:空代码块处理
-```javascript
-// 输入
-
-
-// 预期输出
-
// 不转换
-```
-
-#### 测试用例 3:重复处理防护
-```javascript
-// 第一次处理
-convertMermaidCodeblocks(); // 转换成功
-
-// 第二次处理
-convertMermaidCodeblocks(); // 跳过已处理的元素
-```
-
-#### 测试用例 4:特殊字符保留
-```javascript
-// 输入
-
-A --> B
-C -- text --> D
-
-
-// 预期输出
-代码中的 --> 和 -- 保持不变
-```
-
-### 7.2 集成测试
-
-#### 测试场景 1:与代码高亮集成
-1. 页面包含 mermaid 代码块和其他代码块
-2. 调用 `highlightJsRender()`
-3. 验证 mermaid 代码块被转换,其他代码块被高亮
-
-#### 测试场景 2:与 Mermaid 渲染集成
-1. 页面包含转换后的容器
-2. 调用 `detectMermaidBlocks()`
-3. 验证容器被检测到
-4. 调用 `mermaid.init()`
-5. 验证图表正确渲染
-
-#### 测试场景 3:PJAX 兼容性
-1. 初始页面加载,代码块正确转换和渲染
-2. PJAX 切换到新页面
-3. 验证新页面的代码块正确转换和渲染
-4. 验证已转换的代码块不被重复处理
-
-### 7.3 浏览器测试
-
-#### 测试矩阵
-| 浏览器 | 版本 | 测试项 |
-|--------|------|--------|
-| Chrome | 最新版 | 全部功能 |
-| Firefox | 最新版 | 全部功能 |
-| Safari | 最新版 | 全部功能 |
-| Edge | 最新版 | 全部功能 |
-
-#### 测试项
-- 代码块转换
-- 图表渲染
-- PJAX 切换
-- 性能表现
-
-### 7.4 测试文件
-
-创建 `tests/test-codeblock-magic.html`:
-```html
-
-
-
- Mermaid 代码块魔改测试
-
-
-
-
- flowchart TD
- A --> B
-
-
-
-
- graph LR
- C --> D
-
-
-
-
- flowchart TD
- A -- text --> B
- C ==> D
-
-
-
-
-
-
-```
-
-## 8. 部署设计
-
-### 8.1 部署步骤
-
-#### 步骤 1:修改 argontheme.js
-1. 在 `highlightJsRender()` 函数开始处添加 `convertMermaidCodeblocks()` 调用
-2. 实现 `convertMermaidCodeblocks()` 函数
-3. 修改 `detectMermaidBlocks()` 函数,添加新选择器
-4. 修改 `extractMermaidCode()` 函数,支持新容器类型
-
-#### 步骤 2:测试验证
-1. 创建测试文件 `tests/test-codeblock-magic.html`
-2. 在本地环境测试所有用例
-3. 验证 PJAX 兼容性
-4. 检查浏览器控制台无错误
-
-#### 步骤 3:文档更新
-1. 更新 `docs/mermaid-usage-guide.md`
-2. 更新 `docs/mermaid-developer-guide.md`
-3. 更新 `docs/mermaid-faq.md`
-
-#### 步骤 4:发布
-1. 提交代码到 Git
-2. 更新版本号
-3. 发布更新
-
-### 8.2 回滚方案
-
-如果出现问题,可以快速回滚:
-1. 移除 `convertMermaidCodeblocks()` 调用
-2. 移除新添加的选择器
-3. 恢复原始代码
-
-**降级方案**:
-- 用户仍可使用 Shortcode 格式
-- 用户仍可使用容器语法
-- 不影响现有功能
-
-### 8.3 兼容性保证
-
-#### 向后兼容
-- 不影响现有的 Shortcode 格式
-- 不影响现有的容器语法
-- 不影响其他代码块的高亮
-
-#### 向前兼容
-- 预留扩展接口
-- 支持未来的新格式
-- 易于维护和升级
-
-## 9. 监控与维护
-
-### 9.1 日志设计
-
-#### 调试日志
-```javascript
-this.logDebug('开始转换 Mermaid 代码块');
-this.logDebug('找到 ' + elements.length + ' 个代码块');
-this.logDebug('代码内容: ' + code.substring(0, 100));
-this.logDebug('转换完成');
-```
-
-#### 错误日志
-```javascript
-console.error('Mermaid 代码块转换失败:', err);
-console.error('元素:', element);
-console.error('代码:', code);
-```
-
-### 9.2 性能监控
-
-#### 性能指标
-- 代码块转换时间
-- 页面加载时间
-- 内存使用情况
-
-#### 监控方法
-```javascript
-const startTime = performance.now();
-convertMermaidCodeblocks();
-const endTime = performance.now();
-console.log('转换耗时:', endTime - startTime, 'ms');
-```
-
-### 9.3 维护计划
-
-#### 定期检查
-- 每月检查浏览器兼容性
-- 每季度检查性能表现
-- 每半年检查代码质量
-
-#### 更新策略
-- 跟进 Mermaid.js 版本更新
-- 跟进 WordPress 版本更新
-- 跟进浏览器标准更新
-
-## 10. 风险与缓解
-
-### 10.1 技术风险
-
-#### 风险 1:与其他插件冲突
-**影响**:中等
-**概率**:低
-**缓解措施**:
-- 使用唯一的类名 `mermaid-from-codeblock`
-- 添加命名空间,避免冲突
-- 提供配置选项,允许禁用
-
-#### 风险 2:性能问题
-**影响**:低
-**概率**:低
-**缓解措施**:
-- 优化选择器,减少 DOM 查询
-- 使用缓存,避免重复处理
-- 添加性能监控
-
-#### 风险 3:浏览器兼容性
-**影响**:中等
-**概率**:低
-**缓解措施**:
-- 使用标准 API
-- 添加 polyfill
-- 提供降级方案
-
-### 10.2 用户体验风险
-
-#### 风险 1:误转换其他代码块
-**影响**:高
-**概率**:极低
-**缓解措施**:
-- 使用精确的选择器
-- 添加类名检查
-- 提供白名单机制
-
-#### 风险 2:特殊字符丢失
-**影响**:高
-**概率**:极低
-**缓解措施**:
-- 使用 `textContent` 保留原始内容
-- 不进行任何字符转换
-- 添加测试用例验证
-
-## 11. 未来扩展
-
-### 11.1 短期扩展(1-2 周)
-
-#### 配置选项
-添加主题设置选项:
-- 是否启用代码块魔改
-- 选择器优先级配置
-- 调试模式开关
-
-#### 性能优化
-- 添加缓存机制
-- 批量处理优化
-- 延迟加载支持
-
-### 11.2 中期扩展(1-2 月)
-
-#### 编辑器支持
-- 添加编辑器预览功能
-- 支持实时渲染
-- 提供语法提示
-
-#### 移动端优化
-- 优化移动端显示
-- 支持触摸交互
-- 响应式布局
-
-### 11.3 长期扩展(3-6 月)
-
-#### 多图表库支持
-- 支持 PlantUML
-- 支持 GraphViz
-- 支持 D3.js
-
-#### 高级功能
-- 图表编辑器
-- 图表导出(PNG、SVG)
-- 图表分享
-
-## 12. 总结
-
-### 12.1 设计优势
-
-1. **简单高效**:只需添加一个函数,修改三个位置
-2. **兼容性好**:不影响现有功能,支持多种格式
-3. **性能优秀**:使用原生 JavaScript,优化 DOM 操作
-4. **易于维护**:代码清晰,注释详细,易于理解
-5. **安全可靠**:防止 XSS,处理异常,提供降级
-
-### 12.2 关键决策
-
-| 决策 | 理由 |
-|------|------|
-| 在代码高亮前拦截 | 避免代码高亮干扰 |
-| 使用 textContent | 保留原始内容,防止 XSS |
-| 创建新容器类型 | 便于识别来源,支持多种格式 |
-| 添加重复处理防护 | 支持 PJAX,避免重复转换 |
-| 提供降级支持 | 确保即使转换失败仍能渲染 |
-
-### 12.3 实施建议
-
-1. **分步实施**:先实现核心功能,再添加优化
-2. **充分测试**:创建完整的测试用例,覆盖所有场景
-3. **文档完善**:更新用户文档和开发者文档
-4. **监控反馈**:收集用户反馈,持续优化
-
-### 12.4 成功标准
-
-- ✅ 可以使用 ` ```mermaid ` 代码块编写图表
-- ✅ 代码块不会被代码高亮处理
-- ✅ 图表正确渲染
-- ✅ 特殊字符不被转换
-- ✅ 换行符正确保留
-- ✅ PJAX 切换正常工作
-- ✅ 性能无明显影响
-- ✅ 兼容所有主流浏览器
+- [ ] 所有函数都有 JSDoc 注释
+- [ ] 所有异步操作都有错误处理
+- [ ] 所有事件监听器都在清理时移除
+- [ ] 使用 let/const 而不是 var
+- [ ] 遵循项目代码规范(Tab 缩进、单引号等)
+- [ ] 没有 console.log,使用 ArgonDebug
+- [ ] 性能优化(批量 DOM 操作、requestAnimationFrame)
diff --git a/.kiro/specs/mermaid-codeblock-magic/requirements.md b/.kiro/specs/mermaid-codeblock-magic/requirements.md
index a576fdf..eb1e238 100644
--- a/.kiro/specs/mermaid-codeblock-magic/requirements.md
+++ b/.kiro/specs/mermaid-codeblock-magic/requirements.md
@@ -1,501 +1,257 @@
-# Mermaid 代码块魔改支持 - 需求文档
-
-## 1. 项目概述
-
-### 1.1 背景
-当前 Argon 主题支持 Mermaid 图表渲染,但存在多个标记方式的兼容性问题:
-- **标准 Markdown 代码块** (` ```mermaid `):被 WP-Markdown 插件和代码高亮干扰
-- **容器语法** (`::: mermaid ... :::`):空行导致内容截断
-- **Shortcode** (`[mermaid]...[/mermaid]`):可用但不符合 Markdown 标准
-
-用户希望使用标准 Markdown 语法 ` ```mermaid `,但需要绕过所有干扰。
-
-### 1.2 核心问题
-1. WP-Markdown 插件会将 ` ```mermaid ` 代码块用 `document.write()` 包裹
-2. WordPress 的 `wptexturize()` 会自动转换特殊字符(`--` → `–`)
-3. 主题的代码高亮会处理 mermaid 代码块,添加行号和控制按钮
-4. 三方冲突导致 Mermaid 代码无法正确渲染
-
-### 1.3 解决方案
-**魔改代码块显示**:在代码高亮之前拦截 mermaid 代码块,将其转换为 Mermaid 渲染容器,完全绕过代码高亮和 WordPress 格式化。
-
-### 1.4 参考实现
-主题中数学公式的实现方式可以作为参考:
-- **MathJax/KaTeX**:使用特定分隔符(`$...$`、`\(...\)`)标记数学公式
-- **渲染时机**:在 PJAX 加载完成后调用 `MathJax.typeset()` 或 `renderMathInElement()`
-- **不干扰代码高亮**:数学公式使用特殊标记,不会被代码高亮处理
-- **WordPress 兼容**:数学公式分隔符不会被 WordPress 自动转换
-
-**关键差异**:
-- 数学公式使用**内联标记**(`$...$`),不需要代码块
-- Mermaid 需要使用**代码块**(` ```mermaid `),需要在代码高亮前拦截
-- 数学公式库自动扫描页面,Mermaid 需要手动检测和渲染
-
-## 2. 用户故事
-
-### 2.1 作为博客作者
-**我想要**:使用标准 Markdown 语法 ` ```mermaid ` 编写流程图
-**以便**:在原生编辑器中清晰可见,符合 Markdown 标准,无需学习特殊语法
-
-**验收标准**:
-- 可以使用 ` ```mermaid ` 代码块编写 Mermaid 图表
-- 代码块不会被代码高亮处理(无行号、无控制按钮)
-- 代码块会被正确转换为 Mermaid 图表
-- 支持所有 Mermaid 语法(flowchart, sequence, class, state 等)
-
-### 2.2 作为博客作者
-**我想要**:Mermaid 代码中的特殊字符不被 WordPress 转换
-**以便**:箭头符号 `-->` 不会变成 `–>`,图表能正确渲染
-
-**验收标准**:
-- 箭头符号 `-->` 保持不变
-- 双横线 `--` 保持不变
-- 其他特殊字符(`==`, `~~`, `::` 等)保持不变
-- 换行符正确保留
-
-### 2.3 作为博客作者
-**我想要**:Mermaid 代码块在编辑器中显示为代码块
-**以便**:编辑时能清晰看到代码结构,方便修改
-
-**验收标准**:
-- 在 WordPress 原生编辑器中显示为代码块
-- 在 WP-Markdown 编辑器中显示为代码块
-- 代码块有语法高亮(编辑器层面)
-- 保存后前端正确渲染为图表
-
-### 2.4 作为开发者
-**我想要**:拦截逻辑在代码高亮之前执行
-**以便**:避免代码高亮干扰 Mermaid 渲染
-
-**验收标准**:
-- 在 `highlightJsRender()` 函数开始处添加预处理
-- 查找所有 `pre > code.language-mermaid` 元素
-- 提取纯文本代码(不经过任何处理)
-- 创建 Mermaid 渲染容器
-- 替换原始代码块元素
-
-### 2.5 作为开发者
-**我想要**:支持多种 Mermaid 代码块格式
-**以便**:兼容不同插件和编辑器生成的 HTML 结构
-
-**验收标准**:
-- 支持 `` 格式
-- 支持 `` 格式
-- 支持 `` 格式(无 pre 包裹)
-- 支持 `` 格式
-
-## 3. 功能需求
-
-### 3.1 代码块拦截(核心功能)
-
-**需求描述**:在代码高亮之前拦截 mermaid 代码块
-
-**实现位置**:`argontheme.js` 的 `highlightJsRender()` 函数开始处(第 3942 行)
-
-**参考实现**:类似数学公式在 PJAX 加载后的处理方式(第 2862-2880 行)
-
-**处理流程**:
-1. 在 `highlightJsRender()` 函数开始处添加预处理
-2. 查找所有 mermaid 代码块(多种选择器)
-3. 遍历每个代码块
-4. 提取纯文本代码
-5. 创建 Mermaid 渲染容器
-6. 替换原始代码块元素
-7. 标记已处理(避免重复处理)
-
-**选择器优先级**:
-```javascript
-const selectors = [
- 'pre > code.language-mermaid', // 标准格式(最常见)
- 'pre > code.mermaid', // 简化格式
- 'code.language-mermaid', // 无 pre 包裹
- 'pre[data-lang="mermaid"]' // 自定义属性格式
-];
-```
-
-**实现示例**:
-```javascript
-function highlightJsRender(){
- // 在代码高亮之前,先处理 Mermaid 代码块
- convertMermaidCodeblocks();
-
- // 原有的代码高亮逻辑
- if (typeof(hljs) == "undefined"){
- return;
- }
- // ...
-}
-
-function convertMermaidCodeblocks(){
- // 查找所有 mermaid 代码块
- const selectors = [
- 'pre > code.language-mermaid',
- 'pre > code.mermaid',
- 'code.language-mermaid',
- 'pre[data-lang="mermaid"]'
- ];
-
- 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;
- targetElement.parentNode.replaceChild(container, targetElement);
- });
- });
-}
-```
-
-### 3.2 代码提取
-
-**需求描述**:从不同格式的代码块中提取纯文本代码
-
-**处理逻辑**:
-- 使用 `textContent` 获取纯文本(避免 HTML 实体)
-- 移除前后空白字符(`trim()`)
-- 不进行任何字符转换(保持原始内容)
-- 检查代码是否为空
-
-**特殊处理**:
-- 如果代码块包含 `