From 0a8bb3a453f1ba1739749279942551131d6d9a86 Mon Sep 17 00:00:00 2001 From: nanhaoluo <3075912108@qq.com> Date: Tue, 27 Jan 2026 10:42:08 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=BD=BB=E5=BA=95=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E6=89=80=E6=9C=89=20Mermaid=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 从 argontheme.js 移除所有 Mermaid 相关代码和注释 - 从 style.css 移除所有 Mermaid 样式(约 300 行) - 移除代码高亮中跳过 mermaid 容器的逻辑 - 移除 PJAX 清理函数中的 Mermaid 引用 - 删除临时清理脚本和空文档 --- .kiro/steering/mermaid-fix-2026-01-27.md | 353 --- ai-summary-query.php | 91 +- argon-performance.js | 43 +- argontheme.js | 2100 +-------------- docs/mermaid-developer-guide.md | 1230 --------- docs/mermaid-faq.md | 712 ----- docs/mermaid-shortcode-guide.md | 505 ---- docs/mermaid-troubleshooting.md | 360 --- docs/mermaid-usage-guide.md | 439 --- docs/mermaid-user-guide.md | 719 ----- functions.php | 42 - header.php | 94 +- settings.php | 282 +- sidebar.php | 15 +- style.css | 3120 ---------------------- template-parts/content-preview-1.php | 9 +- template-parts/content-preview-2.php | 9 +- template-parts/content-preview-3.php | 9 +- test-api-connection-debug.php | 172 ++ tests/run-mermaid-config-tests.php | 234 -- tests/test-mermaid-config.php | 293 -- tests/test-mermaid-error-handling.md | 207 -- tests/test-mermaid-fallback.php | 249 -- tests/test-mermaid-loader.php | 136 - tests/test-mermaid-wait-mechanism.md | 124 - tests/test-pjax-mermaid-rendering.md | 413 --- 26 files changed, 542 insertions(+), 11418 deletions(-) delete mode 100644 .kiro/steering/mermaid-fix-2026-01-27.md delete mode 100644 docs/mermaid-developer-guide.md delete mode 100644 docs/mermaid-faq.md delete mode 100644 docs/mermaid-shortcode-guide.md delete mode 100644 docs/mermaid-troubleshooting.md delete mode 100644 docs/mermaid-usage-guide.md delete mode 100644 docs/mermaid-user-guide.md create mode 100644 test-api-connection-debug.php delete mode 100644 tests/run-mermaid-config-tests.php delete mode 100644 tests/test-mermaid-config.php delete mode 100644 tests/test-mermaid-error-handling.md delete mode 100644 tests/test-mermaid-fallback.php delete mode 100644 tests/test-mermaid-loader.php delete mode 100644 tests/test-mermaid-wait-mechanism.md delete mode 100644 tests/test-pjax-mermaid-rendering.md diff --git a/.kiro/steering/mermaid-fix-2026-01-27.md b/.kiro/steering/mermaid-fix-2026-01-27.md deleted file mode 100644 index 06c193d..0000000 --- a/.kiro/steering/mermaid-fix-2026-01-27.md +++ /dev/null @@ -1,353 +0,0 @@ -# Mermaid 渲染问题修复 - -## 修复时间 -2026-01-27 - -## 问题描述 - -用户报告了以下 Mermaid 相关问题: - -1. **代码块转换功能被禁用**:`convertMermaidCodeblocks()` 函数第一行有 `return;`,导致 ```mermaid 代码块无法转换 -2. **检测选择器不完整**:`detectMermaidBlocks()` 只检测 `div.mermaid-shortcode`,缺少其他格式的检测 -3. **首页预览显示原始代码**:点击首页文章预览时,显示无格式化的纯文字 Mermaid 代码 - -## 根本原因 - -### 1. 代码块转换被禁用 -在 `argontheme.js` 第 4345 行,`convertMermaidCodeblocks()` 函数开头有一个 `return;` 语句,导致整个函数被跳过: - -```javascript -function convertMermaidCodeblocks(){ - return; // ← 这行导致函数直接返回 - // ... 后面的代码都不会执行 -} -``` - -### 2. 检测选择器不完整 -在 `MermaidRenderer.detectMermaidBlocks()` 中,选择器数组只包含一个元素: - -```javascript -const selectors = [ - 'div.mermaid-shortcode' // 只检测 shortcode 格式 -]; -``` - -缺少对以下格式的检测: -- `div.mermaid-from-codeblock` - 代码块魔改格式 -- `div.mermaid` - 标准格式 -- `pre code.language-mermaid` - Markdown 格式 -- `pre[data-lang="mermaid"]` - 自定义属性格式 -- `code.mermaid` - 简化格式 - -### 3. 首页预览问题 -在三个文章预览模板中,使用 `wp_trim_words()` 直接处理包含 `[mermaid]...[/mermaid]` shortcode 的内容: - -```php -$preview = wp_trim_words(get_the_content('...'), $trim_words_count); -``` - -`wp_trim_words()` 会破坏 shortcode 结构,导致显示原始的 Mermaid 代码文本。 - -## 修复方案 - -### 1. 启用代码块转换功能 - -**文件**:`argontheme.js` - -**修改**:移除 `convertMermaidCodeblocks()` 函数开头的 `return;` 语句 - -```javascript -// 修改前 -function convertMermaidCodeblocks(){ - return; - // ... -} - -// 修改后 -function convertMermaidCodeblocks(){ - // 支持多种代码块格式 - // ... -} -``` - -**效果**: -- ✅ ```mermaid 代码块可以正常转换为 `
- ↓
- 提取纯文本代码
- ↓
- 创建
- ↓
- 替换原始代码块
- ↓
-代码高亮处理其他代码块
- ↓
-MermaidRenderer.detectMermaidBlocks() ← 【检测阶段】
- ↓
-MermaidRenderer.renderChart() ← 【渲染阶段】
-```
-
-### 支持的 Mermaid 格式
-
-#### 1. Shortcode 格式(推荐)
-
-```
-[mermaid]
-flowchart TD
- A --> B
-[/mermaid]
-```
-
-**优势**:
-- 最稳定的方式
-- 不受代码高亮影响
-- 支持参数配置
-
-#### 2. Markdown 代码块格式
-
-````markdown
-```mermaid
-flowchart TD
- A --> B
-```
-````
-
-**优势**:
-- 标准 Markdown 语法
-- 编辑器支持好
-- 可以拉起代码高亮
-
-**注意**:需要代码块转换功能支持
-
-#### 3. HTML 容器格式
-
-```html
-
-flowchart TD
- A --> B
-
-```
-
-**优势**:
-- 直接渲染
-- 兼容性好
-
-## 测试验证
-
-### 测试场景 1:Shortcode 格式
-
-**测试步骤**:
-1. 创建新文章
-2. 使用 `[mermaid]...[/mermaid]` 格式插入图表
-3. 发布文章
-4. 查看文章页面
-5. 查看首页预览
-
-**预期结果**:
-- ✅ 文章页面正确渲染 Mermaid 图表
-- ✅ 首页预览显示 `[Mermaid 图表]` 占位符
-
-### 测试场景 2:Markdown 代码块格式
-
-**测试步骤**:
-1. 创建新文章
-2. 使用 ` ```mermaid ` 格式插入图表
-3. 发布文章
-4. 查看文章页面
-
-**预期结果**:
-- ✅ 代码块被转换为 ``
-- ✅ 图表正确渲染
-- ✅ 不显示代码高亮的行号和复制按钮
-
-### 测试场景 3:PJAX 切换
-
-**测试步骤**:
-1. 在首页点击文章链接(PJAX 加载)
-2. 查看 Mermaid 图表是否渲染
-3. 返回首页(PJAX 加载)
-4. 再次点击文章
-
-**预期结果**:
-- ✅ PJAX 切换后图表正常渲染
-- ✅ 不会重复渲染
-- ✅ 主题切换正常工作
-
-## 相关文件
-
-### 修改的文件
-- `argontheme.js` - 启用代码块转换,添加完整选择器
-- `functions.php` - 添加 `argon_remove_mermaid_from_preview()` 函数
-- `template-parts/content-preview-1.php` - 修复预览显示
-- `template-parts/content-preview-2.php` - 修复预览显示
-- `template-parts/content-preview-3.php` - 修复预览显示
-
-### 相关文档
-- `docs/mermaid-user-guide.md` - 用户使用指南
-- `docs/mermaid-developer-guide.md` - 开发者文档
-- `docs/mermaid-troubleshooting.md` - 故障排查指南
-
-## Git 提交
-
-```bash
-git commit -m "fix: 修复 Mermaid 渲染问题
-
-- 启用代码块转换功能(移除 convertMermaidCodeblocks 中的 return 语句)
-- 添加完整的 Mermaid 代码块检测选择器
-- 修复首页预览中显示原始 Mermaid 代码的问题
-- 添加 argon_remove_mermaid_from_preview 函数过滤预览内容
-- 更新三个文章预览模板,在预览中用 [Mermaid 图表] 替代原始代码"
-```
-
-**提交哈希**:135c226
-
-## 后续建议
-
-### 1. 功能增强
-
-- [ ] 添加预览中的 Mermaid 图表缩略图
-- [ ] 支持更多 Mermaid 图表类型
-- [ ] 添加图表导出功能
-
-### 2. 性能优化
-
-- [ ] 延迟加载 Mermaid 库
-- [ ] 缓存渲染结果
-- [ ] 优化大型图表的渲染
-
-### 3. 用户体验
-
-- [ ] 添加图表编辑器
-- [ ] 提供图表模板
-- [ ] 改进错误提示
-
-## 常见问题
-
-### Q1: 为什么代码块转换被禁用了?
-
-A: 可能是在之前的某次修复中,为了临时解决某个问题而添加了 `return;` 语句,但忘记移除。
-
-### Q2: 如何确保 PJAX 兼容性?
-
-A: 代码块转换在 `highlightJsRender()` 中调用,该函数已在 PJAX 回调中注册,因此自动支持 PJAX。
-
-### Q3: 为什么不在预览中渲染 Mermaid 图表?
-
-A: 因为:
-1. 预览内容会被 `wp_trim_words()` 截断,可能破坏图表代码
-2. 在列表页渲染大量图表会影响性能
-3. 使用占位符更简洁明了
-
-### Q4: 如何添加新的 Mermaid 格式支持?
-
-A: 在 `detectMermaidBlocks()` 的 `selectors` 数组中添加新的选择器,并在 `extractMermaidCode()` 中添加对应的提取逻辑。
-
-## 总结
-
-本次修复解决了 Mermaid 渲染的三个主要问题:
-
-1. ✅ **启用代码块转换**:移除了阻止转换的 `return;` 语句
-2. ✅ **完善检测机制**:添加了完整的选择器列表,支持多种格式
-3. ✅ **修复预览显示**:在预览中用占位符替代原始代码
-
-用户现在可以:
-- 使用 `[mermaid]...[/mermaid]` shortcode 格式(推荐)
-- 使用 ` ```mermaid ` Markdown 代码块格式
-- 在首页预览中看到友好的占位符而不是原始代码
-- 享受完整的 Mermaid 图表渲染功能
-
-所有修改都遵循了 Argon 主题的代码规范,并保持了向后兼容性。
diff --git a/ai-summary-query.php b/ai-summary-query.php
index fd680e3..e0972ea 100644
--- a/ai-summary-query.php
+++ b/ai-summary-query.php
@@ -178,6 +178,23 @@ if (!empty($query_code)) {
'code' => $query_code
];
+ $log_table = $wpdb->prefix . 'argon_ai_query_log';
+ $latest = $wpdb->get_row($wpdb->prepare(
+ "SELECT provider, model FROM {$log_table} WHERE scenario = %s AND status = 'success' AND post_id = %d ORDER BY id DESC LIMIT 1",
+ 'summary',
+ $post_id
+ ), ARRAY_A);
+ if (is_array($latest)) {
+ if (!empty($latest['provider']) && $latest['provider'] !== $result['provider']) {
+ $result['provider'] = $latest['provider'];
+ update_post_meta($post_id, '_argon_ai_summary_provider', $result['provider']);
+ }
+ if (isset($latest['model']) && $latest['model'] !== $result['model']) {
+ $result['model'] = $latest['model'];
+ update_post_meta($post_id, '_argon_ai_summary_model', $result['model']);
+ }
+ }
+
$provider_names = [
'openai' => 'OpenAI',
'anthropic' => 'Anthropic',
@@ -236,8 +253,30 @@ if (!empty($query_code)) {
];
// 获取 AI 配置信息
- $result['provider'] = get_option('argon_ai_summary_provider', 'openai');
- $result['model'] = get_option('argon_ai_summary_model', '');
+ $result['provider'] = get_comment_meta($comment_id, '_argon_spam_detection_provider', true);
+ $result['model'] = get_comment_meta($comment_id, '_argon_spam_detection_model', true);
+ $log_table = $wpdb->prefix . 'argon_ai_query_log';
+ $latest = $wpdb->get_row($wpdb->prepare(
+ "SELECT provider, model FROM {$log_table} WHERE scenario = %s AND status = 'success' AND comment_id = %d ORDER BY id DESC LIMIT 1",
+ 'spam_detection',
+ $comment_id
+ ), ARRAY_A);
+ if (is_array($latest)) {
+ if (!empty($latest['provider']) && $latest['provider'] !== $result['provider']) {
+ $result['provider'] = $latest['provider'];
+ update_comment_meta($comment_id, '_argon_spam_detection_provider', $result['provider']);
+ }
+ if (isset($latest['model']) && $latest['model'] !== $result['model']) {
+ $result['model'] = $latest['model'];
+ update_comment_meta($comment_id, '_argon_spam_detection_model', $result['model']);
+ }
+ }
+ if (empty($result['provider'])) {
+ $result['provider'] = get_option('argon_ai_summary_provider', 'openai');
+ }
+ if (empty($result['model'])) {
+ $result['model'] = get_option('argon_ai_summary_model', '');
+ }
$provider_names = [
'openai' => 'OpenAI',
@@ -702,6 +741,24 @@ if (current_user_can('manage_options')):
foreach ($summaries as $summary) {
$post = get_post($summary->post_id);
if ($post) {
+ $summary_provider = get_post_meta($summary->post_id, '_argon_ai_summary_provider', true);
+ $summary_model = get_post_meta($summary->post_id, '_argon_ai_summary_model', true);
+ $log_table = $wpdb->prefix . 'argon_ai_query_log';
+ $latest = $wpdb->get_row($wpdb->prepare(
+ "SELECT provider, model FROM {$log_table} WHERE scenario = %s AND status = 'success' AND post_id = %d ORDER BY id DESC LIMIT 1",
+ 'summary',
+ $summary->post_id
+ ), ARRAY_A);
+ if (is_array($latest)) {
+ if (!empty($latest['provider']) && $latest['provider'] !== $summary_provider) {
+ $summary_provider = $latest['provider'];
+ update_post_meta($summary->post_id, '_argon_ai_summary_provider', $summary_provider);
+ }
+ if (isset($latest['model']) && $latest['model'] !== $summary_model) {
+ $summary_model = $latest['model'];
+ update_post_meta($summary->post_id, '_argon_ai_summary_model', $summary_model);
+ }
+ }
$all_records[] = [
'type' => 'summary',
'code' => $summary->code,
@@ -714,8 +771,8 @@ if (current_user_can('manage_options')):
'post_title' => get_the_title($summary->post_id),
'post_url' => get_permalink($summary->post_id),
'summary' => get_post_meta($summary->post_id, '_argon_ai_summary', true),
- 'model' => get_post_meta($summary->post_id, '_argon_ai_summary_model', true),
- 'provider' => get_post_meta($summary->post_id, '_argon_ai_summary_provider', true),
+ 'model' => $summary_model,
+ 'provider' => $summary_provider,
'generated_time' => $summary->time
]
];
@@ -726,6 +783,30 @@ if (current_user_can('manage_options')):
$comment = get_comment($detection->comment_id);
if ($comment) {
$detection_result = get_comment_meta($detection->comment_id, '_argon_spam_detection_result', true);
+ $provider = get_comment_meta($detection->comment_id, '_argon_spam_detection_provider', true);
+ $model = get_comment_meta($detection->comment_id, '_argon_spam_detection_model', true);
+ $log_table = $wpdb->prefix . 'argon_ai_query_log';
+ $latest = $wpdb->get_row($wpdb->prepare(
+ "SELECT provider, model FROM {$log_table} WHERE scenario = %s AND status = 'success' AND comment_id = %d ORDER BY id DESC LIMIT 1",
+ 'spam_detection',
+ $detection->comment_id
+ ), ARRAY_A);
+ if (is_array($latest)) {
+ if (!empty($latest['provider']) && $latest['provider'] !== $provider) {
+ $provider = $latest['provider'];
+ update_comment_meta($detection->comment_id, '_argon_spam_detection_provider', $provider);
+ }
+ if (isset($latest['model']) && $latest['model'] !== $model) {
+ $model = $latest['model'];
+ update_comment_meta($detection->comment_id, '_argon_spam_detection_model', $model);
+ }
+ }
+ if (empty($provider)) {
+ $provider = get_option('argon_ai_summary_provider', 'openai');
+ }
+ if (empty($model)) {
+ $model = get_option('argon_ai_summary_model', '');
+ }
$all_records[] = [
'type' => 'spam_detection',
'code' => $detection->code,
@@ -747,6 +828,8 @@ if (current_user_can('manage_options')):
'suggestion' => isset($detection_result['suggestion']) ? $detection_result['suggestion'] : '',
'analysis' => isset($detection_result['analysis']) ? $detection_result['analysis'] : '',
'action' => isset($detection_result['action']) ? $detection_result['action'] : '',
+ 'provider' => $provider,
+ 'model' => $model,
'detection_time' => $detection->time
]
];
diff --git a/argon-performance.js b/argon-performance.js
index 1ef6498..abea3bc 100644
--- a/argon-performance.js
+++ b/argon-performance.js
@@ -696,23 +696,58 @@ class ArgonResourceLoader {
return this.loading.get(name);
}
- // 创建新的加载 Promise
+ const timeoutValue = (typeof argonConfig !== 'undefined' && argonConfig.resource_load_timeout) ? parseInt(argonConfig.resource_load_timeout, 10) : 8000;
+ const timeout = Number.isFinite(timeoutValue) ? timeoutValue : 8000;
+
const promise = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.async = true;
- script.onload = () => {
+ let finished = false;
+ let timeoutId = null;
+
+ const clearState = () => {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ timeoutId = null;
+ }
+ script.onload = null;
+ script.onerror = null;
+ };
+
+ const resolveSuccess = () => {
+ if (finished) return;
+ finished = true;
+ clearState();
this.loaded.add(name);
this.loading.delete(name);
resolve();
};
- script.onerror = () => {
+ const rejectFailure = (error) => {
+ if (finished) return;
+ finished = true;
+ clearState();
+ if (script.parentNode) {
+ script.parentNode.removeChild(script);
+ }
this.loading.delete(name);
- reject(new Error(`Failed to load ${name} from ${url}`));
+ reject(error);
};
+ script.onload = () => {
+ resolveSuccess();
+ };
+
+ script.onerror = () => {
+ rejectFailure(new Error(`Failed to load ${name} from ${url}`));
+ };
+
+ timeoutId = setTimeout(() => {
+ rejectFailure(new Error(`Timeout loading ${name} from ${url}`));
+ }, timeout);
+
document.head.appendChild(script);
});
diff --git a/argontheme.js b/argontheme.js
index 6f7d5e4..3b0056c 100644
--- a/argontheme.js
+++ b/argontheme.js
@@ -2876,42 +2876,6 @@ function cleanupTippyInstances() {
}
}
-/**
- * 清理 Mermaid 实例
- * @returns {void}
- */
-function cleanupMermaidInstances() {
- try {
- // 清理已渲染的图表记录
- if (typeof MermaidRenderer !== 'undefined' && MermaidRenderer.rendered) {
- const count = MermaidRenderer.rendered.size;
- MermaidRenderer.rendered.clear();
- if (count > 0) {
- ArgonDebug.log(`已清理 ${count} 个 Mermaid 图表记录`);
- }
- }
-
- // 移除 Mermaid 容器的事件监听器
- document.querySelectorAll('.mermaid-container').forEach(function(container) {
- // 移除工具栏事件监听器
- const toolbar = container.querySelector('.mermaid-toolbar');
- if (toolbar) {
- // 克隆节点以移除所有事件监听器
- const newToolbar = toolbar.cloneNode(true);
- toolbar.parentNode.replaceChild(newToolbar, toolbar);
- }
-
- // 清理全屏模式
- if (container._fullscreenCleanup) {
- container._fullscreenCleanup();
- }
- });
-
- ArgonDebug.log('Mermaid 实例已清理');
- } catch(e) {
- ArgonDebug.warn('清理 Mermaid 实例失败:', e);
- }
-}
/**
* 清理动态添加的 style 标签
@@ -2957,7 +2921,6 @@ function cleanupDynamicScripts() {
*/
function cleanupEventListeners() {
try {
- // 清理 Mermaid 相关的事件监听器
// 注意:大部分事件使用事件委托,不需要手动清理
// 这里只清理特定的非委托事件
@@ -2977,7 +2940,6 @@ function cleanupEventListeners() {
* - Lazyload Observer
* - Zoomify 实例
* - Tippy 实例
- * - Mermaid 实例
* - 动态 style 标签
* - 动态 script 标签
* - 事件监听器
@@ -2990,9 +2952,7 @@ function cleanupPjaxResources() {
// 按顺序清理各类资源
cleanupLazyloadObserver();
cleanupZoomifyInstances();
- cleanupTippyInstances();
- cleanupMermaidInstances();
- cleanupDynamicStyles();
+ cleanupTippyInstances(); cleanupDynamicStyles();
cleanupDynamicScripts();
cleanupEventListeners();
@@ -3164,8 +3124,8 @@ $.pjax.defaults.fragment = ['#primary', '#leftbar_part1_menu', '#leftbar_part2_i
/*
* PJAX 事件处理优化说明:
- * 1. pjax:beforeReplace - 统一清理资源(LazyLoad Observer、Zoomify、Tippy、Mermaid)
- * 2. pjax:complete - 单次初始化所有功能模块,添加错误处理,包含 Mermaid 渲染
+ * 1. pjax:beforeReplace - 统一清理资源(LazyLoad Observer、Zoomify、Tippy)
+ * 2. pjax:complete - 单次初始化所有功能模块,添加错误处理,初始化所有功能模块
* 3. pjax:end - 只处理特定任务(移动端目录重置、GT4 验证码重置)
* 4. 移除了重复的初始化调用,避免资源泄漏和性能问题
*
@@ -3195,7 +3155,7 @@ $(document).pjax("a[href]:not([no-pjax]):not(.no-pjax):not([target='_blank']):no
}).on('pjax:beforeReplace', function(e, dom) {
// ========== 需求 1.1-1.4: 清理旧页面的所有资源 ==========
// 调用统一的资源清理管理器
- // 清理内容:Lazyload Observer、Zoomify、Tippy、Mermaid 实例、动态标签
+ // 清理内容:Lazyload Observer、Zoomify、Tippy 实例、动态标签
cleanupPjaxResources();
// 更新 UI 状态
@@ -3260,18 +3220,6 @@ $(document).pjax("a[href]:not([no-pjax]):not(.no-pjax):not([target='_blank']):no
try { calcHumanTimesOnPage(); } catch (err) { ArgonDebug.error('calcHumanTimesOnPage failed:', err); }
try { foldLongComments(); } catch (err) { ArgonDebug.error('foldLongComments failed:', err); }
try { foldLongShuoshuo(); } catch (err) { ArgonDebug.error('foldLongShuoshuo failed:', err); }
-
- // Mermaid 图表渲染(需求 3.6: 页面切换时重新渲染)
- try {
- if (typeof MermaidRenderer !== 'undefined' && MermaidRenderer.init && !MermaidRenderer.initialized) {
- MermaidRenderer.init();
- }
- if (typeof MermaidRenderer !== 'undefined' && MermaidRenderer.renderAllCharts) {
- MermaidRenderer.renderAllCharts();
- }
- } catch (err) {
- ArgonDebug.error('MermaidRenderer.renderAllCharts failed:', err);
- }
try { handleHashNavigation(); } catch (err) { ArgonDebug.error('handleHashNavigation failed:', err); }
$("html").trigger("resize");
@@ -4336,99 +4284,8 @@ function getCodeFromBlock(block){
return res;
}
-/**
- * 在代码高亮之前转换 Mermaid 代码块
- * 将 转换为
- * 这样可以避免代码高亮干扰 Mermaid 渲染
- */
-function convertMermaidCodeblocks(){
- // 支持多种代码块格式
- const selectors = [
- 'pre > code.language-mermaid', // 标准 Markdown 格式(最常见)
- 'pre > code.mermaid', // 简化格式
- 'code.language-mermaid', // 无 pre 包裹
- 'pre[data-lang="mermaid"]' // 自定义属性格式
- ];
-
- let processedCount = 0;
-
- selectors.forEach(selector => {
- document.querySelectorAll(selector).forEach(element => {
- // 避免重复处理
- if (element.dataset.mermaidProcessed) {
- return;
- }
-
- // 提取代码
- let code = element.textContent;
-
- // 清理代码:移除前后空白,但保留内部换行
- code = code.trim();
-
- // 如果代码为空,跳过
- if (!code) {
- return;
- }
-
- // 移除每行开头的统一缩进(保持相对缩进)
- const lines = code.split('\n');
- if (lines.length > 1) {
- // 找到最小缩进
- let minIndent = Infinity;
- lines.forEach(line => {
- if (line.trim().length > 0) {
- const indent = line.match(/^\s*/)[0].length;
- if (indent < minIndent) {
- minIndent = indent;
- }
- }
- });
-
- // 移除最小缩进
- if (minIndent > 0 && minIndent !== Infinity) {
- code = lines.map(line => {
- if (line.trim().length === 0) {
- return '';
- }
- return line.substring(minIndent);
- }).join('\n');
- }
- }
-
- // 创建容器
- const container = document.createElement('div');
- container.className = 'mermaid mermaid-from-codeblock';
- container.textContent = code;
- container.dataset.processed = 'true';
-
- // 调试日志
- if (typeof console !== 'undefined' && console.log) {
- console.log('[Mermaid 转换] 代码长度:', code.length);
- console.log('[Mermaid 转换] 代码内容:', code.substring(0, 200));
- console.log('[Mermaid 转换] 容器 textContent:', container.textContent.substring(0, 200));
- }
-
- // 替换元素
- const targetElement = element.closest('pre') || element;
- if (targetElement.parentNode) {
- targetElement.parentNode.replaceChild(container, targetElement);
- processedCount++;
- }
-
- // 标记已处理
- element.dataset.mermaidProcessed = 'true';
- });
- });
-
- if (processedCount > 0 && typeof console !== 'undefined' && console.log) {
- console.log('[Mermaid] 转换了 ' + processedCount + ' 个代码块');
- }
-}
-function highlightJsRender(){
- // 在代码高亮之前,先处理 Mermaid 代码块
- convertMermaidCodeblocks();
-
+function highlightJsRender(){
if (typeof(hljs) == "undefined"){
return;
}
@@ -4448,14 +4305,6 @@ function highlightJsRender(){
if ($(block).hasClass("no-hljs")){
return;
}
- // 跳过 mermaid 代码块(避免代码高亮干扰 mermaid 渲染)
- if ($(block).hasClass("language-mermaid") || $(block).hasClass("mermaid")){
- return;
- }
- // 跳过在 .mermaid 容器内的代码块
- if ($(block).closest('.mermaid').length > 0){
- return;
- }
$(block).parent().attr("id", randomString());
hljs.highlightBlock(block);
hljs.lineNumbersBlock(block, {singleLine: true});
@@ -4813,1942 +4662,3 @@ void 0;
};
}
})();
-
-// ==========================================================================
-// Mermaid 图表渲染引擎
-// ==========================================================================
-(function() {
- 'use strict';
-
- // ---------- 配置和状态管理 ----------
-
- /**
- * Mermaid 渲染引擎配置
- */
- const MermaidRenderer = {
- initialized: false,
- rendered: new Set(), // 已渲染的图表 ID 集合
- config: null,
- compatibilityFallbackAttempted: false,
-
- /**
- * 等待 Mermaid 库加载
- * 需求 2.6: 清除缓存后首次加载时等待 Mermaid 库完全加载
- * 需求 4.1: 开始渲染前检查 Mermaid 库是否已加载
- * 需求 4.2: Mermaid 库未加载时等待库加载或显示错误提示
- * @param {number} timeout - 超时时间(毫秒),默认 10000ms (增加超时时间以应对慢速网络)
- * @returns {Promise} 是否加载成功
- */
- waitForMermaid(timeout = 10000) {
- return new Promise((resolve) => {
- // 如果已经加载,直接返回成功
- if (typeof window.mermaid !== 'undefined') {
- this.logDebug('Mermaid 库已加载');
- resolve(true);
- return;
- }
-
- this.logDebug('Mermaid 库未加载,开始等待...');
-
- const startTime = Date.now();
- const checkInterval = setInterval(() => {
- // 检查是否已加载
- if (typeof window.mermaid !== 'undefined') {
- clearInterval(checkInterval);
- const loadTime = Date.now() - startTime;
- this.logDebug(`Mermaid 库加载成功,耗时 ${loadTime}ms`);
- resolve(true);
- }
- // 检查是否超时
- else if (Date.now() - startTime > timeout) {
- clearInterval(checkInterval);
- this.logError(`Mermaid 库加载超时(${timeout}ms)`);
-
- // 尝试手动触发降级加载
- if (typeof window.argonMermaidLoadFallback === 'function') {
- this.logDebug('尝试手动触发降级加载');
- window.argonMermaidLoadFallback();
- // 给降级加载一点时间
- setTimeout(() => {
- if (typeof window.mermaid !== 'undefined') {
- resolve(true);
- } else {
- resolve(false);
- }
- }, 2000);
- } else {
- resolve(false);
- }
- }
- }, 100); // 每 100ms 检查一次
- });
- },
-
- /**
- * 初始化 Mermaid 配置
- */
- initConfig() {
- // 检查 Mermaid 库是否已加载
- if (typeof window.mermaid === 'undefined') {
- this.logDebug('Mermaid 库未加载');
- return false;
- }
-
- // 检查配置是否存在
- if (typeof window.argonMermaidConfig === 'undefined') {
- this.logDebug('Mermaid 配置未找到,使用默认配置');
- this.config = {
- enabled: true,
- theme: 'auto',
- debugMode: false,
- fallbackUrls: [],
- libraryLoadedByPlugin: false
- };
- } else {
- this.config = window.argonMermaidConfig;
- }
-
- // 如果库由插件加载,记录日志
- if (this.config.libraryLoadedByPlugin) {
- this.logDebug('Mermaid 库由插件加载,主题只提供样式增强');
- }
-
- // 获取当前主题
- const theme = this.getMermaidTheme();
-
- // 配置 Mermaid
- // 需求 2.1-2.4: 支持多种图表类型,设置正确的主题和安全级别
- try {
- // 检查 initialize 是否存在(兼容旧版本)
- if (typeof window.mermaid.initialize === 'function') {
- window.mermaid.initialize({
- startOnLoad: false, // 手动控制渲染时机
- theme: theme, // 根据网站主题自动切换
- securityLevel: 'loose', // 允许 HTML 标签
- logLevel: this.config.debugMode ? 'debug' : 'error',
- // 流程图配置 (需求 2.2)
- flowchart: {
- useMaxWidth: true,
- htmlLabels: true,
- curve: 'basis'
- },
- // 时序图配置
- sequence: {
- useMaxWidth: true,
- wrap: true
- },
- // 甘特图配置
- gantt: {
- useMaxWidth: true
- },
- // ER 图配置 (需求 2.3)
- er: {
- useMaxWidth: true,
- layoutDirection: 'TB'
- },
- // 状态图配置 (需求 2.4)
- stateDiagram: {
- useMaxWidth: true
- },
- // 类图配置
- class: {
- useMaxWidth: true
- },
- // 饼图配置
- pie: {
- useMaxWidth: true
- },
- // Git 图配置
- gitGraph: {
- useMaxWidth: true
- },
- // 用户旅程图配置
- journey: {
- useMaxWidth: true
- }
- });
- this.initialized = true;
- this.logDebug('Mermaid 配置初始化成功', { theme });
- return true;
- } else {
- this.logError('Mermaid.initialize 方法不存在');
- return false;
- }
- } catch (error) {
- this.logError('Mermaid 配置初始化失败', error);
- return false;
- }
- },
-
- /**
- * 获取当前主题对应的 Mermaid 主题
- * @returns {string} Mermaid 主题名称
- */
- getMermaidTheme() {
- // 如果配置了固定主题(非 auto),直接返回
- if (this.config && this.config.theme !== 'auto') {
- return this.config.theme;
- }
-
- // 根据页面主题模式自动选择
- const isDarkMode = document.documentElement.classList.contains('darkmode');
- return isDarkMode ? 'dark' : 'default';
- },
-
- // ---------- 代码块检测器 ----------
-
- /**
- * 检测所有 Mermaid 代码块
- * 需求 3.1-3.5: 扫描页面中的 Mermaid 代码块并过滤已渲染的块
- * @returns {HTMLElement[]} 未渲染的 Mermaid 代码块数组
- */
- detectMermaidBlocks() {
- const blocks = [];
- const seen = new Set();
- const selectors = [
- 'div.mermaid-shortcode', // Shortcode 格式(推荐)
- 'div.mermaid-from-codeblock', // 代码块魔改格式
- 'div.mermaid', // 标准格式
- 'pre code.language-mermaid', // Markdown 格式(降级)
- 'pre[data-lang="mermaid"]', // 自定义属性格式
- 'code.mermaid' // 简化格式
- ];
-
- selectors.forEach(selector => {
- document.querySelectorAll(selector).forEach(element => {
- if (this.isRendered(element)) {
- return;
- }
- if (seen.has(element)) {
- return;
- }
- blocks.push(element);
- seen.add(element);
- });
- });
-
- this.logDebug(`检测到 ${blocks.length} 个未渲染的 Mermaid 代码块`);
- return blocks;
- },
-
- /**
- * 检查代码块是否已渲染
- * 需求 3.5: 避免重复渲染已渲染的代码块
- * @param {HTMLElement} element - 代码块元素
- * @returns {boolean} 是否已渲染
- */
- isRendered(element) {
- // 检查元素是否有渲染标记
- if (element.hasAttribute('data-mermaid-rendered')) {
- return true;
- }
-
- if (element.classList && element.classList.contains('mermaid-error-container')) {
- return true;
- }
-
- if (element.classList && element.classList.contains('mermaid-loading')) {
- return true;
- }
-
- if (element.classList && element.classList.contains('mermaid-container')) {
- return true;
- }
-
- if (element.tagName === 'DIV' && element.classList.contains('mermaid') && element.querySelector('svg')) {
- return true;
- }
-
- // 检查元素是否在 mermaid-container 容器中
- if (element.closest('.mermaid-container') !== null) {
- return true;
- }
-
- return false;
- },
- /**
- * 提取代码块内容
- * @param {HTMLElement} element - 代码块元素
- * @returns {string} Mermaid 代码
- */
- extractMermaidCode(element) {
- let code = '';
-
- // 处理 Shortcode 格式(推荐)
- if (element.classList.contains('mermaid-shortcode')) {
- code = element.textContent;
- this.logDebug('从 Shortcode 格式提取代码,长度: ' + code.length);
- this.logDebug('代码内容: ' + code.substring(0, 200));
- }
- // 处理代码块魔改格式(新增)
- else if (element.classList.contains('mermaid-from-codeblock')) {
- code = element.textContent;
- this.logDebug('从代码块魔改格式提取代码,长度: ' + code.length);
- this.logDebug('代码内容: ' + code.substring(0, 200));
- this.logDebug('元素 HTML: ' + element.outerHTML.substring(0, 300));
- }
- // 处理 Markdown 容器语法格式
- else if (element.classList.contains('mermaid-container-block')) {
- code = element.textContent;
- this.logDebug('从 Markdown 容器语法提取代码,长度: ' + code.length);
- this.logDebug('代码内容: ' + code.substring(0, 200));
- }
- // 根据不同的元素类型提取代码
- else if (element.tagName === 'DIV' && element.classList.contains('mermaid')) {
- // 检查是否包含 WP-Markdown 生成的 script 标签
- const scriptTag = element.querySelector('script');
- if (scriptTag) {
- // 提取 document.write() 中的内容
- const scriptContent = scriptTag.textContent || scriptTag.innerText;
- this.logDebug('检测到 script 标签,原始内容: ' + scriptContent.substring(0, 100));
-
- // 使用更精确的正则:匹配 document.write() 中的字符串内容
- // 支持双引号和单引号,支持转义字符
- // 使用贪婪匹配 [\s\S]* 来匹配包括换行在内的所有字符
- let match = scriptContent.match(/document\.write\s*\(\s*["']([\s\S]*?)["']\s*\)/);
-
- if (match && match[1]) {
- code = match[1];
- this.logDebug('从 document.write() 提取到代码,长度: ' + code.length);
- } else {
- // 如果没有匹配到 document.write(),尝试直接提取引号内的内容
- match = scriptContent.match(/["']([\s\S]*?)["']/);
- if (match && match[1]) {
- code = match[1];
- this.logDebug('从引号内提取到代码,长度: ' + code.length);
- } else {
- // 最后的降级方案:获取除 script 标签外的文本内容
- const clonedElement = element.cloneNode(true);
- const scripts = clonedElement.querySelectorAll('script');
- scripts.forEach(script => script.remove());
- code = clonedElement.innerText || clonedElement.textContent;
- this.logDebug('使用降级方案提取代码');
- }
- }
-
- // 重要:移除 script 标签,避免重复渲染
- // WP-Markdown 会在 script 后面再输出一次代码
- scriptTag.remove();
- } else {
- // 检查是否包含块级元素或换行标签
- if (element.querySelector('br') || element.querySelector('p') || element.querySelector('div')) {
- const clonedElement = element.cloneNode(true);
- clonedElement.querySelectorAll('script').forEach(script => script.remove());
- clonedElement.querySelectorAll('br').forEach(br => br.replaceWith('\n'));
- clonedElement.querySelectorAll('p,div').forEach(node => {
- node.appendChild(document.createTextNode('\n'));
- });
- code = clonedElement.textContent || '';
- this.logDebug('检测到 HTML 结构,从 innerText 提取代码');
- } else {
- code = element.textContent || '';
- this.logDebug('未检测到 HTML 结构,从 textContent 提取代码');
- }
- }
- } else if (element.tagName === 'CODE') {
- code = element.textContent || '';
- this.logDebug('从 CODE 标签提取');
- } else if (element.tagName === 'PRE') {
- const codeElement = element.querySelector('code');
- if (codeElement) {
- code = codeElement.textContent || '';
- } else {
- code = element.textContent || '';
- }
- this.logDebug('从 PRE 标签提取');
- }
-
- // 记录原始提取的代码(调试用)
- this.logDebug('原始提取的代码长度: ' + code.length);
- this.logDebug('原始代码前100字符: ' + code.substring(0, 100));
-
- // 解码 HTML 实体(WordPress 可能会转义 HTML)
- code = code
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/&/g, '&')
- .replace(/"/g, '"')
- .replace(/'/g, "'");
-
- // 解码转义字符(必须在 HTML 实体解码之后)
- code = code
- .replace(/\\n/g, '\n')
- .replace(/\\t/g, '\t')
- .replace(/\\r/g, '\r')
- .replace(/\\"/g, '"')
- .replace(/\\'/g, "'")
- .replace(/\\\\/g, '\\');
-
- // 清理代码:移除首尾空白,但保留内部换行
- code = code.trim();
-
- // 验证代码完整性
- if (code.length < 10) {
- this.logError('提取的代码过短,可能不完整: ' + code);
- }
-
- this.logDebug('最终提取的代码长度: ' + code.length);
- this.logDebug('最终代码前200字符: ' + code.substring(0, 200) + (code.length > 200 ? '...' : ''));
-
- return code;
- },
-
- convertLegacySyntax(code) {
- let updated = code;
- if (/^\s*flowchart\b/i.test(updated)) {
- updated = updated.replace(/^\s*flowchart\b/i, 'graph');
- }
- if (/^\s*stateDiagram-v2\b/i.test(updated)) {
- updated = updated.replace(/^\s*stateDiagram-v2\b/i, 'stateDiagram');
- }
- return updated;
- },
-
- shouldRetryWithGraphSyntax(error, code, forceLegacy) {
- if (!/^\s*(flowchart|stateDiagram-v2)\b/i.test(code || '')) {
- return false;
- }
- if (forceLegacy) {
- return true;
- }
- if (error && error.hash && Array.isArray(error.hash.expected)) {
- const expected = error.hash.expected.join(' ');
- const tokenText = String(error.hash.text || '').toLowerCase();
- if (expected.includes('GRAPH') && tokenText === 'flowchart') {
- return true;
- }
- if ((expected.includes('STATE') || expected.includes('SD')) && tokenText === 'statediagram-v2') {
- return true;
- }
- }
- const message = String(error && error.message ? error.message : '').toLowerCase();
- return message.includes('graph') || message.includes('state');
- },
-
- needsModernMermaid(code) {
- return /^\s*(flowchart|erDiagram|stateDiagram|stateDiagram-v2)\b/i.test(code || '');
- },
-
- tryUpgradeMermaidLibrary(element, code) {
- if (this.compatibilityFallbackAttempted) {
- return false;
- }
- if (!this.needsModernMermaid(code)) {
- return false;
- }
- if (typeof window.argonMermaidLoadFallback !== 'function') {
- return false;
- }
- this.compatibilityFallbackAttempted = true;
- const container = document.createElement('div');
- container.className = 'mermaid';
- container.textContent = code;
- if (element && element.parentNode) {
- element.parentNode.replaceChild(container, element);
- }
- window.argonMermaidLoadFallback();
- return true;
- },
-
- // ---------- 渲染引擎 ----------
-
- /**
- * 批量渲染所有 Mermaid 图表
- * 需求 2.6: 清除缓存后首次加载时等待 Mermaid 库完全加载
- * 需求 4.1: 开始渲染前检查 Mermaid 库是否已加载
- * 需求 18.1: 使用批量渲染,避免阻塞主线程
- * 需求 18.4: 延迟渲染视口外的图表,优先渲染可见图表
- * @returns {Promise}
- */
- async renderAllCharts() {
- // 需求 4.1-4.2: 等待 Mermaid 库加载
- if (!await this.waitForMermaid()) {
- this.logError('Mermaid 库加载失败,无法渲染图表');
- return;
- }
-
- if (!this.initialized) {
- this.logDebug('Mermaid 未初始化,跳过渲染');
- return;
- }
-
- const startRender = (blocks) => {
- this.logDebug(`准备渲染 ${blocks.length} 个图表 (Lazy Load)`);
- if (this.observer) {
- this.observer.disconnect();
- }
- this.observer = new IntersectionObserver((entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- const block = entry.target;
- const index = blocks.indexOf(block);
- requestAnimationFrame(() => {
- this.renderChart(block, index);
- });
- observer.unobserve(block);
- }
- });
- }, {
- rootMargin: '200px 0px',
- threshold: 0.01
- });
- blocks.forEach(block => {
- this.observer.observe(block);
- });
- };
-
- if (this.domObserver) {
- this.domObserver.disconnect();
- this.domObserver = null;
- }
-
- const blocks = this.detectMermaidBlocks();
- if (blocks.length === 0) {
- this.logDebug('未找到 Mermaid 代码块');
- if (typeof MutationObserver === 'undefined') {
- return;
- }
- let finished = false;
- const waitStart = Date.now();
- const stopWaiting = (message) => {
- if (finished) {
- return;
- }
- finished = true;
- if (this.domObserver) {
- this.domObserver.disconnect();
- this.domObserver = null;
- }
- if (message) {
- this.logDebug(message);
- }
- };
- const tryRender = () => {
- const pending = this.detectMermaidBlocks();
- if (pending.length > 0) {
- stopWaiting();
- startRender(pending);
- return true;
- }
- return false;
- };
- const maxWait = 4000;
- this.domObserver = new MutationObserver(() => {
- if (tryRender()) {
- return;
- }
- if (Date.now() - waitStart > maxWait) {
- stopWaiting('等待 Mermaid 代码块超时');
- }
- });
- this.domObserver.observe(document.body, {
- childList: true,
- subtree: true
- });
- setTimeout(() => {
- if (!finished) {
- if (!tryRender()) {
- stopWaiting('等待 Mermaid 代码块超时');
- }
- }
- }, maxWait);
- return;
- }
-
- startRender(blocks);
- },
-
- /**
- * 渲染单个图表
- * 需求 18.5: 图表渲染失败不阻塞其他图表的渲染
- * @param {HTMLElement} element - 代码块元素
- * @param {number} index - 图表索引
- */
- renderChart(element, index) {
- const chartId = `mermaid-chart-${Date.now()}-${index}`;
-
- // 需求 18.5: 使用 try-catch 包裹整个渲染过程
- try {
- // 检查是否已经是错误容器(避免重复处理错误)
- if (element.classList && element.classList.contains('mermaid-error-container')) {
- this.logDebug(`元素已经是错误容器,跳过: ${chartId}`);
- return;
- }
-
- // 检查是否已经是渲染成功的容器(避免重复渲染)
- if (element.classList && element.classList.contains('mermaid-container') && element.dataset.mermaidCode) {
- this.logDebug(`图表已成功渲染,跳过: ${chartId}`);
- return;
- }
-
- // 检查是否已渲染(避免重复渲染)
- if (this.rendered.has(element)) {
- this.logDebug(`图表已渲染,跳过: ${chartId}`);
- return;
- }
-
- // 提取代码
- const rawCode = this.extractMermaidCode(element);
- const code = rawCode;
-
- if (!code) {
- this.logDebug(`代码块为空,跳过: ${chartId}`);
- return;
- }
-
- this.logDebug(`准备渲染图表: ${chartId}, 代码长度: ${code.length}`);
- this.logDebug(`代码内容: ${code.substring(0, 200)}`);
-
- // 需求 18.2: 创建加载动画容器
- const loadingContainer = document.createElement('div');
- loadingContainer.className = 'mermaid-loading';
- loadingContainer.innerHTML = `
-
- 正在渲染图表...
- `;
-
- // 替换原始元素为加载动画
- if (element.parentNode) {
- element.parentNode.replaceChild(loadingContainer, element);
- }
-
- // 创建最终容器
- const container = document.createElement('div');
- container.className = 'mermaid-container';
- container.id = chartId;
-
- // 检查 Mermaid API
- if (typeof window.mermaid === 'undefined') {
- this.logError('Mermaid 库未加载');
- this.handleRenderError(loadingContainer, new Error('Mermaid 库未加载'), code);
- return;
- }
-
- if (typeof window.mermaid.render !== 'function') {
- this.logError('Mermaid.render 方法不存在');
- this.handleRenderError(loadingContainer, new Error('Mermaid.render 方法不存在'), code);
- return;
- }
-
- const onRenderSuccess = (svg, bindFunctions) => {
- container.innerHTML = svg;
- container.dataset.mermaidCode = code;
- container.dataset.currentTheme = this.getMermaidTheme();
- container.setAttribute('data-mermaid-rendered', 'true');
- if (loadingContainer.parentNode) {
- loadingContainer.parentNode.replaceChild(container, loadingContainer);
- }
- this.rendered.add(container);
- this.applyStyles(container);
- if (typeof bindFunctions === 'function') {
- try {
- bindFunctions(container);
- } catch (bindError) {
- this.logError('Mermaid bindFunctions 执行失败', bindError);
- }
- }
- this.logDebug(`图表渲染成功: ${chartId}`);
- };
-
- const renderResult = window.mermaid.render(`mermaid-svg-${chartId}`, code);
-
- if (renderResult && typeof renderResult.then === 'function') {
- renderResult.then(result => {
- onRenderSuccess(result.svg, result.bindFunctions);
- }).catch(error => {
- // 需求 18.5: 渲染失败时记录错误但不抛出异常
- const retryCode = this.convertLegacySyntax(code);
- if (this.shouldRetryWithGraphSyntax(error, code) && retryCode !== code) {
- this.logDebug('语法降级后重试渲染');
- this.renderChartLegacy(loadingContainer, retryCode, container, chartId);
- return;
- }
- if (this.tryUpgradeMermaidLibrary(loadingContainer, code)) {
- return;
- }
- this.logError(`图表渲染失败: ${chartId}`, error);
- this.handleRenderError(loadingContainer, error, rawCode);
- });
- return;
- }
-
- if (renderResult && typeof renderResult === 'object' && typeof renderResult.svg === 'string') {
- onRenderSuccess(renderResult.svg, renderResult.bindFunctions);
- return;
- }
-
- if (typeof renderResult === 'string') {
- onRenderSuccess(renderResult);
- return;
- }
-
- this.logError('Mermaid.render 没有返回 Promise,可能是版本不兼容');
- if (this.tryUpgradeMermaidLibrary(loadingContainer, code)) {
- return;
- }
- const legacyCode = this.convertLegacySyntax(code);
- this.renderChartLegacy(loadingContainer, legacyCode, container, chartId);
- return;
-
- } catch (error) {
- // 需求 18.5: 捕获所有异常,确保不影响其他图表
- this.logError(`渲染异常: ${chartId}`, error);
-
- // 尝试显示错误信息
- try {
- this.handleRenderError(element, error, '');
- } catch (e) {
- // 如果错误处理也失败,只记录日志
- this.logError('错误处理失败', e);
- }
- }
- },
-
- /**
- * 使用旧版 Mermaid API 渲染(兼容 Mermaid 8.x)
- * @param {HTMLElement} element - 原始代码块元素
- * @param {string} code - Mermaid 代码
- * @param {HTMLElement} container - 容器元素
- * @param {string} chartId - 图表 ID
- */
- renderChartLegacy(element, code, container, chartId) {
- try {
- this.logDebug('尝试使用旧版 Mermaid API');
-
- // Mermaid 8.x 使用 mermaidAPI.render
- if (typeof window.mermaidAPI !== 'undefined' && typeof window.mermaidAPI.render === 'function') {
- window.mermaidAPI.render(`mermaid-svg-${chartId}`, code, (svgCode) => {
- container.innerHTML = svgCode;
- container.dataset.mermaidCode = code;
- container.dataset.currentTheme = this.getMermaidTheme();
-
- // 需求 3.5: 添加渲染状态标记,避免重复渲染
- container.setAttribute('data-mermaid-rendered', 'true');
-
- element.parentNode.replaceChild(container, element);
- this.rendered.add(container);
- this.applyStyles(container);
- this.logDebug(`图表渲染成功(旧版 API): ${chartId}`);
- });
- } else {
- // 最后的降级方案:直接使用 mermaid.init()
- container.className = 'mermaid';
- container.textContent = code;
- element.parentNode.replaceChild(container, element);
-
- if (typeof window.mermaid.init === 'function') {
- window.mermaid.init(undefined, container);
- this.rendered.add(container);
-
- // 需求 3.5: 添加渲染状态标记,避免重复渲染
- container.setAttribute('data-mermaid-rendered', 'true');
-
- this.logDebug(`图表渲染成功(init 方法): ${chartId}`);
- } else {
- this.handleRenderError(element, new Error('无可用的 Mermaid 渲染方法'), code);
- }
- }
- } catch (error) {
- this.logError('旧版 API 渲染失败', error);
- if (this.tryUpgradeMermaidLibrary(element, code)) {
- return;
- }
- this.handleRenderError(element, error, code);
- }
- },
-
- /**
- * 处理渲染错误
- * @param {HTMLElement} element - 原始代码块元素
- * @param {Error} error - 错误对象
- * @param {string} code - 原始代码
- */
- handleRenderError(element, error, code) {
- this.logError('图表渲染失败', error);
-
- // 如果元素已经是错误容器,避免重复嵌套
- if (element.classList && element.classList.contains('mermaid-error-container')) {
- this.logDebug('元素已经是错误容器,跳过重复处理');
- return;
- }
-
- // 提取原始代码(优先使用传入的 code,其次从元素中提取)
- let originalCode = code;
- if (!originalCode) {
- // 尝试从不同类型的元素中提取代码
- if (element.dataset && element.dataset.mermaidCode) {
- originalCode = element.dataset.mermaidCode;
- } else if (element.textContent) {
- originalCode = element.textContent.trim();
- }
- }
-
- // 创建错误提示容器
- const errorContainer = document.createElement('div');
- errorContainer.className = 'mermaid-error-container';
- errorContainer.dataset.errorHandled = 'true'; // 标记已处理
-
- // 提取错误信息
- const errorMessage = error.message || '未知错误';
- const errorType = this.getErrorType(errorMessage);
- const errorLine = this.extractErrorLine(errorMessage);
-
- // 创建错误显示结构
- const errorHeader = document.createElement('div');
- errorHeader.className = 'mermaid-error-header';
- errorHeader.innerHTML = `
-
- Mermaid 图表渲染失败
- `;
-
- const errorBody = document.createElement('div');
- errorBody.className = 'mermaid-error-body';
-
- let errorBodyHTML = `
- 错误类型: ${errorType}
-
- `;
-
- // 如果提取到错误行号,显示它
- if (errorLine) {
- errorBodyHTML += `错误位置: 第 ${errorLine} 行
`;
- }
-
- errorBody.innerHTML = errorBodyHTML;
-
- // 创建代码查看区域
- const codeDetails = document.createElement('details');
- codeDetails.className = 'mermaid-error-code';
-
- const codeSummary = document.createElement('summary');
- codeSummary.textContent = '📄 查看原始代码';
-
- const codeBlock = document.createElement('pre');
- const codeElement = document.createElement('code');
- codeElement.className = 'language-mermaid';
- codeElement.textContent = originalCode || '(无法提取代码)';
-
- codeBlock.appendChild(codeElement);
- codeDetails.appendChild(codeSummary);
- codeDetails.appendChild(codeBlock);
-
- // 组装错误容器
- errorContainer.appendChild(errorHeader);
- errorContainer.appendChild(errorBody);
- errorContainer.appendChild(codeDetails);
-
- // 替换原始代码块
- if (element.parentNode) {
- element.parentNode.replaceChild(errorContainer, element);
- }
- },
-
- /**
- * 获取错误类型
- * @param {string} errorMessage - 错误信息
- * @returns {string} 错误类型
- */
- getErrorType(errorMessage) {
- if (errorMessage.includes('Syntax') || errorMessage.includes('Parse')) {
- return '语法错误';
- } else if (errorMessage.includes('Lexical')) {
- return '词法错误';
- } else if (errorMessage.includes('Expecting')) {
- return '格式错误';
- } else if (errorMessage.includes('Unknown')) {
- return '未知图表类型';
- }
- return '渲染错误';
- },
-
- /**
- * 从错误信息中提取行号
- * @param {string} errorMessage - 错误信息
- * @returns {string|null} 行号或 null
- */
- extractErrorLine(errorMessage) {
- // 尝试匹配常见的行号格式
- const linePatterns = [
- /line (\d+)/i,
- /at line (\d+)/i,
- /on line (\d+)/i,
- /第 (\d+) 行/,
- ];
-
- for (const pattern of linePatterns) {
- const match = errorMessage.match(pattern);
- if (match && match[1]) {
- return match[1];
- }
- }
-
- return null;
- },
-
- /**
- * HTML 转义
- * @param {string} text - 要转义的文本
- * @returns {string} 转义后的文本
- */
- escapeHtml(text) {
- const div = document.createElement('div');
- div.textContent = text;
- return div.innerHTML;
- },
-
- // ---------- 样式增强 ----------
-
- /**
- * 应用容器样式并添加缩放控制
- * 需求 18.3: 使用淡入动画,提升视觉体验
- * @param {HTMLElement} container - 图表容器
- */
- applyStyles(container) {
- // 需求 18.3: 添加淡入动画
- container.style.opacity = '0';
-
- // 使用 requestAnimationFrame 确保动画流畅
- requestAnimationFrame(() => {
- container.style.transition = 'opacity 0.4s ease-in';
- container.style.opacity = '1';
-
- // 动画完成后清理过渡样式
- setTimeout(() => {
- container.style.transition = '';
- }, 400);
- });
-
- // 确保 SVG 响应式
- const svg = container.querySelector('svg');
- if (svg) {
- svg.style.width = '100%';
- svg.style.height = 'auto';
- }
-
- // 添加缩放功能
- this.addZoomControls(container);
- },
-
- /**
- * 添加缩放控制按钮
- * @param {HTMLElement} container - 图表容器
- */
- addZoomControls(container) {
- // 创建内部容器包裹 SVG
- const svg = container.querySelector('svg');
- if (!svg) return;
-
- // 创建内部容器
- const inner = document.createElement('div');
- inner.className = 'mermaid-container-inner';
-
- // 将 SVG 移到内部容器
- container.appendChild(inner);
- inner.appendChild(svg);
-
- // 创建缩放控制
- // 需求 14.1: 添加全屏按钮
- // 需求 15.1: 添加导出按钮和菜单
- const controls = document.createElement('div');
- controls.className = 'mermaid-zoom-controls';
- controls.innerHTML = `
-
- 100%
-
-
-
-
- `;
-
- container.appendChild(controls);
-
- // 需求 15.1: 创建导出菜单
- const exportMenu = document.createElement('div');
- exportMenu.className = 'mermaid-export-menu';
- exportMenu.innerHTML = `
-
-
- `;
- container.appendChild(exportMenu);
-
- // 创建提示文本
- const hint = document.createElement('div');
- hint.className = 'mermaid-hint';
- hint.textContent = '按住 Ctrl+滚轮缩放 | 拖拽移动';
- container.appendChild(hint);
-
- // 缩放状态
- let scale = 1;
- const minScale = 0.5;
- const maxScale = 3;
- const step = 0.25;
-
- // 更新缩放显示
- // 需求 12.2, 12.3: 使用 CSS transform 实现硬件加速和平滑过渡
- // 需求 12.4, 20.3: 更新按钮状态
- const updateZoom = (useTransition = true) => {
- // 控制是否使用过渡动画
- if (!useTransition) {
- inner.style.transition = 'none';
- } else {
- inner.style.transition = 'transform 0.3s ease';
- }
-
- inner.style.transform = `scale(${scale})`;
- controls.querySelector('.mermaid-zoom-level').textContent = Math.round(scale * 100) + '%';
-
- // 更新按钮状态
- const zoomInBtn = controls.querySelector('[data-action="zoom-in"]');
- const zoomOutBtn = controls.querySelector('[data-action="zoom-out"]');
-
- // 禁用/启用放大按钮
- if (scale >= maxScale) {
- zoomInBtn.disabled = true;
- zoomInBtn.style.opacity = '0.4';
- zoomInBtn.style.cursor = 'not-allowed';
- } else {
- zoomInBtn.disabled = false;
- zoomInBtn.style.opacity = '1';
- zoomInBtn.style.cursor = 'pointer';
- }
-
- // 禁用/启用缩小按钮
- if (scale <= minScale) {
- zoomOutBtn.disabled = true;
- zoomOutBtn.style.opacity = '0.4';
- zoomOutBtn.style.cursor = 'not-allowed';
- } else {
- zoomOutBtn.disabled = false;
- zoomOutBtn.style.opacity = '1';
- zoomOutBtn.style.cursor = 'pointer';
- }
-
- // 恢复过渡(用于下次按钮点击)
- if (!useTransition) {
- requestAnimationFrame(() => {
- inner.style.transition = 'transform 0.3s ease';
- });
- }
- };
-
- // 绑定按钮事件
- // 需求 14.1, 14.2, 14.3: 全屏按钮功能
- // 需求 15.1: 导出按钮功能
- controls.addEventListener('click', (e) => {
- const btn = e.target.closest('.mermaid-zoom-btn');
- if (!btn || btn.disabled) return; // 忽略禁用的按钮
-
- const action = btn.dataset.action;
-
- if (action === 'zoom-in' && scale < maxScale) {
- scale = Math.min(scale + step, maxScale);
- updateZoom(true); // 按钮点击使用平滑过渡
- } else if (action === 'zoom-out' && scale > minScale) {
- scale = Math.max(scale - step, minScale);
- updateZoom(true); // 按钮点击使用平滑过渡
- } else if (action === 'zoom-reset') {
- scale = 1;
- updateZoom(true); // 重置使用平滑过渡
- } else if (action === 'fullscreen') {
- // 需求 14.1: 点击全屏按钮
- toggleFullscreen();
- } else if (action === 'export') {
- // 需求 15.1: 点击导出按钮,显示/隐藏导出菜单
- toggleExportMenu();
- }
- });
-
- // ==========================================================================
- // 需求 15: Mermaid 导出功能
- // ==========================================================================
-
- /**
- * 切换导出菜单显示/隐藏
- * 需求 15.1: 点击导出按钮显示导出选项
- */
- const toggleExportMenu = () => {
- const isVisible = exportMenu.classList.contains('visible');
- if (isVisible) {
- exportMenu.classList.remove('visible');
- } else {
- exportMenu.classList.add('visible');
- }
- };
-
- // 点击导出选项
- exportMenu.addEventListener('click', (e) => {
- const option = e.target.closest('.mermaid-export-option');
- if (!option) return;
-
- const format = option.dataset.format;
- exportMenu.classList.remove('visible');
-
- if (format === 'png') {
- exportAsPNG();
- } else if (format === 'svg') {
- exportAsSVG();
- }
- });
-
- // 点击容器外部关闭导出菜单
- document.addEventListener('click', (e) => {
- if (!container.contains(e.target)) {
- exportMenu.classList.remove('visible');
- }
- });
-
- /**
- * 导出为 PNG
- * 需求 15.2: 将图表转换为 PNG 图片并下载
- * 需求 15.4: 保持图表当前的缩放级别和样式
- */
- const exportAsPNG = async () => {
- try {
- const svgElement = inner.querySelector('svg');
- if (!svgElement) {
- throw new Error('未找到 SVG 元素');
- }
-
- // 克隆 SVG 以避免修改原始元素
- const clonedSvg = svgElement.cloneNode(true);
-
- // 需求 15.4: 应用当前缩放级别
- const svgWidth = svgElement.getBoundingClientRect().width * scale;
- const svgHeight = svgElement.getBoundingClientRect().height * scale;
-
- clonedSvg.setAttribute('width', svgWidth);
- clonedSvg.setAttribute('height', svgHeight);
-
- // 将 SVG 转换为 data URL
- const svgData = new XMLSerializer().serializeToString(clonedSvg);
- const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
- const svgUrl = URL.createObjectURL(svgBlob);
-
- // 创建图片元素
- const img = new Image();
- img.onload = () => {
- // 创建 canvas
- const canvas = document.createElement('canvas');
- canvas.width = svgWidth;
- canvas.height = svgHeight;
-
- const ctx = canvas.getContext('2d');
-
- // 设置白色背景(PNG 默认透明)
- ctx.fillStyle = '#ffffff';
- ctx.fillRect(0, 0, canvas.width, canvas.height);
-
- // 绘制图片
- ctx.drawImage(img, 0, 0, svgWidth, svgHeight);
-
- // 转换为 PNG 并下载
- canvas.toBlob((blob) => {
- if (blob) {
- downloadFile(blob, 'mermaid-chart.png');
- }
- URL.revokeObjectURL(svgUrl);
- }, 'image/png');
- };
-
- // 需求 15.5: 添加导出错误处理
- img.onerror = () => {
- URL.revokeObjectURL(svgUrl);
- throw new Error('图片加载失败');
- };
-
- img.src = svgUrl;
-
- } catch (error) {
- // 需求 15.5: 显示友好的错误提示
- handleExportError('PNG', error);
- }
- };
-
- /**
- * 导出为 SVG
- * 需求 15.3: 将 SVG 代码保存为文件并下载
- * 需求 15.4: 保持图表当前的缩放级别和样式
- */
- const exportAsSVG = () => {
- try {
- const svgElement = inner.querySelector('svg');
- if (!svgElement) {
- throw new Error('未找到 SVG 元素');
- }
-
- // 克隆 SVG
- const clonedSvg = svgElement.cloneNode(true);
-
- // 需求 15.4: 应用当前缩放级别
- if (scale !== 1) {
- const svgWidth = svgElement.getBoundingClientRect().width * scale;
- const svgHeight = svgElement.getBoundingClientRect().height * scale;
-
- clonedSvg.setAttribute('width', svgWidth);
- clonedSvg.setAttribute('height', svgHeight);
- }
-
- // 添加 XML 声明和命名空间
- const svgData = new XMLSerializer().serializeToString(clonedSvg);
- const svgWithDeclaration = '\n' + svgData;
-
- // 创建 Blob 并下载
- const blob = new Blob([svgWithDeclaration], { type: 'image/svg+xml;charset=utf-8' });
- downloadFile(blob, 'mermaid-chart.svg');
-
- } catch (error) {
- // 需求 15.5: 显示友好的错误提示
- handleExportError('SVG', error);
- }
- };
-
- /**
- * 下载文件
- * @param {Blob} blob - 文件内容
- * @param {string} filename - 文件名
- */
- const downloadFile = (blob, filename) => {
- const url = URL.createObjectURL(blob);
- const link = document.createElement('a');
- link.href = url;
- link.download = filename;
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- URL.revokeObjectURL(url);
- };
-
- /**
- * 处理导出错误
- * 需求 15.5: 显示友好的错误提示
- * @param {string} format - 导出格式
- * @param {Error} error - 错误对象
- */
- const handleExportError = (format, error) => {
- console.error(`导出 ${format} 失败:`, error);
-
- // 显示错误提示
- const errorMsg = document.createElement('div');
- errorMsg.className = 'mermaid-export-error';
- errorMsg.textContent = `导出 ${format} 失败: ${error.message}`;
- container.appendChild(errorMsg);
-
- // 3秒后自动移除错误提示
- setTimeout(() => {
- if (errorMsg.parentNode) {
- errorMsg.parentNode.removeChild(errorMsg);
- }
- }, 3000);
- };
-
- // 鼠标滚轮缩放(按住 Ctrl)
- // 需求 12.1: 以鼠标为中心进行缩放
- container.addEventListener('wheel', (e) => {
- if (e.ctrlKey || e.metaKey) {
- e.preventDefault();
-
- const delta = e.deltaY > 0 ? -step : step;
- const newScale = Math.max(minScale, Math.min(maxScale, scale + delta));
-
- if (newScale !== scale) {
- // 获取鼠标相对于容器的位置
- const rect = container.getBoundingClientRect();
- const mouseX = e.clientX - rect.left;
- const mouseY = e.clientY - rect.top;
-
- // 计算鼠标相对于内容的位置(缩放前)
- const scrollLeft = container.scrollLeft;
- const scrollTop = container.scrollTop;
- const offsetX = (scrollLeft + mouseX) / scale;
- const offsetY = (scrollTop + mouseY) / scale;
-
- // 更新缩放(滚轮缩放不使用过渡动画,更流畅)
- scale = newScale;
- updateZoom(false);
-
- // 调整滚动位置,使鼠标位置保持不变
- container.scrollLeft = offsetX * scale - mouseX;
- container.scrollTop = offsetY * scale - mouseY;
- }
- }
- }, { passive: false });
-
- // 拖拽功能
- let isDragging = false;
- let startX = 0;
- let startY = 0;
- let scrollLeft = 0;
- let scrollTop = 0;
- let rafId = null;
- let pendingX = 0;
- let pendingY = 0;
-
- // 需求 13.5: 智能启用拖拽 - 检查图表是否需要拖拽
- const checkDragEnabled = () => {
- const svgElement = inner.querySelector('svg');
- if (!svgElement) return false;
-
- // 如果图表未缩放且完全可见,禁用拖拽
- const containerRect = container.getBoundingClientRect();
- const svgRect = svgElement.getBoundingClientRect();
- const isFullyVisible = svgRect.width <= containerRect.width &&
- svgRect.height <= containerRect.height;
- const isNotZoomed = Math.abs(scale - 1) < 0.01;
-
- return !(isFullyVisible && isNotZoomed);
- };
-
- // 需求 13.3: 使用 requestAnimationFrame 优化拖拽性能
- const updateDragPosition = () => {
- if (!isDragging) {
- rafId = null;
- return;
- }
-
- inner.scrollLeft = scrollLeft - pendingX;
- inner.scrollTop = scrollTop - pendingY;
-
- rafId = requestAnimationFrame(updateDragPosition);
- };
-
- inner.addEventListener('mousedown', (e) => {
- // 只响应左键
- if (e.button !== 0) return;
-
- // 需求 13.5: 检查是否需要启用拖拽
- if (!checkDragEnabled()) return;
-
- isDragging = true;
- // 需求 13.1: 改变鼠标光标为抓手样式
- inner.classList.add('dragging');
-
- startX = e.pageX - inner.offsetLeft;
- startY = e.pageY - inner.offsetTop;
- scrollLeft = inner.scrollLeft;
- scrollTop = inner.scrollTop;
-
- // 需求 13.2: 禁用文本选择
- e.preventDefault();
-
- // 启动 RAF 循环
- if (!rafId) {
- rafId = requestAnimationFrame(updateDragPosition);
- }
- });
-
- document.addEventListener('mousemove', (e) => {
- if (!isDragging) return;
-
- e.preventDefault();
-
- const x = e.pageX - inner.offsetLeft;
- const y = e.pageY - inner.offsetTop;
- // 更新待处理的位置,由 RAF 循环处理
- pendingX = (x - startX) * 2;
- pendingY = (y - startY) * 2;
- });
-
- document.addEventListener('mouseup', () => {
- if (isDragging) {
- isDragging = false;
- // 需求 13.4: 恢复正常光标
- inner.classList.remove('dragging');
- // 取消 RAF 循环
- if (rafId) {
- cancelAnimationFrame(rafId);
- rafId = null;
- }
- }
- });
-
- // 防止拖拽时选中文本
- inner.addEventListener('dragstart', (e) => {
- e.preventDefault();
- });
-
- // 需求 13.2: 禁用文本选择
- inner.addEventListener('selectstart', (e) => {
- if (isDragging) {
- e.preventDefault();
- }
- });
-
- // 需求 12.5: 双击智能缩放
- // 双击图表时,如果当前是默认大小则放大到 2 倍,否则重置到 1 倍
- inner.addEventListener('dblclick', (e) => {
- e.preventDefault();
-
- // 获取双击位置
- const rect = container.getBoundingClientRect();
- const mouseX = e.clientX - rect.left;
- const mouseY = e.clientY - rect.top;
-
- // 计算鼠标相对于内容的位置(缩放前)
- const scrollLeft = container.scrollLeft;
- const scrollTop = container.scrollTop;
- const offsetX = (scrollLeft + mouseX) / scale;
- const offsetY = (scrollTop + mouseY) / scale;
-
- // 智能缩放:如果接近默认大小则放大,否则重置
- const targetScale = Math.abs(scale - 1) < 0.1 ? 2 : 1;
- scale = targetScale;
- updateZoom(true); // 使用平滑过渡
-
- // 调整滚动位置,使双击位置保持在视野中心
- setTimeout(() => {
- container.scrollLeft = offsetX * scale - mouseX;
- container.scrollTop = offsetY * scale - mouseY;
- }, 50);
- });
-
- // ==========================================================================
- // 需求 14: Mermaid 全屏模式
- // ==========================================================================
-
- // 全屏状态
- let isFullscreen = false;
- let savedScale = 1;
- let savedScrollLeft = 0;
- let savedScrollTop = 0;
-
- /**
- * 切换全屏模式
- * 需求 14.1: 点击全屏按钮将图表全屏显示
- * 需求 14.2, 14.3: 全屏模式下保持缩放和拖拽功能可用
- * 需求 14.5: 退出全屏恢复图表原始状态
- */
- const toggleFullscreen = () => {
- if (!isFullscreen) {
- // 进入全屏
- enterFullscreen();
- } else {
- // 退出全屏
- exitFullscreen();
- }
- };
-
- /**
- * 进入全屏模式
- */
- const enterFullscreen = () => {
- // 保存当前状态
- savedScale = scale;
- savedScrollLeft = container.scrollLeft;
- savedScrollTop = container.scrollTop;
-
- // 添加全屏类
- container.classList.add('mermaid-fullscreen');
- isFullscreen = true;
-
- // 更新全屏按钮
- const fullscreenBtn = controls.querySelector('[data-action="fullscreen"]');
- if (fullscreenBtn) {
- fullscreenBtn.textContent = '⛶';
- fullscreenBtn.title = '退出全屏';
- fullscreenBtn.classList.add('active');
- }
-
- // 需求 14.3: 全屏模式下显示退出全屏按钮(已在工具栏中)
- // 工具栏在全屏模式下仍然可见
-
- // 禁用页面滚动
- document.body.style.overflow = 'hidden';
-
- this.logDebug('进入全屏模式');
- };
-
- /**
- * 退出全屏模式
- * 需求 14.5: 恢复图表原始状态(缩放级别和位置)
- */
- const exitFullscreen = () => {
- // 移除全屏类
- container.classList.remove('mermaid-fullscreen');
- isFullscreen = false;
-
- // 更新全屏按钮
- const fullscreenBtn = controls.querySelector('[data-action="fullscreen"]');
- if (fullscreenBtn) {
- fullscreenBtn.textContent = '⛶';
- fullscreenBtn.title = '全屏';
- fullscreenBtn.classList.remove('active');
- }
-
- // 恢复原始状态
- scale = savedScale;
- updateZoom(false);
-
- // 恢复滚动位置
- setTimeout(() => {
- container.scrollLeft = savedScrollLeft;
- container.scrollTop = savedScrollTop;
- }, 50);
-
- // 恢复页面滚动
- document.body.style.overflow = '';
-
- this.logDebug('退出全屏模式');
- };
-
- // 需求 14.4: 按 ESC 键退出全屏模式
- const handleEscKey = (e) => {
- if (e.key === 'Escape' && isFullscreen) {
- exitFullscreen();
- }
- };
-
- // 绑定 ESC 键事件
- document.addEventListener('keydown', handleEscKey);
-
- // 清理函数(在容器销毁时调用)
- container.dataset.cleanupFullscreen = 'registered';
- container._fullscreenCleanup = () => {
- document.removeEventListener('keydown', handleEscKey);
- if (isFullscreen) {
- exitFullscreen();
- }
- };
-
- // ==========================================================================
- // 需求 16.2-16.4: 移动端触摸手势支持
- // ==========================================================================
-
- // 触摸状态
- let touchStartDistance = 0;
- let touchStartScale = 1;
- let touchStartX = 0;
- let touchStartY = 0;
- let isTouchDragging = false;
- let touchScrollLeft = 0;
- let touchScrollTop = 0;
-
- /**
- * 计算两个触摸点之间的距离
- * @param {Touch} touch1 - 第一个触摸点
- * @param {Touch} touch2 - 第二个触摸点
- * @returns {number} 距离
- */
- const getTouchDistance = (touch1, touch2) => {
- const dx = touch2.clientX - touch1.clientX;
- const dy = touch2.clientY - touch1.clientY;
- return Math.sqrt(dx * dx + dy * dy);
- };
-
- /**
- * 获取两个触摸点的中心位置
- * @param {Touch} touch1 - 第一个触摸点
- * @param {Touch} touch2 - 第二个触摸点
- * @returns {Object} 中心位置 {x, y}
- */
- const getTouchCenter = (touch1, touch2) => {
- return {
- x: (touch1.clientX + touch2.clientX) / 2,
- y: (touch1.clientY + touch2.clientY) / 2
- };
- };
-
- // 需求 16.2: 双指缩放手势
- inner.addEventListener('touchstart', (e) => {
- if (e.touches.length === 2) {
- // 双指触摸 - 缩放模式
- e.preventDefault();
-
- touchStartDistance = getTouchDistance(e.touches[0], e.touches[1]);
- touchStartScale = scale;
-
- // 记录缩放中心点
- const center = getTouchCenter(e.touches[0], e.touches[1]);
- const rect = container.getBoundingClientRect();
- touchStartX = center.x - rect.left;
- touchStartY = center.y - rect.top;
-
- } else if (e.touches.length === 1) {
- // 需求 16.3: 单指拖拽移动
- // 检查是否需要启用拖拽
- if (!checkDragEnabled()) return;
-
- isTouchDragging = true;
- inner.classList.add('dragging');
-
- const touch = e.touches[0];
- touchStartX = touch.clientX;
- touchStartY = touch.clientY;
- touchScrollLeft = container.scrollLeft;
- touchScrollTop = container.scrollTop;
- }
- }, { passive: false });
-
- // 需求 16.4: 优化触摸事件响应速度
- inner.addEventListener('touchmove', (e) => {
- if (e.touches.length === 2) {
- // 双指缩放
- e.preventDefault();
-
- const currentDistance = getTouchDistance(e.touches[0], e.touches[1]);
- const scaleChange = currentDistance / touchStartDistance;
- const newScale = Math.max(minScale, Math.min(maxScale, touchStartScale * scaleChange));
-
- if (newScale !== scale) {
- // 获取缩放中心点
- const center = getTouchCenter(e.touches[0], e.touches[1]);
- const rect = container.getBoundingClientRect();
- const centerX = center.x - rect.left;
- const centerY = center.y - rect.top;
-
- // 计算缩放前的内容位置
- const scrollLeft = container.scrollLeft;
- const scrollTop = container.scrollTop;
- const offsetX = (scrollLeft + centerX) / scale;
- const offsetY = (scrollTop + centerY) / scale;
-
- // 更新缩放(不使用过渡动画,更流畅)
- scale = newScale;
- updateZoom(false);
-
- // 调整滚动位置,使缩放中心保持不变
- requestAnimationFrame(() => {
- container.scrollLeft = offsetX * scale - centerX;
- container.scrollTop = offsetY * scale - centerY;
- });
- }
-
- } else if (e.touches.length === 1 && isTouchDragging) {
- // 单指拖拽
- e.preventDefault();
-
- const touch = e.touches[0];
- const deltaX = touch.clientX - touchStartX;
- const deltaY = touch.clientY - touchStartY;
-
- // 使用 requestAnimationFrame 优化性能
- requestAnimationFrame(() => {
- container.scrollLeft = touchScrollLeft - deltaX;
- container.scrollTop = touchScrollTop - deltaY;
- });
- }
- }, { passive: false });
-
- inner.addEventListener('touchend', (e) => {
- if (isTouchDragging) {
- isTouchDragging = false;
- inner.classList.remove('dragging');
- }
-
- // 重置触摸状态
- if (e.touches.length === 0) {
- touchStartDistance = 0;
- touchStartScale = 1;
- }
- }, { passive: true });
-
- inner.addEventListener('touchcancel', () => {
- if (isTouchDragging) {
- isTouchDragging = false;
- inner.classList.remove('dragging');
- }
- touchStartDistance = 0;
- touchStartScale = 1;
- }, { passive: true });
- },
-
- // ---------- 主题切换监听 ----------
-
- /**
- * 监听主题切换事件
- */
- listenThemeSwitch() {
- // 如果配置了固定主题,不需要监听切换
- if (this.config && this.config.theme !== 'auto') {
- return;
- }
-
- // 监听 Argon 主题切换事件
- document.addEventListener('argon:theme-switched', () => {
- this.reRenderCharts();
- });
-
- // 使用 MutationObserver 监听 darkmode class 变化
- const observer = new MutationObserver((mutations) => {
- mutations.forEach((mutation) => {
- if (mutation.attributeName === 'class') {
- const isDarkMode = document.documentElement.classList.contains('darkmode');
- const currentTheme = isDarkMode ? 'dark' : 'default';
-
- // 检查主题是否真的改变了
- const firstChart = document.querySelector('.mermaid-container');
- if (firstChart && firstChart.dataset.currentTheme !== currentTheme) {
- this.reRenderCharts();
- }
- }
- });
- });
-
- observer.observe(document.documentElement, {
- attributes: true,
- attributeFilter: ['class']
- });
-
- this.logDebug('主题切换监听器已启动');
- },
-
- /**
- * 重新渲染所有图表(主题切换时)
- * 需求 17.3: 保持图表的缩放级别和位置
- * 需求 17.4, 17.5: 使用淡入淡出过渡效果
- */
- reRenderCharts() {
- // 只选择成功渲染的图表容器,排除错误容器
- const charts = document.querySelectorAll('.mermaid-container:not(.mermaid-error-container)');
-
- if (charts.length === 0) {
- return;
- }
-
- this.logDebug(`重新渲染 ${charts.length} 个图表`);
-
- // 更新 Mermaid 主题配置
- const newTheme = this.getMermaidTheme();
-
- try {
- window.mermaid.initialize({
- startOnLoad: false,
- theme: newTheme,
- securityLevel: 'loose',
- logLevel: this.config.debugMode ? 'debug' : 'error',
- flowchart: {
- useMaxWidth: true,
- htmlLabels: true,
- curve: 'basis'
- },
- sequence: {
- useMaxWidth: true,
- wrap: true
- },
- gantt: {
- useMaxWidth: true
- }
- });
-
- // 重新渲染每个图表
- charts.forEach((chart, index) => {
- const code = chart.dataset.mermaidCode;
- if (!code) {
- this.logDebug('图表缺少原始代码,跳过重新渲染');
- return;
- }
-
- // 检查主题是否真的需要更新
- if (chart.dataset.currentTheme === newTheme) {
- this.logDebug('图表主题未改变,跳过重新渲染');
- return;
- }
-
- // 需求 17.3: 保存当前的缩放级别和滚动位置
- const inner = chart.querySelector('.mermaid-container-inner');
- let savedState = null;
-
- if (inner) {
- const transform = window.getComputedStyle(inner).transform;
- const scrollLeft = inner.scrollLeft;
- const scrollTop = inner.scrollTop;
-
- // 提取缩放比例
- let scale = 1;
- if (transform && transform !== 'none') {
- const matrix = transform.match(/matrix\(([^)]+)\)/);
- if (matrix) {
- const values = matrix[1].split(',').map(v => parseFloat(v.trim()));
- scale = values[0]; // scaleX
- }
- }
-
- savedState = {
- scale: scale,
- scrollLeft: scrollLeft,
- scrollTop: scrollTop
- };
-
- this.logDebug('保存图表状态', savedState);
- }
-
- const chartId = `mermaid-chart-rerender-${Date.now()}-${index}`;
-
- // 需求 17.4: 添加淡出动画
- chart.style.transition = 'opacity 0.2s ease-out';
- chart.style.opacity = '0';
-
- // 等待淡出完成后重新渲染
- setTimeout(() => {
- window.mermaid.render(`mermaid-svg-${chartId}`, code).then(result => {
- // 保存旧的内部容器引用
- const oldInner = chart.querySelector('.mermaid-container-inner');
-
- // 更新 SVG 内容
- if (oldInner) {
- // 只替换 SVG,保留容器结构
- const oldSvg = oldInner.querySelector('svg');
- if (oldSvg) {
- // 创建临时容器解析新的 SVG
- const temp = document.createElement('div');
- temp.innerHTML = result.svg;
- const newSvg = temp.querySelector('svg');
-
- if (newSvg) {
- // 替换 SVG
- oldSvg.replaceWith(newSvg);
-
- // 确保 SVG 响应式
- newSvg.style.width = '100%';
- newSvg.style.height = 'auto';
- }
- }
- } else {
- // 如果没有内部容器,直接替换内容
- chart.innerHTML = result.svg;
-
- // 确保 SVG 响应式
- const svg = chart.querySelector('svg');
- if (svg) {
- svg.style.width = '100%';
- svg.style.height = 'auto';
- }
- }
-
- // 更新主题标记
- chart.dataset.currentTheme = newTheme;
-
- // 需求 17.3: 恢复缩放级别和滚动位置
- if (savedState && oldInner) {
- // 恢复缩放
- oldInner.style.transform = `scale(${savedState.scale})`;
-
- // 恢复滚动位置
- oldInner.scrollLeft = savedState.scrollLeft;
- oldInner.scrollTop = savedState.scrollTop;
-
- // 更新缩放显示
- const zoomLevel = chart.querySelector('.mermaid-zoom-level');
- if (zoomLevel) {
- zoomLevel.textContent = Math.round(savedState.scale * 100) + '%';
- }
-
- this.logDebug('恢复图表状态', savedState);
- }
-
- // 需求 17.5: 添加淡入动画
- requestAnimationFrame(() => {
- chart.style.transition = 'opacity 0.3s ease-in';
- chart.style.opacity = '1';
-
- // 动画完成后清理过渡样式
- setTimeout(() => {
- chart.style.transition = '';
- }, 300);
- });
-
- this.logDebug(`图表重新渲染成功: ${chartId}`);
- }).catch(error => {
- this.logError('图表重新渲染失败', error);
-
- // 重新渲染失败时,恢复显示
- chart.style.opacity = '1';
- chart.style.transition = '';
-
- // 不替换为错误容器,保持原样
- // 因为之前已经成功渲染过,只是主题切换失败
- });
- }, 200); // 等待淡出动画完成
- });
-
- } catch (error) {
- this.logError('重新渲染失败', error);
- }
- },
-
- // ---------- 日志工具 ----------
-
- /**
- * 调试日志
- * @param {string} message - 日志信息
- * @param {*} data - 附加数据
- */
- logDebug(message, data) {
- if (this.config && this.config.debugMode) {
- if (data) {
- console.log(`[Argon Mermaid] ${message}`, data);
- } else {
- console.log(`[Argon Mermaid] ${message}`);
- }
- }
- },
-
- /**
- * 错误日志
- * @param {string} message - 错误信息
- * @param {Error} error - 错误对象
- */
- logError(message, error) {
- console.error(`[Argon Mermaid] ${message}`, error);
- },
-
- // ---------- 初始化 ----------
-
- /**
- * 初始化渲染引擎
- * @returns {Promise}
- */
- async init() {
- // 检查是否启用
- if (typeof window.argonMermaidConfig !== 'undefined' && !window.argonMermaidConfig.enabled) {
- this.logDebug('Mermaid 支持未启用');
- return;
- }
-
- // 需求 2.6, 4.1-4.2: 等待 Mermaid 库加载
- if (!await this.waitForMermaid()) {
- this.logError('Mermaid 库加载失败');
- return;
- }
-
- this.initAndRender();
- },
-
- /**
- * 初始化并渲染
- */
- initAndRender() {
- // 初始化配置
- if (!this.initConfig()) {
- return;
- }
-
- // 渲染所有图表
- this.renderAllCharts();
-
- // 监听主题切换
- this.listenThemeSwitch();
-
- this.logDebug('Mermaid 渲染引擎初始化完成');
- }
- };
-
- // ---------- 启动渲染引擎 ----------
-
- // 暴露到全局作用域(用于库加载失败时的降级处理)
- window.MermaidRenderer = MermaidRenderer;
-
- // 在 DOM 加载完成后初始化
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', () => {
- MermaidRenderer.init();
- });
- } else {
- // DOM 已加载完成,直接初始化
- MermaidRenderer.init();
- }
-
- // 暴露到全局(用于 PJAX 等场景)
- window.ArgonMermaidRenderer = MermaidRenderer;
-
-})();
-/* ========== End of Mermaid 图表渲染引擎 ========== */
diff --git a/docs/mermaid-developer-guide.md b/docs/mermaid-developer-guide.md
deleted file mode 100644
index 4023ada..0000000
--- a/docs/mermaid-developer-guide.md
+++ /dev/null
@@ -1,1230 +0,0 @@
-# Mermaid 功能开发者文档
-
-## 目录
-
-1. [架构概述](#架构概述)
-2. [PHP 函数参考](#php-函数参考)
-3. [JavaScript API 参考](#javascript-api-参考)
-4. [配置系统](#配置系统)
-5. [扩展开发](#扩展开发)
-6. [测试指南](#测试指南)
-
----
-
-## 架构概述
-
-### 系统组成
-
-Mermaid 功能由以下几个部分组成:
-
-```
-┌─────────────────────────────────────────┐
-│ WordPress 后台设置 │
-│ (settings.php) │
-└──────────────┬──────────────────────────┘
- │
- ├─> 配置管理 (functions.php)
- │ ├─ argon_get_mermaid_option()
- │ ├─ argon_update_mermaid_option()
- │ └─ argon_validate_mermaid_settings()
- │
- ├─> 库加载器 (functions.php)
- │ ├─ argon_enqueue_mermaid_scripts()
- │ ├─ argon_has_mermaid_content()
- │ └─ argon_get_mermaid_library_url()
- │
- ├─> 插件兼容层 (functions.php)
- │ ├─ argon_detect_mermaid_plugins()
- │ ├─ argon_is_mermaid_library_enqueued()
- │ └─ argon_should_load_mermaid_library()
- │
- └─> 前端渲染引擎 (argontheme.js)
- ├─ MermaidRenderer.init()
- ├─ MermaidRenderer.detectMermaidBlocks()
- ├─ MermaidRenderer.renderChart()
- └─ MermaidRenderer.handleThemeSwitch()
-```
-
-### 工作流程
-
-```mermaid
-flowchart TD
- A[页面加载] --> B{检查是否启用}
- B -->|否| Z[结束]
- B -->|是| C[检测页面内容]
- C --> D{包含 Mermaid?}
- D -->|否| Z
- D -->|是| E[检测插件]
- E --> F{插件已加载?}
- F -->|是| G[只提供样式]
- F -->|否| H[加载 Mermaid 库]
- H --> I[初始化配置]
- G --> I
- I --> J[检测代码块]
- J --> K[渲染图表]
- K --> L[监听主题切换]
- L --> Z
-```
-
----
-
-## PHP 函数参考
-
-### 配置管理函数
-
-#### argon_get_mermaid_option()
-
-获取 Mermaid 配置选项。
-
-**函数签名:**
-```php
-function argon_get_mermaid_option($option_name, $default = null)
-```
-
-**参数:**
-- `$option_name` (string) - 配置选项名称,支持简短名称或完整名称
-- `$default` (mixed) - 默认值,当选项不存在时返回
-
-**返回值:**
-- (mixed) 配置选项值
-
-**支持的选项名称:**
-| 简短名称 | 完整名称 | 说明 |
-|---------|---------|------|
-| `enabled` | `argon_enable_mermaid` | 是否启用 Mermaid |
-| `cdn_source` | `argon_mermaid_cdn_source` | CDN 来源 |
-| `custom_cdn_url` | `argon_mermaid_cdn_custom_url` | 自定义 CDN URL |
-| `theme` | `argon_mermaid_theme` | 图表主题 |
-| `use_local` | `argon_mermaid_use_local` | 使用本地镜像 |
-| `debug_mode` | `argon_mermaid_debug_mode` | 调试模式 |
-
-**示例:**
-```php
-// 使用简短名称
-$enabled = argon_get_mermaid_option('enabled', false);
-
-// 使用完整名称
-$theme = argon_get_mermaid_option('argon_mermaid_theme', 'auto');
-
-// 获取不存在的选项,返回默认值
-$custom = argon_get_mermaid_option('custom_option', 'default_value');
-```
-
----
-
-#### argon_update_mermaid_option()
-
-保存 Mermaid 配置选项。
-
-**函数签名:**
-```php
-function argon_update_mermaid_option($option_name, $value)
-```
-
-**参数:**
-- `$option_name` (string) - 配置选项名称
-- `$value` (mixed) - 配置选项值
-
-**返回值:**
-- (bool) 是否保存成功
-
-**示例:**
-```php
-// 启用 Mermaid
-argon_update_mermaid_option('enabled', true);
-
-// 设置主题
-argon_update_mermaid_option('theme', 'dark');
-
-// 设置 CDN 来源
-argon_update_mermaid_option('cdn_source', 'jsdelivr');
-```
-
----
-
-#### argon_validate_mermaid_cdn_url()
-
-验证 Mermaid CDN URL 格式。
-
-**函数签名:**
-```php
-function argon_validate_mermaid_cdn_url($url)
-```
-
-**参数:**
-- `$url` (string) - CDN URL
-
-**返回值:**
-- (bool) 是否为有效的 CDN URL
-
-**验证规则:**
-1. URL 不能为空
-2. 必须是有效的 URL 格式
-3. 必须以 `.js` 结尾
-4. 必须使用 `http://` 或 `https://` 协议
-
-**示例:**
-```php
-// 有效的 URL
-$valid = argon_validate_mermaid_cdn_url('https://cdn.example.com/mermaid.min.js');
-// 返回: true
-
-// 无效的 URL(不以 .js 结尾)
-$invalid = argon_validate_mermaid_cdn_url('https://cdn.example.com/mermaid');
-// 返回: false
-```
-
----
-
-#### argon_get_mermaid_theme()
-
-获取当前主题模式对应的 Mermaid 主题。
-
-**函数签名:**
-```php
-function argon_get_mermaid_theme()
-```
-
-**返回值:**
-- (string) Mermaid 主题名称
-
-**说明:**
-- 如果配置为固定主题,直接返回配置的主题
-- 如果配置为 `auto`,在 PHP 端返回 `default`,实际切换在 JavaScript 中实现
-
-**示例:**
-```php
-$theme = argon_get_mermaid_theme();
-// 返回: 'default', 'dark', 'forest', 'neutral' 之一
-```
-
----
-
-### 库加载函数
-
-#### argon_has_mermaid_content()
-
-检测页面内容是否包含 Mermaid 代码块。
-
-**函数签名:**
-```php
-function argon_has_mermaid_content($content)
-```
-
-**参数:**
-- `$content` (string) - 页面内容
-
-**返回值:**
-- (bool) 是否包含 Mermaid 代码块
-
-**支持的格式:**
-- ``
-- ``
-- ``
-- ``
-
-**示例:**
-```php
-global $post;
-if (argon_has_mermaid_content($post->post_content)) {
- // 页面包含 Mermaid 代码块
- echo '检测到 Mermaid 图表';
-}
-```
-
----
-
-#### argon_get_mermaid_library_url()
-
-获取 Mermaid 库的 URL。
-
-**函数签名:**
-```php
-function argon_get_mermaid_library_url()
-```
-
-**返回值:**
-- (string) Mermaid 库 URL
-
-**说明:**
-- 根据配置返回对应的 CDN 或本地路径
-- 如果自定义 URL 无效,自动降级到 jsDelivr
-
-**示例:**
-```php
-$url = argon_get_mermaid_library_url();
-// 返回: 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js'
-```
-
----
-
-#### argon_enqueue_mermaid_scripts()
-
-加载 Mermaid JavaScript 库。
-
-**函数签名:**
-```php
-function argon_enqueue_mermaid_scripts()
-```
-
-**说明:**
-- 在 `wp_enqueue_scripts` 钩子中调用
-- 只在启用 Mermaid 且页面包含代码块时加载
-- 自动检测插件,避免重复加载
-
-**钩子:**
-```php
-add_action('wp_enqueue_scripts', 'argon_enqueue_mermaid_scripts');
-```
-
----
-
-### 插件兼容函数
-
-#### argon_detect_mermaid_plugins()
-
-检测已安装的 Mermaid 相关插件。
-
-**函数签名:**
-```php
-function argon_detect_mermaid_plugins()
-```
-
-**返回值:**
-- (array) 插件检测结果数组
-
-**返回格式:**
-```php
-[
- 'wp-githuber-md' => false,
- 'markdown-block' => false,
- 'code-syntax-block' => false,
- 'mermaid-loaded' => false
-]
-```
-
-**示例:**
-```php
-$plugins = argon_detect_mermaid_plugins();
-if ($plugins['wp-githuber-md']) {
- echo 'WP Githuber MD 插件已安装';
-}
-```
-
----
-
-#### argon_is_mermaid_library_enqueued()
-
-检查是否有插件已经加载了 Mermaid 库。
-
-**函数签名:**
-```php
-function argon_is_mermaid_library_enqueued()
-```
-
-**返回值:**
-- (bool) 是否已加载
-
-**说明:**
-- 检测常见的 Mermaid 脚本句柄
-- 包括:`mermaid`, `mermaid-js`, `githuber-mermaid`, `wp-mermaid`, `markdown-mermaid`
-
-**示例:**
-```php
-if (argon_is_mermaid_library_enqueued()) {
- echo 'Mermaid 库已由其他来源加载';
-}
-```
-
----
-
-#### argon_should_load_mermaid_library()
-
-判断是否应该加载 Mermaid 库。
-
-**函数签名:**
-```php
-function argon_should_load_mermaid_library()
-```
-
-**返回值:**
-- (bool) 是否应该加载
-
-**说明:**
-- 检测插件和已加载的库
-- 避免重复加载
-- 在调试模式下输出日志
-
-**示例:**
-```php
-if (argon_should_load_mermaid_library()) {
- // 主题负责加载 Mermaid 库
- wp_enqueue_script('mermaid', $url);
-}
-```
-
----
-
-## JavaScript API 参考
-
-### MermaidRenderer 对象
-
-前端渲染引擎,负责检测和渲染 Mermaid 图表。
-
-#### 属性
-
-| 属性 | 类型 | 说明 |
-|------|------|------|
-| `initialized` | boolean | 是否已初始化 |
-| `rendered` | Set | 已渲染的图表 ID 集合 |
-| `config` | Object | 配置对象 |
-
----
-
-#### init()
-
-初始化 Mermaid 配置并渲染所有图表。
-
-**函数签名:**
-```javascript
-MermaidRenderer.init()
-```
-
-**返回值:**
-- (boolean) 是否初始化成功
-
-**说明:**
-- 检查 Mermaid 库是否已加载
-- 初始化 Mermaid 配置
-- 渲染所有图表
-- 监听主题切换事件
-
-**示例:**
-```javascript
-// 页面加载完成后初始化
-document.addEventListener('DOMContentLoaded', function() {
- if (typeof MermaidRenderer !== 'undefined') {
- MermaidRenderer.init();
- }
-});
-```
-
----
-
-#### detectMermaidBlocks()
-
-检测所有 Mermaid 代码块。
-
-**函数签名:**
-```javascript
-MermaidRenderer.detectMermaidBlocks()
-```
-
-**返回值:**
-- (Array) Mermaid 代码块元素数组
-
-**检测规则:**
-1. `div.mermaid` - 标准格式
-2. `pre code.language-mermaid` - Markdown 格式
-3. `pre[data-lang="mermaid"]` - 自定义属性格式
-4. `code.mermaid` - 简化格式
-
-**示例:**
-```javascript
-const blocks = MermaidRenderer.detectMermaidBlocks();
-console.log(`检测到 ${blocks.length} 个 Mermaid 代码块`);
-```
-
----
-
-#### extractMermaidCode()
-
-提取代码块内容。
-
-**函数签名:**
-```javascript
-MermaidRenderer.extractMermaidCode(element)
-```
-
-**参数:**
-- `element` (HTMLElement) - 代码块元素
-
-**返回值:**
-- (string) Mermaid 代码
-
-**说明:**
-- 支持多种代码块格式
-- 自动处理 WP-Markdown 生成的 `document.write()` 格式
-- 解码转义字符(`\n`, `\"`, `\'`)
-
-**示例:**
-```javascript
-const element = document.querySelector('.mermaid');
-const code = MermaidRenderer.extractMermaidCode(element);
-console.log(code);
-```
-
----
-
-#### renderChart()
-
-渲染单个图表。
-
-**函数签名:**
-```javascript
-MermaidRenderer.renderChart(element, index)
-```
-
-**参数:**
-- `element` (HTMLElement) - 代码块元素
-- `index` (number) - 图表索引
-
-**说明:**
-- 生成唯一的图表 ID
-- 避免重复渲染
-- 渲染成功后替换原始代码块
-- 渲染失败时显示错误提示
-
-**示例:**
-```javascript
-const blocks = MermaidRenderer.detectMermaidBlocks();
-blocks.forEach((block, index) => {
- MermaidRenderer.renderChart(block, index);
-});
-```
-
----
-
-#### handleThemeSwitch()
-
-处理主题切换事件。
-
-**函数签名:**
-```javascript
-MermaidRenderer.handleThemeSwitch()
-```
-
-**说明:**
-- 检测页面主题变化
-- 重新渲染所有图表
-- 使用新的主题配置
-
-**示例:**
-```javascript
-// 监听主题切换
-document.addEventListener('themeChanged', function() {
- MermaidRenderer.handleThemeSwitch();
-});
-```
-
----
-
-### 配置对象
-
-#### argonMermaidConfig
-
-全局配置对象,由 PHP 传递到前端。
-
-**属性:**
-```javascript
-{
- enabled: true, // 是否启用
- theme: 'auto', // 图表主题
- debugMode: false, // 调试模式
- fallbackUrls: [], // 备用 CDN URL 列表
- libraryLoadedByPlugin: false // 库是否由插件加载
-}
-```
-
-**访问方式:**
-```javascript
-if (window.argonMermaidConfig) {
- console.log('Mermaid 已启用:', argonMermaidConfig.enabled);
- console.log('当前主题:', argonMermaidConfig.theme);
-}
-```
-
----
-
-## 配置系统
-
-### 配置选项
-
-| 选项名称 | 类型 | 默认值 | 说明 |
-|---------|------|--------|------|
-| `argon_enable_mermaid` | boolean | false | 是否启用 Mermaid |
-| `argon_mermaid_cdn_source` | string | 'jsdelivr' | CDN 来源 |
-| `argon_mermaid_cdn_custom_url` | string | '' | 自定义 CDN URL |
-| `argon_mermaid_theme` | string | 'auto' | 图表主题 |
-| `argon_mermaid_use_local` | boolean | false | 使用本地镜像 |
-| `argon_mermaid_debug_mode` | boolean | false | 调试模式 |
-
-### CDN 来源选项
-
-| 值 | 说明 | URL |
-|----|------|-----|
-| `jsdelivr` | jsDelivr CDN | `https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js` |
-| `unpkg` | unpkg CDN | `https://unpkg.com/mermaid@10/dist/mermaid.min.js` |
-| `custom` | 自定义 CDN | 用户指定的 URL |
-| `local` | 本地文件 | `{theme_url}/assets/vendor/mermaid/mermaid.min.js` |
-
-### 主题选项
-
-| 值 | 说明 |
-|----|------|
-| `auto` | 自动切换(跟随页面主题) |
-| `default` | 默认主题(浅色) |
-| `dark` | 深色主题 |
-| `forest` | 森林主题(绿色) |
-| `neutral` | 中性主题(灰色) |
-
----
-
-## 扩展开发
-
-### 添加新的图表类型支持
-
-Mermaid 支持的所有图表类型都已自动支持,无需额外配置。
-
-### 自定义样式
-
-在 `style.css` 中添加自定义样式:
-
-```css
-/* 自定义 Mermaid 容器样式 */
-.mermaid-container {
- background: #f5f5f5;
- padding: 20px;
- border-radius: 8px;
- box-shadow: 0 2px 8px rgba(0,0,0,0.1);
-}
-
-/* 自定义错误提示样式 */
-.mermaid-error-container {
- background: #fff3cd;
- border-left: 4px solid #ffc107;
- padding: 15px;
- border-radius: 4px;
-}
-
-/* 夜间模式样式 */
-html.darkmode .mermaid-container {
- background: #2d2d2d;
-}
-```
-
-### 添加自定义钩子
-
-在 `functions.php` 中添加钩子:
-
-```php
-/**
- * Mermaid 渲染前钩子
- *
- * @param string $content 页面内容
- * @return string 修改后的内容
- */
-function my_custom_mermaid_before_render($content) {
- // 自定义处理逻辑
- return $content;
-}
-add_filter('argon_mermaid_before_render', 'my_custom_mermaid_before_render');
-
-/**
- * Mermaid 渲染后钩子
- *
- * @param string $content 渲染后的内容
- * @return string 修改后的内容
- */
-function my_custom_mermaid_after_render($content) {
- // 自定义处理逻辑
- return $content;
-}
-add_filter('argon_mermaid_after_render', 'my_custom_mermaid_after_render');
-```
-
----
-
-## 测试指南
-
-### 单元测试
-
-运行 PHP 单元测试:
-
-```bash
-cd tests
-php run-mermaid-config-tests.php
-```
-
-### 测试用例
-
-测试文件位于 `tests/` 目录:
-
-- `test-mermaid-config.php` - 配置管理测试
-- `test-mermaid-loader.php` - 库加载测试
-- `test-mermaid-fallback.php` - CDN 降级测试
-- `test-wp-markdown-format.html` - WP-Markdown 格式测试
-
-### 手动测试
-
-1. **基础功能测试**
- - 启用 Mermaid 支持
- - 创建包含 Mermaid 代码的文章
- - 验证图表是否正确渲染
-
-2. **主题切换测试**
- - 切换页面主题(日间/夜间)
- - 验证图表主题是否自动切换
-
-3. **CDN 降级测试**
- - 模拟 CDN 加载失败
- - 验证是否自动尝试备用 CDN
-
-4. **插件兼容测试**
- - 安装 Mermaid 相关插件
- - 验证是否正确检测并避免冲突
-
-5. **错误处理测试**
- - 输入错误的 Mermaid 代码
- - 验证错误提示是否友好
-
----
-
-## 调试技巧
-
-### 启用调试模式
-
-在主题设置中启用调试模式,查看详细日志:
-
-```javascript
-[Argon Mermaid] Mermaid 配置初始化成功 {theme: "default"}
-[Argon Mermaid] 检测到 3 个 Mermaid 代码块
-[Argon Mermaid] 渲染图表 #mermaid-chart-1234567890-0
-[Argon Mermaid] 图表渲染成功
-```
-
-### 浏览器控制台
-
-打开浏览器开发者工具(F12),查看:
-
-1. **控制台日志** - 查看 Mermaid 相关日志
-2. **网络请求** - 查看 CDN 加载状态
-3. **元素检查** - 查看渲染后的 SVG 结构
-
-### PHP 错误日志
-
-在 `wp-config.php` 中启用调试:
-
-```php
-define('WP_DEBUG', true);
-define('WP_DEBUG_LOG', true);
-define('WP_DEBUG_DISPLAY', false);
-```
-
-查看日志文件:`wp-content/debug.log`
-
----
-
-## 性能优化
-
-### 按需加载
-
-主题已实现按需加载,只在包含 Mermaid 代码的页面加载库。
-
-### 异步加载
-
-Mermaid 库使用 `async` 属性异步加载,不阻塞页面渲染。
-
-### CDN 缓存
-
-使用 CDN 加速加载,浏览器会自动缓存库文件。
-
-### 避免重复渲染
-
-使用 `rendered` Set 记录已渲染的图表,避免重复渲染。
-
----
-
-## 贡献指南
-
-### 代码规范
-
-遵循 Argon 主题的代码规范:
-
-- **PHP**: 遵循 WordPress Coding Standards
-- **JavaScript**: 使用 Tab 缩进,严格相等运算符
-- **CSS**: 使用 Tab 缩进,属性独占一行
-
-### 提交规范
-
-- **功能添加**: `feat: 添加 XXX 功能`
-- **Bug 修复**: `fix: 修复 XXX 问题`
-- **文档更新**: `docs: 更新 XXX 文档`
-
-### 测试要求
-
-- 添加新功能时,必须添加对应的测试用例
-- 修复 Bug 时,必须添加回归测试
-- 所有测试必须通过
-
----
-
-## 相关资源
-
-- [Mermaid 官方文档](https://mermaid.js.org/)
-- [WordPress 开发文档](https://developer.wordpress.org/)
-- [Argon 主题 GitHub](https://github.com/solstice23/argon-theme)
-
----
-
-**最后更新:** 2024-01-22
-**文档版本:** 1.0.0
-
-
----
-
-## 代码块魔改技术
-
-### 概述
-
-代码块魔改是一种在代码高亮之前拦截并转换 Mermaid 代码块的技术,使得标准 Markdown 代码块 (` ```mermaid `) 能够正确渲染为 Mermaid 图表,而不被代码高亮处理。
-
-### 实现原理
-
-#### 1. 执行顺序
-
-```
-页面加载/PJAX切换
- ↓
-highlightJsRender() 调用
- ↓
-convertMermaidCodeblocks() 执行 ← 【拦截阶段】
- ↓
-代码高亮处理其他代码块
- ↓
-MermaidRenderer.detectMermaidBlocks() ← 【检测阶段】
- ↓
-MermaidRenderer.renderChart() ← 【渲染阶段】
-```
-
-#### 2. 核心函数
-
-**convertMermaidCodeblocks()**
-
-在代码高亮之前转换 Mermaid 代码块。
-
-**位置:** `argontheme.js` 第 3942 行之前
-
-**功能:**
-- 使用多个选择器查找 mermaid 代码块
-- 提取纯文本代码(使用 `textContent`)
-- 创建 `` 容器
-- 替换原始代码块元素
-- 添加 `data-processed="true"` 标记防止重复处理
-
-**代码示例:**
-```javascript
-function convertMermaidCodeblocks(){
- // 支持多种代码块格式
- const selectors = [
- 'pre > code.language-mermaid', // 标准 Markdown 格式(最常见)
- 'pre > code.mermaid', // 简化格式
- 'code.language-mermaid', // 无 pre 包裹
- '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';
- });
- });
-
- if (processedCount > 0 && typeof console !== 'undefined' && console.log) {
- console.log('[Mermaid] 转换了 ' + processedCount + ' 个代码块');
- }
-}
-```
-
-#### 3. 集成点
-
-**集成点 1:highlightJsRender() 函数**
-
-在函数开始处调用 `convertMermaidCodeblocks()`:
-
-```javascript
-function highlightJsRender(){
- // 在代码高亮之前,先处理 Mermaid 代码块
- convertMermaidCodeblocks();
-
- if (typeof(hljs) == "undefined"){
- return;
- }
- // ... 原有的代码高亮逻辑
-}
-```
-
-**集成点 2:detectMermaidBlocks() 函数**
-
-在 selectors 数组中添加新的容器类型:
-
-```javascript
-const selectors = [
- 'div.mermaid-shortcode', // Shortcode 格式(推荐)
- 'div.mermaid-from-codeblock', // 代码块魔改格式(新增)
- 'div.mermaid', // 标准格式
- 'pre code.language-mermaid', // Markdown 格式(降级)
- 'pre[data-lang="mermaid"]', // 自定义属性格式
- 'code.mermaid' // 简化格式
-];
-```
-
-**集成点 3:extractMermaidCode() 函数**
-
-添加对新容器类型的处理:
-
-```javascript
-// 处理代码块魔改格式(新增)
-else if (element.classList.contains('mermaid-from-codeblock')) {
- code = element.textContent;
- this.logDebug('从代码块魔改格式提取代码');
-}
-```
-
-### 技术细节
-
-#### 选择器优先级
-
-选择器按以下优先级匹配:
-
-1. `pre > code.language-mermaid` - 标准 Markdown 格式(最常见)
-2. `pre > code.mermaid` - 简化格式
-3. `code.language-mermaid` - 无 pre 包裹
-4. `pre[data-lang="mermaid"]` - 自定义属性格式
-
-#### 重复处理防护
-
-使用 `data-mermaid-processed` 属性标记已处理的元素:
-
-```javascript
-if (element.dataset.mermaidProcessed) {
- return; // 跳过已处理的元素
-}
-// ... 处理逻辑
-element.dataset.mermaidProcessed = 'true';
-```
-
-这样可以:
-- 避免 PJAX 切换时重复处理
-- 防止多次调用导致的错误
-- 提高性能
-
-#### 代码提取
-
-使用 `textContent` 而非 `innerHTML`:
-
-```javascript
-let code = element.textContent.trim();
-```
-
-**优势:**
-- 获取纯文本,避免 HTML 实体
-- 防止 XSS 攻击
-- 保持原始内容不变
-
-#### 容器创建
-
-创建新的 div 容器:
-
-```javascript
-const container = document.createElement('div');
-container.className = 'mermaid-from-codeblock';
-container.textContent = code;
-container.dataset.processed = 'true';
-```
-
-**属性说明:**
-- `class="mermaid-from-codeblock"` - 标识来源于代码块
-- `data-processed="true"` - 标记已处理
-- `textContent` - 纯文本内容(不使用 `innerHTML`)
-
-#### 元素替换
-
-优先替换整个 `` 元素:
-
-```javascript
-const targetElement = element.closest('pre') || element;
-if (targetElement.parentNode) {
- targetElement.parentNode.replaceChild(container, targetElement);
-}
-```
-
-**逻辑:**
-- 如果代码块在 `` 中,替换整个 `` 元素
-- 如果没有 `` 包裹,替换 `` 元素
-- 保留原始位置和上下文
-
-### PJAX 兼容性
-
-代码块转换自动支持 PJAX,因为:
-
-1. `highlightJsRender()` 已在 PJAX 回调中调用
-2. `convertMermaidCodeblocks()` 在 `highlightJsRender()` 开始处执行
-3. 使用 `data-processed` 属性防止重复处理
-
-**PJAX 回调链:**
-```javascript
-$(document).on('pjax:complete', function() {
- // ... 其他初始化
- try { highlightJsRender(); } catch (err) { ... } // 包含代码块转换
- // ... 其他初始化
-});
-```
-
-### 性能优化
-
-#### 1. 使用原生 JavaScript
-
-```javascript
-document.querySelectorAll(selector) // 而非 $(selector)
-```
-
-**理由:** 原生方法性能更好,减少 jQuery 开销
-
-#### 2. 提前返回
-
-```javascript
-if (element.dataset.mermaidProcessed) {
- return; // 提前返回,避免不必要的处理
-}
-```
-
-**理由:** 减少重复处理,提高效率
-
-#### 3. 批量处理
-
-```javascript
-selectors.forEach(selector => {
- document.querySelectorAll(selector).forEach(element => {
- // 处理逻辑
- });
-});
-```
-
-**理由:** 一次性查找所有元素,减少 DOM 查询次数
-
-#### 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);
-```
-
-**理由:** 一次性创建和替换,减少重排和重绘
-
-### 安全性
-
-#### XSS 防护
-
-使用 `textContent` 而非 `innerHTML`:
-
-```javascript
-container.textContent = code; // 安全
-// container.innerHTML = code; // 不安全
-```
-
-**理由:** `textContent` 会自动转义 HTML,防止 XSS 攻击
-
-#### 代码来源验证
-
-```javascript
-let code = element.textContent.trim();
-if (!code) {
- return; // 拒绝空代码
-}
-```
-
-**理由:** 避免处理恶意或无效的代码块
-
-### 错误处理
-
-#### 空代码检查
-
-```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' // 降级选择器
-```
-
-**理由:** 确保即使转换失败,仍能渲染
-
-### 扩展方式
-
-#### 添加新的选择器
-
-在 `selectors` 数组中添加新的选择器:
-
-```javascript
-const selectors = [
- 'pre > code.language-mermaid',
- 'pre > code.mermaid',
- 'code.language-mermaid',
- 'pre[data-lang="mermaid"]',
- 'your-custom-selector' // 添加自定义选择器
-];
-```
-
-#### 自定义容器类名
-
-修改容器类名:
-
-```javascript
-container.className = 'your-custom-class';
-```
-
-然后在 `detectMermaidBlocks()` 中添加对应的选择器:
-
-```javascript
-const selectors = [
- 'div.mermaid-shortcode',
- 'div.your-custom-class', // 添加自定义类名
- 'div.mermaid',
- // ...
-];
-```
-
-### 调试技巧
-
-#### 1. 启用调试日志
-
-在 `convertMermaidCodeblocks()` 中添加日志:
-
-```javascript
-console.log('[Mermaid] 找到代码块:', element);
-console.log('[Mermaid] 提取的代码:', code);
-console.log('[Mermaid] 创建的容器:', container);
-```
-
-#### 2. 检查转换结果
-
-在浏览器控制台中检查:
-
-```javascript
-// 查看所有转换后的容器
-document.querySelectorAll('.mermaid-from-codeblock')
-
-// 查看容器内容
-document.querySelectorAll('.mermaid-from-codeblock').forEach(el => {
- console.log(el.textContent);
-});
-```
-
-#### 3. 验证执行顺序
-
-在关键位置添加日志:
-
-```javascript
-function highlightJsRender(){
- console.log('[1] highlightJsRender 开始');
- convertMermaidCodeblocks();
- console.log('[2] convertMermaidCodeblocks 完成');
- // ... 代码高亮逻辑
- console.log('[3] 代码高亮完成');
-}
-```
-
-### 测试用例
-
-参见 `tests/test-codeblock-magic.html` 文件,包含以下测试:
-
-1. 标准 Markdown 格式
-2. 多个代码块(批量处理)
-3. 特殊字符保留
-4. 空代码块(边界情况)
-5. 多行代码块(换行符保留)
-6. 简化格式
-7. 无 pre 包裹
-8. 自定义属性格式
-9. 复杂图表
-
-### 常见问题
-
-#### Q: 为什么要在代码高亮之前拦截?
-
-A: 因为代码高亮会:
-- 添加行号和控制按钮
-- 转换特殊字符(`-->` → `–>`)
-- 添加额外的 HTML 结构
-- 干扰 Mermaid 渲染
-
-#### Q: 为什么使用 `textContent` 而不是 `innerHTML`?
-
-A: 因为:
-- `textContent` 获取纯文本,避免 HTML 实体
-- 防止 XSS 攻击
-- 保持原始内容不变
-
-#### Q: 如何确保 PJAX 兼容性?
-
-A: 通过:
-- 在 `highlightJsRender()` 中调用(已在 PJAX 回调中)
-- 使用 `data-processed` 属性防止重复处理
-- 无需额外修改 PJAX 逻辑
-
-#### Q: 如何添加新的代码块格式?
-
-A: 在 `selectors` 数组中添加新的选择器,然后在 `detectMermaidBlocks()` 和 `extractMermaidCode()` 中添加对应的处理逻辑。
-
----
diff --git a/docs/mermaid-faq.md b/docs/mermaid-faq.md
deleted file mode 100644
index 6e67c4e..0000000
--- a/docs/mermaid-faq.md
+++ /dev/null
@@ -1,712 +0,0 @@
-# Mermaid 图表常见问题解答 (FAQ)
-
-## 目录
-
-- [基础问题](#基础问题)
-- [配置问题](#配置问题)
-- [渲染问题](#渲染问题)
-- [兼容性问题](#兼容性问题)
-- [性能问题](#性能问题)
-- [高级问题](#高级问题)
-
----
-
-## 基础问题
-
-### Q1: 什么是 Mermaid?
-
-**A:** Mermaid 是一个基于 JavaScript 的图表和图形工具,它使用类似 Markdown 的文本语法来创建和修改图表。您可以用简单的文本代码创建流程图、时序图、类图等多种专业图表。
-
-**优势:**
-- 📝 文本即代码,易于版本控制
-- 🎨 自动布局,无需手动调整
-- 🔄 易于修改和维护
-- 📱 响应式设计,自适应屏幕
-
----
-
-### Q2: Argon 主题支持哪些 Mermaid 图表类型?
-
-**A:** Argon 主题支持 Mermaid 的所有主要图表类型:
-
-1. **流程图 (Flowchart)** - 展示流程和决策
-2. **时序图 (Sequence Diagram)** - 描述对象交互
-3. **类图 (Class Diagram)** - 展示类结构
-4. **状态图 (State Diagram)** - 表示状态转换
-5. **饼图 (Pie Chart)** - 显示数据占比
-6. **甘特图 (Gantt Chart)** - 项目进度管理
-7. **用户旅程图 (User Journey)** - 用户体验流程
-8. **Git 图 (Git Graph)** - 版本控制可视化
-
-详细示例请参考[用户指南](mermaid-user-guide.md#支持的图表类型)。
-
----
-
-### Q3: 如何在文章中插入 Mermaid 图表?
-
-**A:** 有三种方式:
-
-**方式 1:标准 Markdown 代码块**(推荐)⭐
-````markdown
-三个反引号mermaid
-flowchart TD
- A[开始] --> B[结束]
-三个反引号
-````
-
-**优点:**
-- ✅ 符合标准 Markdown 语法
-- ✅ 在所有 Markdown 编辑器中都能正确显示
-- ✅ 易于迁移到其他平台(GitHub、GitLab、Typora 等)
-- ✅ 主题自动拦截处理,避免代码高亮干扰
-
-**方式 2:Markdown 容器语法**(备选)
-````markdown
-::: mermaid
-flowchart TD
- A[开始] --> B[结束]
-:::
-````
-
-**优点:**
-- ✅ 符合 Markdown 扩展规范(VuePress、Docusaurus 等)
-- ✅ 不会被 WP-Markdown 当作代码块处理
-
-**方式 3:Shortcode 格式**(兼容)
-```
-[mermaid]
-flowchart TD
- A[开始] --> B[结束]
-[/mermaid]
-```
-
-**优点:**
-- ✅ WordPress 原生支持
-- ✅ 兼容性最好
-
-**缺点:**
-- ❌ 不符合 Markdown 标准
-- ❌ 在其他平台无法使用
-
----
-
-### Q4: 为什么推荐使用标准 Markdown 代码块?
-
-**A:** 因为标准 Markdown 代码块 (` ```mermaid `) 是最通用的方式:
-
-- **GitHub** 使用这种语法
-- **GitLab** 使用这种语法
-- **Typora** 使用这种语法
-- **VS Code** 使用这种语法
-- **符合 CommonMark 规范**
-
-Argon 主题通过**代码块魔改**技术,在代码高亮之前拦截并转换 mermaid 代码块,因此:
-- ✅ 不会被代码高亮处理(无行号、无控制按钮)
-- ✅ 不会有字符转义问题(`-->` 保持不变)
-- ✅ 不会有嵌套结构问题
-- ✅ 完全符合标准 Markdown 语法
-- ✅ 易于迁移到其他平台
-```mermaid
-flowchart TD
- A --> B
-```
-````
-
-**方式 2:HTML 标签**
-```html
-
-flowchart TD
- A --> B
-
-```
-
-**注意:** 使用前需要在主题设置中启用 Mermaid 支持。
-
----
-
-### Q4: 需要安装额外的插件吗?
-
-**A:** 不需要。Argon 主题内置了 Mermaid 支持,开箱即用。
-
-**但是:**
-- 如果您已经安装了 Mermaid 相关插件(如 WP Githuber MD),主题会自动检测并避免重复加载
-- 主题会智能判断是否需要加载 Mermaid 库
-
----
-
-## 配置问题
-
-### Q5: 如何启用 Mermaid 支持?
-
-**A:** 按照以下步骤操作:
-
-1. 登录 WordPress 后台
-2. 进入 **外观 → Argon 主题设置**
-3. 找到 **Mermaid 图表** 分类
-4. 勾选 **"启用 Mermaid 支持"**
-5. 点击 **"保存设置"**
-
----
-
-### Q6: 应该选择哪个 CDN 来源?
-
-**A:** 推荐选择:
-
-**首选:jsDelivr CDN**
-- ✅ 全球 CDN,速度快
-- ✅ 稳定性高
-- ✅ 免费使用
-
-**备选:unpkg CDN**
-- 作为备用选项
-
-**特殊情况:**
-- **内网环境** → 使用本地文件
-- **特定版本需求** → 使用自定义 CDN
-- **极致速度** → 下载本地文件
-
-**主题优势:** 即使主 CDN 失败,主题会自动尝试备用 CDN,确保可用性。
-
----
-
-### Q7: 如何使用自定义 CDN?
-
-**A:** 步骤如下:
-
-1. 在 **CDN 来源** 中选择 **"自定义 CDN 地址"**
-2. 在 **自定义 CDN 地址** 输入框中填入完整的 URL
-3. 保存设置
-
-**URL 格式要求:**
-- 必须是有效的 URL
-- 必须以 `.js` 结尾
-- 必须使用 `http://` 或 `https://` 协议
-
-**示例:**
-```
-https://cdn.example.com/mermaid@10.0.0/mermaid.min.js
-```
-
----
-
-### Q8: 图表主题应该选择哪个?
-
-**A:** 推荐选择 **"自动切换"**。
-
-**原因:**
-- 自动跟随页面的日间/夜间模式
-- 日间模式使用浅色主题
-- 夜间模式使用深色主题
-- 用户体验最佳
-
-**其他选项:**
-- **默认主题** - 固定使用浅色
-- **深色主题** - 固定使用深色
-- **森林主题** - 绿色主题
-- **中性主题** - 灰色主题
-
----
-
-### Q9: 什么时候需要启用调试模式?
-
-**A:** 在以下情况下启用:
-
-1. **图表不显示** - 查看是否有加载错误
-2. **渲染失败** - 查看详细的错误信息
-3. **主题切换异常** - 检查主题切换逻辑
-4. **CDN 加载问题** - 查看 CDN 加载状态
-5. **开发测试** - 开发新功能时调试
-
-**如何使用:**
-1. 启用调试模式
-2. 打开浏览器开发者工具(F12)
-3. 切换到"控制台"标签
-4. 查看以 `[Argon Mermaid]` 开头的日志
-
-**注意:** 生产环境建议关闭调试模式。
-
----
-
-## 渲染问题
-
-### Q10: 图表不显示,只显示代码怎么办?
-
-**A:** 按以下步骤排查:
-
-**步骤 1:检查是否启用**
-- 确认主题设置中已启用 Mermaid 支持
-
-**步骤 2:检查代码格式**
-- 确认使用了正确的代码块格式
-- 检查是否有语法错误
-
-**步骤 3:检查浏览器控制台**
-- 按 F12 打开开发者工具
-- 查看是否有 JavaScript 错误
-
-**步骤 4:启用调试模式**
-- 在主题设置中启用调试模式
-- 查看详细的日志信息
-
-**步骤 5:验证代码**
-- 访问 [Mermaid Live Editor](https://mermaid.live/)
-- 粘贴代码验证语法是否正确
-
----
-
-### Q11: 图表显示"渲染失败"错误?
-
-**A:** 这通常是代码语法错误导致的。
-
-**解决方法:**
-
-1. **查看错误详情**
- - 点击错误提示中的"查看原始代码"
- - 查看错误类型和错误信息
-
-2. **常见语法错误:**
- - 缺少必要的关键字
- - 箭头符号错误
- - 节点 ID 重复
- - 引号不匹配
- - 缩进不正确
-
-3. **使用在线编辑器验证**
- - 访问 [Mermaid Live Editor](https://mermaid.live/)
- - 粘贴代码并查看错误提示
- - 根据提示修正语法
-
-4. **参考官方文档**
- - [Mermaid 语法参考](https://mermaid.js.org/intro/syntax-reference.html)
- - 查看对应图表类型的语法规则
-
----
-
-### Q12: 图表在夜间模式下看不清?
-
-**A:** 这是主题配置问题。
-
-**解决方法:**
-
-1. 进入 **主题设置 → Mermaid 图表 → 外观设置**
-2. 将 **图表主题** 设置为 **"自动切换"**
-3. 保存设置
-
-**原理:**
-- 自动切换模式会检测页面主题
-- 日间模式使用浅色图表主题
-- 夜间模式使用深色图表主题
-
-**如果仍有问题:**
-- 清除浏览器缓存
-- 刷新页面
-- 检查是否有其他 CSS 冲突
-
----
-
-### Q13: 图表太大,超出容器怎么办?
-
-**A:** Mermaid 图表会自动适应容器宽度,但如果图表过于复杂,可能会出现问题。
-
-**解决方法:**
-
-**方法 1:简化图表**
-- 减少节点数量
-- 拆分为多个小图表
-- 使用子图组织内容
-
-**方法 2:自定义样式**
-```css
-.mermaid-container {
- max-width: 100%;
- overflow-x: auto;
-}
-
-.mermaid-container svg {
- max-width: 100%;
- height: auto;
-}
-```
-
-**方法 3:调整图表方向**
-```mermaid
-flowchart LR /* 横向布局 */
- A --> B
-```
-
-```mermaid
-flowchart TD /* 纵向布局 */
- A --> B
-```
-
----
-
-### Q14: 如何在评论中使用 Mermaid?
-
-**A:** 评论中需要使用 HTML 格式。
-
-**步骤:**
-
-1. **确保评论允许 HTML**
- - 检查 WordPress 评论设置
- - 确认允许使用 HTML 标签
-
-2. **使用 HTML 格式**
-```html
-
-flowchart LR
- A --> B
-
-```
-
-3. **提交评论**
- - 评论会在前台自动渲染为图表
-
-**注意:**
-- 不能使用 Markdown 代码块格式
-- 必须使用 `` 包裹
-- 代码需要正确的换行格式
-
----
-
-## 兼容性问题
-
-### Q15: 与其他插件冲突怎么办?
-
-**A:** Argon 主题内置了智能兼容机制。
-
-**自动检测的插件:**
-- WP Githuber MD
-- Markdown Block
-- Code Syntax Block
-
-**兼容策略:**
-1. 主题会自动检测已安装的 Mermaid 插件
-2. 如果检测到插件,主题只提供样式增强
-3. 避免重复加载 Mermaid 库
-
-**查看兼容性状态:**
-1. 进入 **主题设置 → Mermaid 图表 → 高级选项**
-2. 查看 **插件兼容性检测** 部分
-3. 查看检测结果和建议
-
-**如果仍有冲突:**
-- 禁用主题的 Mermaid 支持,使用插件
-- 或禁用插件,使用主题的 Mermaid 支持
-- 不要同时启用多个 Mermaid 功能
-
----
-
-### Q16: 检测到多个 Mermaid 插件怎么办?
-
-**A:** 这会导致重复加载和冲突。
-
-**建议:**
-1. **只保留一个** - 选择功能最完善的插件
-2. **或使用主题** - 禁用所有插件,使用主题的 Mermaid 支持
-
-**如何选择:**
-- **插件功能更多** → 禁用主题支持,使用插件
-- **主题集成更好** → 禁用插件,使用主题支持
-- **性能优先** → 使用主题支持(按需加载)
-
----
-
-### Q17: 在不同编辑器中如何使用?
-
-**A:** 不同编辑器使用方法略有不同。
-
-**Gutenberg 编辑器:**
-1. 添加"代码"块或"自定义 HTML"块
-2. 输入 Mermaid 代码
-3. 使用 `` 包裹
-
-**经典编辑器:**
-1. 切换到"文本"模式
-2. 使用 HTML 格式
-3. 使用 `` 包裹
-
-**Markdown 编辑器(如 WP-Markdown):**
-1. 使用代码块语法
-2. 指定语言为 `mermaid`
-````markdown
-```mermaid
-flowchart TD
- A --> B
-```
-````
-
-**WP Githuber MD:**
-- 插件自带 Mermaid 支持
-- 主题会自动检测并避免冲突
-- 使用插件的 Mermaid 功能即可
-
----
-
-## 性能问题
-
-### Q18: Mermaid 会影响页面加载速度吗?
-
-**A:** 主题已做了充分的性能优化。
-
-**优化措施:**
-
-1. **按需加载**
- - 只在包含 Mermaid 代码的页面加载库
- - 没有 Mermaid 代码的页面不加载
-
-2. **异步加载**
- - Mermaid 库使用 async 属性异步加载
- - 不阻塞页面渲染
-
-3. **CDN 加速**
- - 使用全球 CDN 加速加载
- - 浏览器缓存减少重复加载
-
-4. **智能检测**
- - 检测插件是否已加载库
- - 避免重复加载
-
-**实际影响:**
-- Mermaid 库大小约 300KB(gzip 后约 100KB)
-- 首次加载约 0.5-1 秒
-- 后续访问使用缓存,几乎无影响
-
----
-
-### Q19: 一篇文章可以使用多少个图表?
-
-**A:** 技术上没有限制,但建议控制数量。
-
-**建议:**
-- **小型图表** - 不超过 10 个
-- **中型图表** - 不超过 5 个
-- **大型图表** - 不超过 3 个
-
-**原因:**
-- 过多图表会增加渲染时间
-- 影响页面性能
-- 用户体验下降
-
-**优化建议:**
-1. 合并相关图表
-2. 使用子图组织内容
-3. 复杂图表考虑使用图片替代
-4. 分页展示大量图表
-
----
-
-### Q20: 如何优化 Mermaid 性能?
-
-**A:** 以下是一些优化建议:
-
-**1. 使用本地文件**
-- 下载 Mermaid 库到主题目录
-- 启用"使用本地镜像"
-- 减少网络请求
-
-**2. 简化图表**
-- 减少节点数量
-- 避免过于复杂的关系
-- 使用简洁的文本
-
-**3. 启用浏览器缓存**
-- CDN 文件会自动缓存
-- 减少重复加载
-
-**4. 按需加载**
-- 主题已自动实现
-- 只在需要时加载库
-
-**5. 使用 CDN**
-- 利用 CDN 的全球加速
-- 减少服务器负载
-
----
-
-## 高级问题
-
-### Q21: 如何自定义 Mermaid 样式?
-
-**A:** 可以通过 CSS 自定义样式。
-
-**方法 1:使用主题自定义 CSS**
-1. 进入 **外观 → 自定义 → 额外 CSS**
-2. 添加自定义样式
-
-**示例:**
-```css
-/* 自定义图表容器样式 */
-.mermaid-container {
- background: #f5f5f5;
- padding: 20px;
- border-radius: 8px;
- box-shadow: 0 2px 8px rgba(0,0,0,0.1);
-}
-
-/* 自定义错误提示样式 */
-.mermaid-error-container {
- background: #fff3cd;
- border-left: 4px solid #ffc107;
-}
-```
-
-**方法 2:修改主题文件**
-- 编辑 `style.css`
-- 在 Mermaid 相关部分添加样式
-- 不推荐(主题更新会覆盖)
-
----
-
-### Q22: 如何导出 Mermaid 图表为图片?
-
-**A:** 有多种方法可以导出图表。
-
-**方法 1:使用 Mermaid Live Editor**
-1. 访问 [Mermaid Live Editor](https://mermaid.live/)
-2. 粘贴代码
-3. 点击"Export"按钮
-4. 选择 PNG 或 SVG 格式
-
-**方法 2:浏览器截图**
-1. 在浏览器中打开文章
-2. 使用截图工具截取图表
-3. Windows: Win + Shift + S
-4. Mac: Cmd + Shift + 4
-
-**方法 3:开发者工具**
-1. 右键点击图表 → 检查元素
-2. 找到 SVG 元素
-3. 右键 → Copy → Copy outerHTML
-4. 保存为 .svg 文件
-5. 使用工具转换为 PNG
-
-**方法 4:使用插件**
-- 安装浏览器截图插件
-- 如 Awesome Screenshot
-- 直接截取并保存
-
----
-
-### Q23: 如何在 Mermaid 代码中使用中文?
-
-**A:** Mermaid 完全支持中文,直接使用即可。
-
-**示例:**
-```mermaid
-flowchart TD
- 开始([开始]) --> 处理[处理数据]
- 处理 --> 判断{是否成功?}
- 判断 -->|是| 成功([成功])
- 判断 -->|否| 失败([失败])
-```
-
-**注意事项:**
-1. **引号问题**
- - 如果文本包含特殊字符,使用引号包裹
- - `A["包含特殊字符的文本"]`
-
-2. **编码问题**
- - 确保文件编码为 UTF-8
- - WordPress 默认使用 UTF-8
-
-3. **字体问题**
- - 确保浏览器支持中文字体
- - 现代浏览器都支持
-
----
-
-### Q24: 如何在 Mermaid 中添加链接?
-
-**A:** Mermaid 支持为节点添加链接。
-
-**语法:**
-```mermaid
-flowchart TD
- A[节点 A]
- B[节点 B]
- A --> B
- click A "https://example.com" "点击访问"
- click B "https://example.com" _blank
-```
-
-**参数说明:**
-- 第一个参数:节点 ID
-- 第二个参数:链接 URL
-- 第三个参数:提示文本或打开方式
- - `"提示文本"` - 鼠标悬停提示
- - `_blank` - 新标签页打开
- - `_self` - 当前标签页打开
-
----
-
-### Q25: 如何使用 Mermaid 的高级功能?
-
-**A:** Mermaid 支持许多高级功能。
-
-**1. 子图 (Subgraph)**
-```mermaid
-flowchart TD
- subgraph 输入阶段
- A[接收] --> B[验证]
- end
- subgraph 处理阶段
- B --> C[处理]
- end
-```
-
-**2. 样式定义**
-```mermaid
-flowchart TD
- A[节点 A]
- B[节点 B]
- A --> B
- style A fill:#f9f,stroke:#333,stroke-width:4px
- style B fill:#bbf,stroke:#333,stroke-width:2px
-```
-
-**3. 类样式**
-```mermaid
-flowchart TD
- A[节点 A]:::someclass
- B[节点 B]:::someclass
- classDef someclass fill:#f96,stroke:#333
-```
-
-**4. 注释**
-```mermaid
-%% 这是注释,不会显示
-flowchart TD
- A --> B %% 行尾注释
-```
-
-**更多功能:**
-- 查看 [Mermaid 官方文档](https://mermaid.js.org/)
-- 参考各图表类型的详细语法
-
----
-
-## 获取更多帮助
-
-### 官方资源
-
-- 📚 [Mermaid 官方文档](https://mermaid.js.org/)
-- 🎨 [Mermaid Live Editor](https://mermaid.live/)
-- 💬 [Mermaid GitHub](https://github.com/mermaid-js/mermaid)
-
-### Argon 主题支持
-
-- 🐛 [提交 Issue](https://github.com/solstice23/argon-theme/issues)
-- 📖 [主题文档](https://github.com/solstice23/argon-theme)
-- 💡 [用户指南](mermaid-user-guide.md)
-
-### 社区资源
-
-- 🌐 WordPress 论坛
-- 💬 主题用户群
-- 📝 技术博客和教程
-
----
-
-**最后更新:** 2024-01-22
-**文档版本:** 1.0.0
diff --git a/docs/mermaid-shortcode-guide.md b/docs/mermaid-shortcode-guide.md
deleted file mode 100644
index bcf8089..0000000
--- a/docs/mermaid-shortcode-guide.md
+++ /dev/null
@@ -1,505 +0,0 @@
-# Mermaid Shortcode 使用指南
-
-## 为什么使用 Shortcode?
-
-在 WP-Markdown 环境下,使用 Shortcode 是最可靠的 Mermaid 图表标记方式:
-
-### 优点 ✅
-
-1. **不依赖 WP-Markdown 的处理方式**
- - 不会被 WordPress 自动格式化破坏
- - 不会将 `-->` 转换为 `–>`
- - 不会丢失换行符
-
-2. **在原生编辑器中清晰可见**
- - 经典编辑器:文本模式下直接可见
- - Gutenberg 编辑器:使用"短代码"块
- - 易于编辑和维护
-
-3. **支持参数配置**
- - 可以设置主题(theme)
- - 可以设置宽度(width)
- - 可以设置高度(height)
- - 可以设置对齐方式(align)
-
-4. **完全兼容**
- - 与其他 Shortcode 一样使用
- - 不需要额外插件
- - 不需要修改 WP-Markdown
-
-### 对比其他方式
-
-| 方式 | 优点 | 缺点 |
-|------|------|------|
-| **Shortcode** ⭐ | 可靠、易用、支持参数 | 需要记住语法 |
-| 容器语法 `::: mermaid` | 符合 Markdown 规范 | 被 WP 格式化破坏 |
-| 代码块 ` ```mermaid ` | 通用 | 被代码高亮干扰 |
-| HTML `` | 灵活 | 编辑不便 |
-
----
-
-## 基本用法
-
-### 语法
-
-```
-[mermaid]
-flowchart TD
- A[开始] --> B[处理]
- B --> C[结束]
-[/mermaid]
-```
-
-### 在经典编辑器中使用
-
-1. 切换到"文本"模式(不是"可视化"模式)
-2. 输入 Shortcode:
- ```
- [mermaid]
- 你的 Mermaid 代码
- [/mermaid]
- ```
-3. 保存并预览
-
-### 在 Gutenberg 编辑器中使用
-
-1. 添加"短代码"块(Shortcode Block)
-2. 输入 Shortcode:
- ```
- [mermaid]
- 你的 Mermaid 代码
- [/mermaid]
- ```
-3. 保存并预览
-
----
-
-## 参数说明
-
-### theme - 主题
-
-设置图表主题,支持以下值:
-- `default` - 默认主题(浅色)
-- `dark` - 深色主题
-- `forest` - 森林主题
-- `neutral` - 中性主题
-
-**示例**:
-```
-[mermaid theme="dark"]
-flowchart TD
- A --> B
-[/mermaid]
-```
-
-### width - 宽度
-
-设置图表容器宽度,支持:
-- 百分比:`100%`, `80%`, `50%`
-- 像素值:`800px`, `600px`
-- 自动:`auto`
-
-**示例**:
-```
-[mermaid width="80%"]
-flowchart TD
- A --> B
-[/mermaid]
-```
-
-### height - 高度
-
-设置图表容器高度,支持:
-- 像素值:`600px`, `400px`
-- 自动:`auto`(默认)
-
-**示例**:
-```
-[mermaid height="500px"]
-flowchart TD
- A --> B
-[/mermaid]
-```
-
-### align - 对齐方式
-
-设置图表对齐方式,支持:
-- `left` - 左对齐
-- `center` - 居中(默认)
-- `right` - 右对齐
-
-**示例**:
-```
-[mermaid align="left"]
-flowchart TD
- A --> B
-[/mermaid]
-```
-
-### 组合使用
-
-```
-[mermaid theme="dark" width="80%" height="500px" align="center"]
-flowchart TD
- A[开始] --> B[处理]
- B --> C[结束]
-[/mermaid]
-```
-
----
-
-## 使用示例
-
-### 示例 1: 简单流程图
-
-```
-[mermaid]
-flowchart TD
- Start([开始]) --> Process[处理数据]
- Process --> Decision{是否成功?}
- Decision -->|是| Success[显示成功]
- Decision -->|否| Error[显示错误]
- Success --> End([结束])
- Error --> End
-[/mermaid]
-```
-
-### 示例 2: 时序图
-
-```
-[mermaid]
-sequenceDiagram
- participant User as 用户
- participant Server as 服务器
- participant DB as 数据库
-
- User->>Server: 发送请求
- Server->>DB: 查询数据
- DB-->>Server: 返回数据
- Server-->>User: 返回响应
-[/mermaid]
-```
-
-### 示例 3: 类图
-
-```
-[mermaid]
-classDiagram
- class Animal {
- +String name
- +int age
- +makeSound()
- }
- class Dog {
- +String breed
- +bark()
- }
- class Cat {
- +String color
- +meow()
- }
- Animal <|-- Dog
- Animal <|-- Cat
-[/mermaid]
-```
-
-### 示例 4: 甘特图
-
-```
-[mermaid]
-gantt
- title 项目进度
- dateFormat YYYY-MM-DD
- section 设计
- 需求分析 :a1, 2024-01-01, 7d
- UI设计 :a2, after a1, 5d
- section 开发
- 前端开发 :b1, after a2, 10d
- 后端开发 :b2, after a2, 12d
- section 测试
- 功能测试 :c1, after b1, 5d
- 性能测试 :c2, after b2, 3d
-[/mermaid]
-```
-
-### 示例 5: 状态图
-
-```
-[mermaid]
-stateDiagram-v2
- [*] --> 待审核
- 待审核 --> 已通过: 审核通过
- 待审核 --> 已拒绝: 审核拒绝
- 已通过 --> [*]
- 已拒绝 --> [*]
-[/mermaid]
-```
-
-### 示例 6: 饼图
-
-```
-[mermaid]
-pie title 编程语言使用占比
- "JavaScript" : 386
- "Python" : 285
- "Java" : 215
- "C++" : 115
- "其他" : 85
-[/mermaid]
-```
-
-### 示例 7: ER 图
-
-```
-[mermaid]
-erDiagram
- USER ||--o{ ORDER : places
- ORDER ||--|{ ORDER_ITEM : contains
- PRODUCT ||--o{ ORDER_ITEM : "ordered in"
-
- USER {
- int id PK
- string name
- string email
- }
- ORDER {
- int id PK
- int user_id FK
- date created_at
- }
- PRODUCT {
- int id PK
- string name
- decimal price
- }
-[/mermaid]
-```
-
-### 示例 8: 带样式的流程图
-
-```
-[mermaid]
-flowchart TD
- Start([用户提交评论]) --> PreProcess[预处理]
- PreProcess --> CheckEnabled{启用 AI 检测?}
- CheckEnabled -->|是| AICheck[AI 检测]
- CheckEnabled -->|否| Save[保存评论]
- AICheck --> Result{检测结果?}
- Result -->|垃圾评论| Trash[移入回收站]
- Result -->|正常评论| Save
-
- style Start fill:#e1f5e1,stroke:#2e7d32,stroke-width:2px
- style Trash fill:#ff6b6b,stroke:#c62828,stroke-width:2px
- style Save fill:#95e1d3,stroke:#2e7d32,stroke-width:2px
-[/mermaid]
-```
-
----
-
-## 常见问题
-
-### 1. Shortcode 不生效怎么办?
-
-**可能原因**:
-- 主题未更新到最新版本
-- 使用了"可视化"模式编辑
-
-**解决方案**:
-1. 更新 Argon 主题到最新版本
-2. 切换到"文本"模式编辑
-3. 检查 Shortcode 语法是否正确
-
-### 2. 图表渲染失败怎么办?
-
-**排查步骤**:
-
-1. **检查 Mermaid 语法**
- - 访问 [Mermaid Live Editor](https://mermaid.live/)
- - 粘贴你的代码验证语法
-
-2. **查看浏览器控制台**
- - 按 F12 打开开发者工具
- - 查看 Console 标签页
- - 搜索错误信息
-
-3. **检查主题设置**
- - WordPress 后台 → 外观 → Argon 主题选项
- - 找到"Mermaid 图表"分类
- - 确认"启用 Mermaid 支持"已开启
-
-### 3. 如何迁移现有的容器语法?
-
-如果你之前使用了 `::: mermaid ... :::` 语法,可以批量替换:
-
-**查找**:
-```
-::: mermaid
-```
-
-**替换为**:
-```
-[mermaid]
-```
-
-**查找**:
-```
-:::
-```
-
-**替换为**:
-```
-[/mermaid]
-```
-
-### 4. 可以在评论中使用吗?
-
-不可以。Shortcode 只能在文章和页面中使用,评论中不支持。
-
-### 5. 可以嵌套使用吗?
-
-不可以。Shortcode 不支持嵌套,每个图表需要独立的 `[mermaid]...[/mermaid]` 标签。
-
----
-
-## 最佳实践
-
-### 1. 使用有意义的节点 ID
-
-**推荐**:
-```
-[mermaid]
-flowchart TD
- UserSubmit([用户提交]) --> Validate[验证数据]
- Validate --> Save[保存数据]
-[/mermaid]
-```
-
-**不推荐**:
-```
-[mermaid]
-flowchart TD
- A --> B
- B --> C
-[/mermaid]
-```
-
-### 2. 添加适当的注释
-
-```
-[mermaid]
-flowchart TD
- %% 用户流程
- Start([开始]) --> Login[登录]
-
- %% 验证流程
- Login --> Check{验证成功?}
- Check -->|是| Dashboard[进入控制台]
- Check -->|否| Error[显示错误]
-[/mermaid]
-```
-
-### 3. 使用样式定义
-
-```
-[mermaid]
-flowchart TD
- Success[成功] --> End
- Error[错误] --> End
-
- style Success fill:#95e1d3,stroke:#2e7d32
- style Error fill:#ff6b6b,stroke:#c62828
-[/mermaid]
-```
-
-### 4. 保持图表简洁
-
-- 避免过多的节点(建议 < 20 个)
-- 使用子图(subgraph)组织复杂流程
-- 考虑拆分为多个图表
-
-### 5. 测试后再发布
-
-1. 先在 [Mermaid Live Editor](https://mermaid.live/) 中测试
-2. 确认语法正确后再粘贴到文章中
-3. 使用"预览"功能查看效果
-4. 确认无误后再发布
-
----
-
-## 技术细节
-
-### Shortcode 实现
-
-Argon 主题在 `functions.php` 中注册了 `mermaid` shortcode:
-
-```php
-add_shortcode('mermaid','shortcode_mermaid');
-function shortcode_mermaid($attr,$content=""){
- // 预处理内容
- $content = shortcode_content_preprocess($attr, $content);
-
- // 获取参数
- $theme = isset( $attr['theme'] ) ? $attr['theme'] : 'default';
- $width = isset( $attr['width'] ) ? $attr['width'] : '100%';
- $height = isset( $attr['height'] ) ? $attr['height'] : 'auto';
- $align = isset( $attr['align'] ) ? $attr['align'] : 'center';
-
- // 生成 HTML
- $out = '';
- $out .= '';
- $out .= esc_html($content);
- $out .= '';
-
- return $out;
-}
-```
-
-### JavaScript 检测
-
-在 `argontheme.js` 中,Mermaid 渲染器会自动检测 `div.mermaid-shortcode` 元素:
-
-```javascript
-const selectors = [
- 'div.mermaid-shortcode', // Shortcode 格式(推荐)
- 'div.mermaid', // 标准格式
- // ...
-];
-```
-
-### 安全性
-
-- 使用 `esc_html()` 转义输出,防止 XSS 攻击
-- 使用 `esc_attr()` 转义属性值
-- 不执行任何用户提供的 JavaScript 代码
-
----
-
-## 相关资源
-
-- [Mermaid 官方文档](https://mermaid.js.org/)
-- [Mermaid Live Editor](https://mermaid.live/)
-- [WordPress Shortcode API](https://developer.wordpress.org/plugins/shortcodes/)
-- [Argon 主题文档](https://argon-docs.solstice23.top/)
-
----
-
-## 更新日志
-
-### 2026-01-24
-- ✅ 添加 Mermaid Shortcode 支持
-- ✅ 支持 theme、width、height、align 参数
-- ✅ 自动检测和渲染
-- ✅ 完整的使用文档和示例
-
----
-
-## 总结
-
-使用 Shortcode 是在 WP-Markdown 环境下最可靠的 Mermaid 图表标记方式:
-
-1. **简单易用**:`[mermaid]...[/mermaid]`
-2. **功能强大**:支持主题、尺寸、对齐等参数
-3. **完全兼容**:不需要额外插件或修改
-4. **易于维护**:在编辑器中清晰可见
-
-推荐所有用户使用 Shortcode 方式编写 Mermaid 图表!
diff --git a/docs/mermaid-troubleshooting.md b/docs/mermaid-troubleshooting.md
deleted file mode 100644
index d4b189f..0000000
--- a/docs/mermaid-troubleshooting.md
+++ /dev/null
@@ -1,360 +0,0 @@
-# Mermaid 图表故障排查指南
-
-## 常见错误及解决方案
-
-### 1. 语法错误:`Parse error on line 1`
-
-#### 错误示例
-```
-Parse error on line 1: flowchart TD Sta
-^
-Expecting 'NEWLINE', 'SPACE', 'GRAPH', got 'ALPHA'
-```
-
-#### 原因分析
-- 代码块中有多余的空格或缩进
-- WordPress 或插件添加了额外的格式
-- 代码提取不完整
-
-#### 解决方案
-
-**方案 1:检查代码格式**
-
-确保 Mermaid 代码格式正确:
-
-```markdown
-三个反引号mermaid
-flowchart TD
- A[开始] --> B[结束]
-三个反引号
-```
-
-**注意**:
-- 第一行只有 `flowchart TD`,后面不要有其他内容
-- 每行开头不要有多余的空格(除了必要的缩进)
-- 使用 Tab 或 4 个空格作为缩进
-
-**方案 2:使用容器语法**
-
-如果代码块格式有问题,尝试使用容器语法:
-
-```markdown
-::: mermaid
-flowchart TD
- A[开始] --> B[结束]
-:::
-```
-
-**方案 3:使用 Shortcode**
-
-最稳定的方式是使用 Shortcode:
-
-```
-[mermaid]
-flowchart TD
- A[开始] --> B[结束]
-[/mermaid]
-```
-
-### 2. API 错误:`window.mermaid.render(...).then is not a function`
-
-#### 错误示例
-```
-window.mermaid.render(...).then is not a function
-```
-
-#### 原因分析
-- Mermaid 库版本过旧(< 10.0)
-- Mermaid 库未正确加载
-- CDN 加载失败
-
-#### 解决方案
-
-**方案 1:检查 Mermaid 版本**
-
-在浏览器控制台中运行:
-
-```javascript
-console.log(mermaid.version);
-```
-
-如果版本 < 10.0,需要更新 CDN 或使用兼容模式。
-
-**方案 2:检查 CDN 加载**
-
-在浏览器控制台中运行:
-
-```javascript
-console.log(typeof window.mermaid);
-console.log(typeof window.mermaid.render);
-```
-
-如果输出 `undefined`,说明 Mermaid 库未加载。
-
-**解决步骤**:
-1. WordPress 后台 → 外观 → Argon 主题选项
-2. 找到"Mermaid 图表"设置
-3. 检查"启用 Mermaid 支持"是否开启
-4. 尝试切换不同的 CDN 源
-5. 清除浏览器缓存后刷新
-
-**方案 3:使用兼容模式**
-
-主题已内置 Mermaid 8.x 和 10.x 的兼容代码,会自动检测并使用合适的 API。
-
-如果仍然报错,在浏览器控制台查看详细日志:
-
-```javascript
-// 启用调试模式
-localStorage.setItem('argon_mermaid_debug', 'true');
-location.reload();
-```
-
-### 3. 渲染错误:图表显示不完整或错位
-
-#### 原因分析
-- CSS 样式冲突
-- 容器宽度限制
-- 主题切换问题
-
-#### 解决方案
-
-**方案 1:检查容器宽度**
-
-在浏览器开发者工具中检查 `.mermaid-container` 的宽度。
-
-如果宽度过小,添加自定义 CSS:
-
-```css
-.mermaid-container {
- max-width: 100%;
- overflow-x: auto;
-}
-```
-
-**方案 2:检查主题模式**
-
-Mermaid 图表会根据主题模式(日间/夜间)自动切换颜色。
-
-如果颜色不正确:
-1. 切换主题模式(日间 ↔ 夜间)
-2. 刷新页面
-3. 检查主题设置中的 Mermaid 主题配置
-
-**方案 3:强制重新渲染**
-
-在浏览器控制台中运行:
-
-```javascript
-// 清除已渲染标记
-document.querySelectorAll('.mermaid-container').forEach(el => {
- el.remove();
-});
-
-// 重新渲染
-if (typeof MermaidRenderer !== 'undefined') {
- MermaidRenderer.renderAllCharts();
-}
-```
-
-### 4. PJAX 切换后图表消失
-
-#### 原因分析
-- PJAX 切换后未重新渲染
-- 代码块转换未执行
-
-#### 解决方案
-
-**方案 1:检查 PJAX 配置**
-
-确保主题的 PJAX 功能已启用:
-1. WordPress 后台 → 外观 → Argon 主题选项
-2. 找到"PJAX"设置
-3. 确认已启用
-
-**方案 2:手动触发渲染**
-
-在浏览器控制台中运行:
-
-```javascript
-// 监听 PJAX 完成事件
-$(document).on('pjax:complete', function() {
- console.log('[调试] PJAX 完成,重新渲染 Mermaid');
- if (typeof MermaidRenderer !== 'undefined') {
- MermaidRenderer.renderAllCharts();
- }
-});
-```
-
-### 5. 代码块被代码高亮处理
-
-#### 错误表现
-- Mermaid 代码块显示为普通代码
-- 有行号和复制按钮
-- 无法渲染为图表
-
-#### 原因分析
-- 代码块转换未执行
-- 代码高亮在转换之前执行
-
-#### 解决方案
-
-**方案 1:检查执行顺序**
-
-在浏览器控制台中查看日志:
-
-```
-[Mermaid] 转换了 X 个代码块 ← 应该在代码高亮之前
-```
-
-如果没有看到这条日志,说明转换函数未执行。
-
-**方案 2:手动转换**
-
-在浏览器控制台中运行:
-
-```javascript
-// 手动执行转换
-if (typeof convertMermaidCodeblocks === 'function') {
- convertMermaidCodeblocks();
- console.log('[调试] 手动转换完成');
-}
-```
-
-**方案 3:使用其他标记方式**
-
-如果代码块转换不生效,使用容器语法或 Shortcode:
-
-```markdown
-::: mermaid
-flowchart TD
- A --> B
-:::
-```
-
-或
-
-```
-[mermaid]
-flowchart TD
- A --> B
-[/mermaid]
-```
-
-## 调试工具
-
-### 1. 启用调试模式
-
-在浏览器控制台中运行:
-
-```javascript
-// 启用 Mermaid 调试
-localStorage.setItem('argon_mermaid_debug', 'true');
-location.reload();
-```
-
-### 2. 查看转换结果
-
-在浏览器控制台中运行:
-
-```javascript
-// 查看所有转换后的容器
-document.querySelectorAll('.mermaid-from-codeblock').forEach((el, i) => {
- console.log(`容器 ${i + 1}:`, el);
- console.log(`代码内容:`, el.textContent);
-});
-```
-
-### 3. 查看 Mermaid 配置
-
-在浏览器控制台中运行:
-
-```javascript
-// 查看 Mermaid 配置
-console.log('Mermaid 版本:', mermaid.version);
-console.log('Mermaid 配置:', mermaid.getConfig());
-console.log('主题配置:', window.argonMermaidConfig);
-```
-
-### 4. 测试代码语法
-
-访问 [Mermaid Live Editor](https://mermaid.live/) 测试你的 Mermaid 代码是否正确。
-
-### 5. 检查网络请求
-
-在浏览器开发者工具的 Network 标签中:
-1. 刷新页面
-2. 搜索 `mermaid`
-3. 检查 Mermaid 库是否成功加载
-4. 查看 HTTP 状态码(应该是 200)
-
-## 常见问题 FAQ
-
-### Q: 为什么有些图表能渲染,有些不能?
-
-A: 可能原因:
-1. 代码语法错误(使用 Mermaid Live Editor 验证)
-2. 代码块格式不一致(检查缩进和空格)
-3. 特殊字符被转义(使用容器语法或 Shortcode)
-
-### Q: 如何查看详细的错误信息?
-
-A: 打开浏览器控制台(F12),查看 Console 标签页,搜索 `[Mermaid]` 或 `[Argon Mermaid]`。
-
-### Q: 代码块魔改功能如何工作?
-
-A:
-1. 页面加载时,`convertMermaidCodeblocks()` 函数在代码高亮之前执行
-2. 查找所有 `` 元素
-3. 提取纯文本代码并清理缩进
-4. 创建 `` 容器
-5. 替换原始代码块元素
-6. Mermaid 渲染引擎检测并渲染容器
-
-### Q: 如何禁用代码块魔改功能?
-
-A: 如果代码块魔改导致问题,可以使用容器语法或 Shortcode 代替。
-
-### Q: 支持哪些 Mermaid 图表类型?
-
-A: 支持所有 Mermaid 官方图表类型:
-- flowchart / graph(流程图)
-- sequenceDiagram(时序图)
-- classDiagram(类图)
-- stateDiagram(状态图)
-- erDiagram(实体关系图)
-- gantt(甘特图)
-- pie(饼图)
-- journey(用户旅程图)
-- gitGraph(Git 图)
-
-## 获取帮助
-
-如果以上方法都无法解决问题:
-
-1. **收集信息**:
- - 浏览器类型和版本
- - WordPress 版本
- - Argon 主题版本
- - 使用的插件列表
- - 完整的错误信息(控制台截图)
- - Mermaid 代码示例
-
-2. **检查文档**:
- - [用户指南](mermaid-usage-guide.md)
- - [开发者指南](mermaid-developer-guide.md)
- - [FAQ](mermaid-faq.md)
-
-3. **联系支持**:
- - GitHub Issues
- - 主题论坛
- - 技术支持邮箱
-
-## 更新日志
-
-### 2026-01-24
-- ✅ 添加智能缩进清理功能
-- ✅ 添加 Mermaid API 版本检测
-- ✅ 支持 Mermaid 8.x 和 10.x
-- ✅ 增强错误处理和调试日志
-- ✅ 修复代码提取时的空格问题
diff --git a/docs/mermaid-usage-guide.md b/docs/mermaid-usage-guide.md
deleted file mode 100644
index 8376994..0000000
--- a/docs/mermaid-usage-guide.md
+++ /dev/null
@@ -1,439 +0,0 @@
-# Argon 主题 Mermaid 图表使用指南
-
-## 推荐的标记方式
-
-### 1. 标准 Markdown 代码块(推荐)⭐
-
-```markdown
-三个反引号mermaid
-flowchart TD
- A[开始] --> B[处理]
- B --> C[结束]
-三个反引号
-```
-
-**优点**:
-- ✅ 符合标准 Markdown 语法
-- ✅ 在所有 Markdown 编辑器中都能正确显示
-- ✅ 支持语法高亮(编辑器层面)
-- ✅ 易于迁移到其他平台(GitHub、GitLab、Typora 等)
-- ✅ 主题自动拦截处理,避免代码高亮干扰
-- ✅ 支持所有 Mermaid 图表类型
-
-**工作原理**:
-- 主题在代码高亮之前拦截 mermaid 代码块
-- 自动转换为 Mermaid 渲染容器
-- 完全绕过代码高亮和 WordPress 格式化
-
-### 2. Markdown 容器语法(备选)
-
-```markdown
-::: mermaid
-flowchart TD
- A[开始] --> B[处理]
- B --> C[结束]
-:::
-```
-
-**优点**:
-- ✅ 符合 Markdown 扩展规范(VuePress、Docusaurus 等使用相同语法)
-- ✅ 不会被 WP-Markdown 当作代码块处理,避免嵌套问题
-- ✅ 语法简洁,易于编写和阅读
-- ✅ 支持所有 Mermaid 图表类型
-- ✅ 在纯文本编辑器中也很清晰
-- ✅ 易于迁移到其他平台
-
-### 3. Shortcode 格式(兼容)
-
-```
-[mermaid]
-flowchart TD
- A[开始] --> B[处理]
- B --> C[结束]
-[/mermaid]
-```
-
-**优点**:
-- ✅ WordPress 原生支持
-- ✅ 不会被任何插件干扰
-- ✅ 兼容性最好
-
-**缺点**:
-- ❌ 不符合 Markdown 标准
-- ❌ 在其他平台无法使用
-- ❌ 编辑器中不显示为代码块
-
-### 为什么推荐标准 Markdown 代码块?
-
-**标准 Markdown 代码块** (` ```mermaid `) 是最通用的方式:
-
-- **GitHub** 使用这种语法
-- **GitLab** 使用这种语法
-- **Typora** 使用这种语法
-- **VS Code** 使用这种语法
-- **符合 CommonMark 规范**
-
-Argon 主题通过**代码块魔改**技术,在代码高亮之前拦截并转换 mermaid 代码块,因此:
-- 不会被代码高亮处理(无行号、无控制按钮)
-- 不会有字符转义问题(`-->` 保持不变)
-- 不会有嵌套结构问题
-- 完全符合标准 Markdown 语法
-
----
-
-## 使用示例
-
-### 流程图
-
-**标准 Markdown 代码块:**
-```markdown
-三个反引号mermaid
-flowchart LR
- A[用户] --> B{登录?}
- B -->|是| C[显示首页]
- B -->|否| D[跳转登录页]
-三个反引号
-```
-
-**容器语法:**
-```markdown
-::: mermaid
-flowchart LR
- A[用户] --> B{登录?}
- B -->|是| C[显示首页]
- B -->|否| D[跳转登录页]
-:::
-```
-
-### 时序图
-
-```markdown
-三个反引号mermaid
-sequenceDiagram
- Alice->>Bob: Hello Bob!
- Bob-->>Alice: Hi Alice!
- Alice->>Bob: How are you?
-三个反引号
-```
-
-### 类图
-
-```markdown
-三个反引号mermaid
-classDiagram
- class Animal {
- +String name
- +int age
- +makeSound()
- }
- class Dog {
- +String breed
- +bark()
- }
- Animal <|-- Dog
-三个反引号
-```
-
-### 甘特图
-
-```markdown
-三个反引号mermaid
-gantt
- title 项目进度
- dateFormat YYYY-MM-DD
- section 设计
- 需求分析 :a1, 2024-01-01, 7d
- UI设计 :a2, after a1, 5d
- section 开发
- 前端开发 :b1, after a2, 10d
- 后端开发 :b2, after a2, 12d
-三个反引号
-```
-
-### 状态图
-
-```markdown
-三个反引号mermaid
-stateDiagram-v2
- [*] --> 待审核
- 待审核 --> 已通过: 审核通过
- 待审核 --> 已拒绝: 审核拒绝
- 已通过 --> [*]
- 已拒绝 --> [*]
-三个反引号
-```
-
-### 饼图
-
-```markdown
-三个反引号mermaid
-pie title 宠物分布
- "狗" : 386
- "猫" : 85
- "兔子" : 15
-三个反引号
-```
-
----
-
-## 常见问题
-
-### 1. 如何在 WordPress 编辑器中使用?
-
-**在经典编辑器中:**
-1. 切换到"文本"模式(不是"可视化"模式)
-2. 直接输入容器语法
-3. 保存并预览
-
-**在 Gutenberg 编辑器中:**
-1. 添加"自定义 HTML"块或"代码"块
-2. 输入容器语法
-3. 保存并预览
-
-### 2. 已有文章如何迁移?
-
-如果你的文章使用了传统的 Markdown 代码块,可以批量替换:
-
-**查找:**
-```
-三个反引号mermaid
-```
-
-**替换为:**
-```
-::: mermaid
-```
-
-**查找:**
-```
-三个反引号(代码块结束)
-```
-
-**替换为:**
-```
-:::
-```
-
-### 3. 容器语法不生效怎么办?
-
-**可能的原因:**
-- 其他插件或主题处理了容器语法
-- WP-Markdown 插件版本过旧
-
-**解决方案:**
-1. 检查是否有其他 Markdown 插件冲突
-2. 更新 WP-Markdown 插件到最新版本
-3. 查看浏览器控制台的错误信息
-
-### 4. 渲染失败怎么办?
-
-**排查步骤:**
-
-1. **检查语法**
- - 访问 [Mermaid Live Editor](https://mermaid.live/) 验证语法
- - 确保图表类型正确
-
-2. **查看控制台**
- - 按 F12 打开开发者工具
- - 查看 Console 标签页
- - 搜索 `[Argon Mermaid]` 日志
-
-3. **检查主题设置**
- - WordPress 后台 → 外观 → Argon 主题选项
- - 找到"Mermaid 图表"分类
- - 确认"启用 Mermaid 支持"已开启
-
-4. **测试 CDN 连接**
- - 访问测试页面验证 CDN 是否可用
- - 如果 CDN 失败,尝试切换到其他 CDN
-
----
-
-## 最佳实践
-
-### 1. 使用标准 Markdown 代码块
-
-**推荐:**
-```markdown
-三个反引号mermaid
-flowchart TD
- A --> B
-三个反引号
-```
-
-**也可以使用容器语法:**
-```markdown
-::: mermaid
-flowchart TD
- A --> B
-:::
-```
-
-**不推荐(Shortcode):**
-```
-[mermaid]
-flowchart TD
- A --> B
-[/mermaid]
-```
-
-### 2. 避免特殊字符
-
-如果图表中包含特殊字符,使用引号包裹:
-
-```markdown
-::: mermaid
-flowchart TD
- A["包含 <特殊> 字符"] --> B["使用引号包裹"]
-:::
-```
-
-### 3. 测试复杂图表
-
-对于复杂的图表:
-1. 先在 [Mermaid Live Editor](https://mermaid.live/) 中测试
-2. 确认语法正确后再粘贴到文章中
-3. 发布前预览文章
-
-### 4. 启用调试模式
-
-如果遇到问题:
-1. 主题设置 → Mermaid 图表 → 基本设置
-2. 开启"调试模式"
-3. 刷新页面查看控制台日志
-
-### 5. 保持代码简洁
-
-- 使用有意义的节点 ID
-- 添加适当的注释
-- 保持图表结构清晰
-
----
-
-## 技术细节
-
-### 支持的标记格式
-
-Argon 主题支持以下格式(按优先级排序):
-
-1. ` ```mermaid ... ``` ` - 标准 Markdown 代码块 ⭐(推荐)
-2. `::: mermaid ... :::` - Markdown 容器语法(备选)
-3. `[mermaid]...[/mermaid]` - Shortcode 格式(兼容)
-4. `` - 标准格式(WPMD 生成)
-5. `` - Markdown 格式(降级)
-6. `` - 自定义属性格式
-7. `` - 简化格式
-
-### 代码块魔改技术
-
-**工作原理**:
-
-1. **拦截阶段**(在代码高亮之前)
- - 查找所有 `` 元素
- - 提取纯文本代码(使用 `textContent`)
- - 创建 `` 容器
- - 替换原始代码块元素
-
-2. **代码高亮阶段**
- - 处理其他代码块
- - 跳过 mermaid 相关的元素
-
-3. **Mermaid 渲染阶段**
- - 检测所有 Mermaid 容器(包括新的 `mermaid-from-codeblock`)
- - 提取代码并渲染为 SVG 图表
-
-**优势**:
-- ✅ 完全绕过代码高亮干扰
-- ✅ 特殊字符不被转换(`-->` 保持不变)
-- ✅ 换行符正确保留
-- ✅ 支持 PJAX 页面切换
-- ✅ 性能无明显影响
-
-### 代码提取逻辑
-
-1. **检测代码块**
- - CSS 选择器查找标准格式
- - TreeWalker 查找容器语法
- - 代码块魔改:在代码高亮前拦截
-
-2. **提取代码**
- - 代码块魔改格式:直接提取 `textContent`
- - 容器语法:移除 `::: mermaid` 和 `:::`
- - WPMD 格式:正则提取 `document.write()` 内容
- - 标准格式:直接提取文本内容
-
-3. **解码处理**
- - 先解码 HTML 实体(`<` → `<`)
- - 再解码转义字符(`\n` → 换行符)
-
-4. **渲染图表**
- - 使用 Mermaid.js 库渲染为 SVG
- - 应用主题样式(夜间模式适配)
- - 错误时显示友好提示
-
----
-
-## 故障排除
-
-### 问题:容器语法被显示为普通文本
-
-**症状**:`::: mermaid` 被显示在页面上
-
-**原因**:可能被其他插件或主题处理为普通文本
-
-**解决**:
-- 检查是否有其他 Markdown 插件冲突
-- 确认 Argon 主题已更新到最新版本
-- 查看浏览器控制台是否有 JavaScript 错误
-
-### 问题:只显示第一个单词
-
-**症状**:图表渲染失败,错误信息显示 `"text": "flowchart"`
-
-**原因**:代码提取不完整
-
-**解决**:
-- 确保使用最新版本的 Argon 主题
-- 使用容器语法而不是传统代码块
-- 查看控制台日志确认提取到的完整代码
-
-### 问题:HTML 实体未解码
-
-**症状**:图表中显示 `<` 而不是 `<`
-
-**原因**:HTML 实体解码失败
-
-**解决**:
-- Argon 主题会自动解码 HTML 实体
-- 使用容器语法可避免此问题
-- 在 Mermaid 代码中使用引号包裹特殊字符
-
----
-
-## 相关资源
-
-- [Mermaid 官方文档](https://mermaid.js.org/)
-- [Mermaid Live Editor](https://mermaid.live/)
-- [VuePress 容器语法](https://vuepress.vuejs.org/guide/markdown.html#custom-containers)
-- [CommonMark 规范](https://commonmark.org/)
-
----
-
-## 更新日志
-
-### 2026-01-24
-- ✅ **新增代码块魔改支持**:支持标准 Markdown 代码块 (` ```mermaid `)
-- ✅ 在代码高亮之前拦截并转换 mermaid 代码块
-- ✅ 完全绕过代码高亮和 WordPress 格式化
-- ✅ 特殊字符不被转换(`-->` 保持不变)
-- ✅ 换行符正确保留
-- ✅ 支持 PJAX 页面切换
-- ✅ 添加 Markdown 容器语法支持(`::: mermaid ... :::`)
-- ✅ 修复 WP-Markdown 格式的代码提取问题
-- ✅ 改进正则表达式,支持多行代码
-- ✅ 添加降级方案和详细日志
-- ✅ 修复代码高亮干扰 mermaid 渲染的问题(排除 mermaid 代码块)
-- ✅ 修复容器语法中空行导致内容被截断的问题
-- ✅ 修复 WP-Markdown 的 document.write 重复输出问题
-- ✅ 改进容器语法检测,支持跨多个元素的内容收集
-- ✅ 修复换行符丢失问题(将 `
` 标签正确转换为换行符)
diff --git a/docs/mermaid-user-guide.md b/docs/mermaid-user-guide.md
deleted file mode 100644
index 3aa0aeb..0000000
--- a/docs/mermaid-user-guide.md
+++ /dev/null
@@ -1,719 +0,0 @@
-# Argon 主题 Mermaid 图表使用指南
-
-## 目录
-
-1. [功能简介](#功能简介)
-2. [快速开始](#快速开始)
-3. [支持的图表类型](#支持的图表类型)
-4. [使用方法](#使用方法)
-5. [主题设置](#主题设置)
-6. [常见问题](#常见问题)
-7. [最佳实践](#最佳实践)
-8. [故障排除](#故障排除)
-
----
-
-## 功能简介
-
-Argon 主题内置了 Mermaid 图表支持,让您可以在文章中轻松创建各种专业的图表,包括:
-
-- 📊 **流程图** - 展示业务流程和逻辑关系
-- 📈 **时序图** - 描述系统交互和时间顺序
-- 🏗️ **类图** - 展示面向对象的类结构
-- 📉 **状态图** - 表示状态机和状态转换
-- 🥧 **饼图** - 显示数据占比
-- 📅 **甘特图** - 项目进度管理
-- 🗺️ **用户旅程图** - 用户体验流程
-- 🌳 **Git 图** - 版本控制分支可视化
-
-### 主要特性
-
-✅ **零配置使用** - 开箱即用,无需额外插件
-✅ **自动主题切换** - 跟随页面日间/夜间模式
-✅ **智能加载** - 只在需要时加载库文件
-✅ **插件兼容** - 自动检测并避免重复加载
-✅ **错误提示** - 友好的错误信息和调试支持
-✅ **CDN 降级** - 多个 CDN 自动切换,确保可用性
-
----
-
-## 快速开始
-
-### 1. 启用 Mermaid 支持
-
-进入 **WordPress 后台 → 外观 → Argon 主题设置 → Mermaid 图表**,勾选"启用 Mermaid 支持"。
-
-### 2. 在文章中使用
-
-在文章编辑器中,使用以下格式插入 Mermaid 代码:
-
-````markdown
-```mermaid
-flowchart TD
- Start([开始]) --> Process[处理数据]
- Process --> End([结束])
-```
-````
-
-或使用 HTML 格式:
-
-```html
-
-flowchart TD
- Start([开始]) --> Process[处理数据]
- Process --> End([结束])
-
-```
-
-### 3. 发布并查看
-
-保存文章后,在前台页面即可看到渲染后的图表。
-
----
-
-## 支持的图表类型
-
-### 1. 流程图 (Flowchart)
-
-展示流程和决策逻辑。
-
-````markdown
-```mermaid
-flowchart TD
- A[开始] --> B{判断条件}
- B -->|是| C[执行操作 A]
- B -->|否| D[执行操作 B]
- C --> E[结束]
- D --> E
-```
-````
-
-**节点形状:**
-- `[文本]` - 矩形
-- `([文本])` - 圆角矩形
-- `{文本}` - 菱形(判断)
-- `((文本))` - 圆形
-- `[[文本]]` - 子程序
-
-### 2. 时序图 (Sequence Diagram)
-
-描述对象之间的交互顺序。
-
-````markdown
-```mermaid
-sequenceDiagram
- participant 用户
- participant 服务器
- participant 数据库
-
- 用户->>服务器: 发送请求
- 服务器->>数据库: 查询数据
- 数据库-->>服务器: 返回结果
- 服务器-->>用户: 响应数据
-```
-````
-
-**箭头类型:**
-- `->` - 实线箭头
-- `-->` - 虚线箭头
-- `->>` - 实线箭头(带箭头)
-- `-->>` - 虚线箭头(带箭头)
-
-### 3. 类图 (Class Diagram)
-
-展示面向对象的类结构。
-
-````markdown
-```mermaid
-classDiagram
- class Animal {
- +String name
- +int age
- +makeSound()
- }
- class Dog {
- +String breed
- +bark()
- }
- class Cat {
- +meow()
- }
- Animal <|-- Dog
- Animal <|-- Cat
-```
-````
-
-### 4. 状态图 (State Diagram)
-
-表示状态机和状态转换。
-
-````markdown
-```mermaid
-stateDiagram-v2
- [*] --> 待处理
- 待处理 --> 处理中: 开始处理
- 处理中 --> 已完成: 处理成功
- 处理中 --> 失败: 处理失败
- 失败 --> 待处理: 重试
- 已完成 --> [*]
-```
-````
-
-### 5. 饼图 (Pie Chart)
-
-显示数据占比。
-
-````markdown
-```mermaid
-pie title 浏览器市场份额
- "Chrome" : 65
- "Safari" : 15
- "Firefox" : 10
- "Edge" : 7
- "其他" : 3
-```
-````
-
-### 6. 甘特图 (Gantt Chart)
-
-项目进度管理。
-
-````markdown
-```mermaid
-gantt
- title 项目开发计划
- dateFormat YYYY-MM-DD
- section 设计阶段
- 需求分析 :a1, 2024-01-01, 7d
- UI 设计 :a2, after a1, 5d
- section 开发阶段
- 前端开发 :b1, after a2, 10d
- 后端开发 :b2, after a2, 12d
- section 测试阶段
- 功能测试 :c1, after b1, 5d
-```
-````
-
-### 7. 用户旅程图 (User Journey)
-
-描述用户体验流程。
-
-````markdown
-```mermaid
-journey
- title 用户购物旅程
- section 浏览商品
- 访问网站: 5: 用户
- 搜索商品: 3: 用户
- 查看详情: 4: 用户
- section 下单
- 加入购物车: 4: 用户
- 填写信息: 2: 用户
- 支付: 3: 用户
- section 收货
- 等待发货: 2: 用户
- 收到商品: 5: 用户
-```
-````
-
-### 8. Git 图 (Git Graph)
-
-版本控制分支可视化。
-
-````markdown
-```mermaid
-gitGraph
- commit
- commit
- branch develop
- checkout develop
- commit
- commit
- checkout main
- merge develop
- commit
-```
-````
-
----
-
-## 使用方法
-
-### 在 Markdown 编辑器中使用
-
-如果您使用 Markdown 编辑器(如 WP-Markdown),直接使用代码块语法:
-
-````markdown
-```mermaid
-flowchart LR
- A --> B
- B --> C
-```
-````
-
-### 在 Gutenberg 编辑器中使用
-
-1. 添加"代码"块或"自定义 HTML"块
-2. 输入 Mermaid 代码
-3. 使用 `` 包裹
-
-```html
-
-flowchart LR
- A --> B
- B --> C
-
-```
-
-### 在经典编辑器中使用
-
-切换到"文本"模式,使用 HTML 格式:
-
-```html
-
-flowchart LR
- A --> B
- B --> C
-
-```
-
----
-
-## 主题设置
-
-### 基本设置
-
-#### 启用 Mermaid 支持
-
-**位置:** Argon 主题设置 → Mermaid 图表 → 基本设置
-
-勾选此选项以启用 Mermaid 图表渲染功能。
-
-#### CDN 来源
-
-选择 Mermaid 库的加载来源:
-
-- **jsDelivr CDN** (推荐) - 全球 CDN,速度快,稳定性高
-- **unpkg CDN** - 备用 CDN
-- **自定义 CDN 地址** - 使用自己的 CDN 或镜像
-- **本地文件** - 使用主题目录中的本地文件
-
-**建议:** 使用 jsDelivr CDN,主题会自动在多个 CDN 之间切换以确保可用性。
-
-#### 自定义 CDN 地址
-
-当选择"自定义 CDN 地址"时,输入完整的 Mermaid 库 URL。
-
-**格式要求:**
-- 必须是有效的 URL
-- 必须以 `.js` 结尾
-- 必须使用 `http://` 或 `https://` 协议
-
-**示例:**
-```
-https://cdn.example.com/mermaid@10.0.0/mermaid.min.js
-```
-
-### 外观设置
-
-#### 图表主题
-
-选择 Mermaid 图表的配色主题:
-
-- **自动切换** (推荐) - 跟随页面日间/夜间模式自动切换
-- **默认主题** - 浅色主题,适合日间模式
-- **深色主题** - 深色主题,适合夜间模式
-- **森林主题** - 绿色主题
-- **中性主题** - 灰色主题
-
-**建议:** 使用"自动切换",让图表主题与页面主题保持一致。
-
-### 高级选项
-
-#### 使用本地镜像
-
-启用后,如果检测到主题目录中存在 Mermaid 库文件,将优先使用本地文件而不是 CDN。
-
-**本地文件路径:**
-```
-wp-content/themes/argon/assets/vendor/mermaid/mermaid.min.js
-```
-
-**适用场景:**
-- 内网环境无法访问外部 CDN
-- 需要使用特定版本的 Mermaid
-- 追求极致的加载速度
-
-#### 调试模式
-
-启用后,将在浏览器控制台输出详细的 Mermaid 渲染日志。
-
-**日志内容包括:**
-- 初始化状态
-- 检测到的代码块数量
-- 渲染成功/失败信息
-- 主题切换记录
-- CDN 加载状态
-
-**使用方法:**
-1. 启用调试模式
-2. 打开浏览器开发者工具(F12)
-3. 切换到"控制台"标签
-4. 查看以 `[Argon Mermaid]` 开头的日志
-
-#### 插件兼容性检测
-
-主题会自动检测已安装的 Mermaid 插件,避免重复加载库文件。
-
-**支持的插件:**
-- WP Githuber MD
-- Markdown Block
-- Code Syntax Block
-
-**兼容策略:**
-- 如果检测到插件,主题将只提供样式增强
-- 如果未检测到插件,主题将负责加载 Mermaid 库
-- 如果检测到多个插件,会显示警告信息
-
----
-
-## 常见问题
-
-### Q1: 图表不显示,只显示代码?
-
-**可能原因:**
-1. 未启用 Mermaid 支持
-2. 代码格式不正确
-3. JavaScript 加载失败
-
-**解决方法:**
-1. 检查主题设置中是否启用了 Mermaid 支持
-2. 确认代码格式正确(参考本文档示例)
-3. 打开浏览器控制台查看是否有错误信息
-4. 启用调试模式查看详细日志
-
-### Q2: 图表显示"渲染失败"错误?
-
-**可能原因:**
-1. Mermaid 代码语法错误
-2. 使用了不支持的图表类型
-3. 代码格式不符合规范
-
-**解决方法:**
-1. 检查代码语法是否正确
-2. 使用 [Mermaid Live Editor](https://mermaid.live/) 验证代码
-3. 查看错误提示中的详细信息
-4. 参考本文档中的示例代码
-
-### Q3: 图表在夜间模式下看不清?
-
-**解决方法:**
-1. 进入主题设置 → Mermaid 图表 → 外观设置
-2. 将"图表主题"设置为"自动切换"
-3. 图表会自动跟随页面主题切换
-
-### Q4: CDN 加载失败怎么办?
-
-**主题已内置降级机制:**
-1. 主 CDN 失败时,自动尝试备用 CDN
-2. 所有 CDN 都失败时,显示友好的错误提示
-
-**手动解决:**
-1. 切换到其他 CDN 来源
-2. 使用自定义 CDN 地址
-3. 下载本地文件并启用"使用本地镜像"
-
-### Q5: 与其他插件冲突?
-
-**主题已内置兼容机制:**
-- 自动检测已安装的 Mermaid 插件
-- 避免重复加载库文件
-- 只提供样式增强功能
-
-**如果仍有冲突:**
-1. 查看插件兼容性检测结果
-2. 禁用主题的 Mermaid 支持,使用插件
-3. 或禁用插件,使用主题的 Mermaid 支持
-
-### Q6: 如何在评论中使用 Mermaid?
-
-**方法:**
-1. 评论中使用 HTML 格式
-2. 使用 `` 包裹代码
-
-**示例:**
-```html
-
-flowchart LR
- A --> B
-
-```
-
-**注意:** 需要确保评论允许 HTML 标签。
-
-### Q7: 图表太大,超出容器?
-
-**解决方法:**
-1. Mermaid 图表会自动适应容器宽度
-2. 如果图表过于复杂,考虑简化或拆分
-3. 使用 CSS 自定义样式调整大小
-
-**自定义样式示例:**
-```css
-.mermaid-container {
- max-width: 100%;
- overflow-x: auto;
-}
-```
-
-### Q8: 如何导出图表为图片?
-
-**方法 1:使用浏览器截图**
-1. 在浏览器中打开文章
-2. 使用截图工具截取图表部分
-
-**方法 2:使用 Mermaid Live Editor**
-1. 访问 [Mermaid Live Editor](https://mermaid.live/)
-2. 粘贴代码
-3. 点击"Export"导出为 PNG/SVG
-
-**方法 3:使用浏览器开发者工具**
-1. 右键点击图表 → 检查元素
-2. 找到 SVG 元素
-3. 复制 SVG 代码或导出为图片
-
----
-
-## 最佳实践
-
-### 1. 代码格式规范
-
-**推荐:**
-```mermaid
-flowchart TD
- A[开始] --> B[处理]
- B --> C[结束]
-```
-
-**不推荐:**
-```mermaid
-flowchart TD
-A[开始]-->B[处理]
-B-->C[结束]
-```
-
-**建议:**
-- 使用缩进保持代码可读性
-- 箭头两侧添加空格
-- 每行一个语句
-
-### 2. 节点命名
-
-**推荐:**
-```mermaid
-flowchart TD
- start([开始])
- process[处理数据]
- decision{是否成功?}
-```
-
-**不推荐:**
-```mermaid
-flowchart TD
- a([开始])
- b[处理数据]
- c{是否成功?}
-```
-
-**建议:**
-- 使用有意义的节点 ID
-- 节点文本简洁明了
-- 避免使用特殊字符
-
-### 3. 图表复杂度
-
-**建议:**
-- 单个图表不超过 20 个节点
-- 复杂流程拆分为多个图表
-- 使用子图组织相关节点
-
-**示例:**
-```mermaid
-flowchart TD
- subgraph 输入阶段
- A[接收数据] --> B[验证数据]
- end
- subgraph 处理阶段
- B --> C[处理数据]
- C --> D[保存结果]
- end
-```
-
-### 4. 性能优化
-
-**建议:**
-- 避免在一篇文章中使用过多图表(建议不超过 10 个)
-- 复杂图表考虑使用图片替代
-- 启用 CDN 加速加载
-
-### 5. 可访问性
-
-**建议:**
-- 为图表添加文字说明
-- 使用清晰的节点文本
-- 避免仅依赖颜色传达信息
-
-**示例:**
-```html
-
- 以下是用户注册流程图:
-
- flowchart TD
- Start([用户访问注册页]) --> Input[填写信息]
- Input --> Validate{验证信息}
- Validate -->|通过| Register[注册成功]
- Validate -->|失败| Input
-
-
-```
-
-### 6. 版本控制
-
-**建议:**
-- 在文章中记录 Mermaid 代码版本
-- 复杂图表保存源代码备份
-- 使用注释说明图表用途
-
-**示例:**
-```mermaid
-%% 用户注册流程图
-%% 版本: 1.0
-%% 更新日期: 2024-01-20
-flowchart TD
- Start --> End
-```
-
----
-
-## 故障排除
-
-### 调试步骤
-
-1. **启用调试模式**
- - 进入主题设置 → Mermaid 图表 → 高级选项
- - 勾选"启用调试模式"
-
-2. **打开浏览器控制台**
- - 按 F12 打开开发者工具
- - 切换到"控制台"标签
-
-3. **查看日志信息**
- - 查找以 `[Argon Mermaid]` 开头的日志
- - 记录错误信息和警告
-
-4. **验证代码语法**
- - 访问 [Mermaid Live Editor](https://mermaid.live/)
- - 粘贴代码并检查是否有语法错误
-
-5. **检查网络请求**
- - 在开发者工具中切换到"网络"标签
- - 查看 Mermaid 库是否成功加载
- - 检查是否有 404 或其他错误
-
-### 常见错误代码
-
-#### 错误 1: Parse error on line X
-
-**原因:** Mermaid 代码语法错误
-
-**解决:**
-1. 检查代码语法是否正确
-2. 使用 Mermaid Live Editor 验证
-3. 参考官方文档修正语法
-
-#### 错误 2: Mermaid 库未加载
-
-**原因:** CDN 加载失败或被阻止
-
-**解决:**
-1. 检查网络连接
-2. 切换到其他 CDN 来源
-3. 使用本地文件
-
-#### 错误 3: 主题切换失败
-
-**原因:** 配置错误或 JavaScript 冲突
-
-**解决:**
-1. 检查主题设置是否正确
-2. 禁用其他可能冲突的插件
-3. 清除浏览器缓存
-
-### 获取帮助
-
-如果以上方法都无法解决问题,请:
-
-1. **收集信息:**
- - WordPress 版本
- - Argon 主题版本
- - 浏览器类型和版本
- - 错误信息和日志
- - 问题复现步骤
-
-2. **提交问题:**
- - 访问 [Argon 主题 GitHub](https://github.com/solstice23/argon-theme/issues)
- - 创建新 Issue
- - 提供详细的问题描述和信息
-
-3. **社区支持:**
- - 访问主题官方论坛
- - 搜索类似问题
- - 向社区求助
-
----
-
-## 相关资源
-
-### 官方文档
-
-- [Mermaid 官方文档](https://mermaid.js.org/)
-- [Mermaid 语法参考](https://mermaid.js.org/intro/syntax-reference.html)
-- [Mermaid Live Editor](https://mermaid.live/)
-
-### 教程和示例
-
-- [Mermaid 快速入门](https://mermaid.js.org/intro/getting-started.html)
-- [流程图教程](https://mermaid.js.org/syntax/flowchart.html)
-- [时序图教程](https://mermaid.js.org/syntax/sequenceDiagram.html)
-- [类图教程](https://mermaid.js.org/syntax/classDiagram.html)
-
-### 工具和插件
-
-- [Mermaid Chart](https://www.mermaidchart.com/) - 在线图表编辑器
-- [VS Code Mermaid 插件](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-mermaid)
-- [Chrome Mermaid 扩展](https://chrome.google.com/webstore/search/mermaid)
-
----
-
-## 更新日志
-
-### 版本 1.0.0 (2024-01-22)
-
-- ✨ 初始版本发布
-- ✅ 支持所有主要图表类型
-- ✅ 自动主题切换
-- ✅ 插件兼容性检测
-- ✅ CDN 降级机制
-- ✅ 错误提示和调试支持
-
----
-
-## 反馈与建议
-
-如果您在使用过程中有任何问题或建议,欢迎:
-
-- 📧 发送邮件至主题作者
-- 💬 在 GitHub 上提交 Issue
-- 🌟 为项目点赞支持
-
-感谢您使用 Argon 主题!
diff --git a/functions.php b/functions.php
index 911d220..06172a3 100644
--- a/functions.php
+++ b/functions.php
@@ -4585,45 +4585,7 @@ function shortcode_video($attr,$content=""){
$out .= "";
return $out;
}
-add_shortcode('mermaid','shortcode_mermaid');
-function shortcode_mermaid($attr,$content=""){
- // 预处理内容:移除 WordPress 自动添加的 和
标签
- $content = shortcode_content_preprocess($attr, $content);
-
- // 获取参数
- $theme = isset( $attr['theme'] ) ? $attr['theme'] : 'default';
- $width = isset( $attr['width'] ) ? $attr['width'] : '100%';
- $height = isset( $attr['height'] ) ? $attr['height'] : 'auto';
- $align = isset( $attr['align'] ) ? $attr['align'] : 'center';
-
- // 生成唯一 ID
- $chart_id = 'mermaid-' . mt_rand(1000000000, 9999999999);
-
- // 构建输出
- $out = '
';
- $out .= '';
- $out .= esc_html($content);
- $out .= '';
- $out .= '';
-
- return $out;
-}
-/**
- * 从内容中移除 Mermaid shortcode,用于文章预览
- * 避免在预览中显示原始 Mermaid 代码
- *
- * @param string $content 文章内容
- * @return string 移除 Mermaid shortcode 后的内容
- */
-function argon_remove_mermaid_from_preview($content) {
- // 移除 [mermaid]...[/mermaid] shortcode
- $content = preg_replace('/\[mermaid[^\]]*\].*?\[\/mermaid\]/is', '[Mermaid 图表]', $content);
- return $content;
-}
add_shortcode('hide_reading_time','shortcode_hide_reading_time');
function shortcode_hide_reading_time($attr,$content=""){
return "";
@@ -10975,8 +10937,6 @@ function argon_normalize_social_url($platform, $input) {
return esc_url($base_urls[$platform] . $username);
}
-// ==========================================================================
-// Mermaid 图表支持 - 配置管理
// ==========================================================================
/**
@@ -11211,8 +11171,6 @@ function argon_update_mermaid_settings($settings) {
];
}
-// ==========================================================================
-// Mermaid 图表支持 - 库加载器
// ==========================================================================
/**
diff --git a/header.php b/header.php
index 3c104ce..ede4a70 100644
--- a/header.php
+++ b/header.php
@@ -329,59 +329,47 @@
';
-
- // 调用函数添加属性
- $modified_tag = argon_add_mermaid_async_attribute($original_tag, 'mermaid');
-
- // 验证包含 async 属性
- if (strpos($modified_tag, 'async') === false) {
- echo "❌ 失败: 缺少 async 属性\n";
- return false;
- }
- echo "✓ 包含 async 属性\n";
-
- // 验证包含 onerror 属性
- if (strpos($modified_tag, 'onerror') === false) {
- echo "❌ 失败: 缺少 onerror 属性\n";
- return false;
- }
- echo "✓ 包含 onerror 属性\n";
-
- // 验证 onerror 调用正确的函数
- if (strpos($modified_tag, 'argonMermaidLoadFallback()') === false) {
- echo "❌ 失败: onerror 函数名不正确\n";
- return false;
- }
- echo "✓ onerror 调用正确的函数\n";
-
- echo "修改后的标签: {$modified_tag}\n";
- echo "✅ 通过: onerror 属性添加正确\n\n";
- return true;
-}
-
-/**
- * 测试 4: 验证非 Mermaid 脚本不受影响
- */
-function test_other_scripts_unaffected() {
- echo "测试 4: 验证非 Mermaid 脚本不受影响\n";
- echo str_repeat('-', 50) . "\n";
-
- // 模拟其他脚本标签
- $original_tag = '';
-
- // 调用函数
- $modified_tag = argon_add_mermaid_async_attribute($original_tag, 'other-script');
-
- // 验证标签未被修改
- if ($original_tag === $modified_tag) {
- echo "✓ 非 Mermaid 脚本未被修改\n";
- echo "✅ 通过: 其他脚本不受影响\n\n";
- return true;
- } else {
- echo "❌ 失败: 非 Mermaid 脚本被错误修改\n";
- echo "原始: {$original_tag}\n";
- echo "修改: {$modified_tag}\n\n";
- return false;
- }
-}
-
-/**
- * 测试 5: 验证 JSON 编码的备用 URL
- */
-function test_json_encoded_urls() {
- echo "测试 5: 验证 JSON 编码的备用 URL\n";
- echo str_repeat('-', 50) . "\n";
-
- $fallback_urls = argon_get_mermaid_fallback_urls();
- $json = json_encode($fallback_urls);
-
- // 验证 JSON 编码成功
- if ($json === false) {
- echo "❌ 失败: JSON 编码失败\n";
- return false;
- }
- echo "✓ JSON 编码成功\n";
-
- // 验证可以解码回数组
- $decoded = json_decode($json, true);
- if (!is_array($decoded)) {
- echo "❌ 失败: JSON 解码失败\n";
- return false;
- }
- echo "✓ JSON 解码成功\n";
-
- // 验证解码后的数组与原数组一致
- if ($decoded !== $fallback_urls) {
- echo "❌ 失败: 解码后的数组与原数组不一致\n";
- return false;
- }
- echo "✓ 解码后的数组与原数组一致\n";
-
- echo "JSON: {$json}\n";
- echo "✅ 通过: JSON 编码验证成功\n\n";
- return true;
-}
-
-// 运行所有测试
-echo "\n";
-echo "=================================================\n";
-echo "Mermaid 库加载失败降级处理测试\n";
-echo "=================================================\n\n";
-
-$tests = [
- 'test_fallback_urls',
- 'test_fallback_script_generation',
- 'test_onerror_attribute',
- 'test_other_scripts_unaffected',
- 'test_json_encoded_urls'
-];
-
-$passed = 0;
-$failed = 0;
-
-foreach ($tests as $test) {
- if ($test()) {
- $passed++;
- } else {
- $failed++;
- }
-}
-
-// 输出测试总结
-echo "=================================================\n";
-echo "测试总结\n";
-echo "=================================================\n";
-echo "通过: {$passed} 个测试\n";
-echo "失败: {$failed} 个测试\n";
-
-if ($failed === 0) {
- echo "\n✅ 所有测试通过!\n";
- exit(0);
-} else {
- echo "\n❌ 部分测试失败,请检查实现。\n";
- exit(1);
-}
diff --git a/tests/test-mermaid-loader.php b/tests/test-mermaid-loader.php
deleted file mode 100644
index d589528..0000000
--- a/tests/test-mermaid-loader.php
+++ /dev/null
@@ -1,136 +0,0 @@
-flowchart TD\nA-->B';
-test_assert(argon_has_mermaid_content($content1), "测试 1: 检测 div class=\"mermaid\" 格式");
-
-// 测试 2: 检测 code class="language-mermaid" 格式
-$content2 = 'graph LR\nA-->B
';
-test_assert(argon_has_mermaid_content($content2), "测试 2: 检测 code class=\"language-mermaid\" 格式");
-
-// 测试 3: 检测 pre data-lang="mermaid" 格式
-$content3 = 'sequenceDiagram\nA->>B: Hello
';
-test_assert(argon_has_mermaid_content($content3), "测试 3: 检测 pre data-lang=\"mermaid\" 格式");
-
-// 测试 4: 检测 code class="mermaid" 格式
-$content4 = 'pie title Pets\n"Dogs" : 386';
-test_assert(argon_has_mermaid_content($content4), "测试 4: 检测 code class=\"mermaid\" 格式");
-
-// 测试 5: 不包含 Mermaid 代码块
-$content5 = 'This is a regular paragraph
console.log("hello")';
-test_assert(!argon_has_mermaid_content($content5), "测试 5: 不包含 Mermaid 代码块");
-
-// 测试 6: 空内容
-test_assert(!argon_has_mermaid_content(''), "测试 6: 空内容返回 false");
-
-// 测试 7: 检测多个 class 的情况
-$content7 = 'flowchart TD';
-test_assert(argon_has_mermaid_content($content7), "测试 7: 检测多个 class 的情况");
-
-// 测试 8: 大小写不敏感
-$content8 = 'flowchart TD';
-test_assert(argon_has_mermaid_content($content8), "测试 8: 大小写不敏感");
-
-echo "\n=== 测试 argon_get_mermaid_library_url() ===\n\n";
-
-// 测试 9: jsdelivr CDN
-update_option('argon_mermaid_cdn_source', 'jsdelivr');
-update_option('argon_mermaid_use_local', false);
-$url9 = argon_get_mermaid_library_url();
-test_assert_contains('cdn.jsdelivr.net', $url9, "测试 9: jsdelivr CDN URL");
-
-// 测试 10: unpkg CDN
-update_option('argon_mermaid_cdn_source', 'unpkg');
-update_option('argon_mermaid_use_local', false);
-$url10 = argon_get_mermaid_library_url();
-test_assert_contains('unpkg.com', $url10, "测试 10: unpkg CDN URL");
-
-// 测试 11: 本地镜像
-update_option('argon_mermaid_use_local', true);
-$url11 = argon_get_mermaid_library_url();
-test_assert_contains('/assets/vendor/mermaid/', $url11, "测试 11: 本地镜像 URL");
-
-// 测试 12: 自定义 CDN(有效 URL)
-update_option('argon_mermaid_cdn_source', 'custom');
-update_option('argon_mermaid_cdn_custom_url', 'https://example.com/mermaid.min.js');
-update_option('argon_mermaid_use_local', false);
-$url12 = argon_get_mermaid_library_url();
-test_assert_equals('https://example.com/mermaid.min.js', $url12, "测试 12: 自定义 CDN URL");
-
-// 测试 13: 自定义 CDN(无效 URL,降级到 jsdelivr)
-update_option('argon_mermaid_cdn_source', 'custom');
-update_option('argon_mermaid_cdn_custom_url', 'invalid-url');
-update_option('argon_mermaid_use_local', false);
-$url13 = argon_get_mermaid_library_url();
-test_assert_contains('cdn.jsdelivr.net', $url13, "测试 13: 无效自定义 URL 降级到 jsdelivr");
-
-// 测试 14: 本地镜像优先级最高
-update_option('argon_mermaid_cdn_source', 'jsdelivr');
-update_option('argon_mermaid_use_local', true);
-$url14 = argon_get_mermaid_library_url();
-test_assert_contains('/assets/vendor/mermaid/', $url14, "测试 14: 本地镜像优先级最高");
-
-// 测试 15: 未知 CDN 来源降级到 jsdelivr
-update_option('argon_mermaid_cdn_source', 'unknown-source');
-update_option('argon_mermaid_use_local', false);
-$url15 = argon_get_mermaid_library_url();
-test_assert_contains('cdn.jsdelivr.net', $url15, "测试 15: 未知 CDN 来源降级到 jsdelivr");
-
-echo "\n=== 测试 argon_get_mermaid_fallback_urls() ===\n\n";
-
-// 测试 16: 备用 URL 列表
-$fallback_urls = argon_get_mermaid_fallback_urls();
-test_assert(is_array($fallback_urls), "测试 16: 返回数组");
-test_assert(count($fallback_urls) === 3, "测试 17: 包含 3 个备用 URL");
-test_assert_contains('cdn.jsdelivr.net', $fallback_urls[0], "测试 18: 第一个备用 URL 是 jsdelivr");
-test_assert_contains('unpkg.com', $fallback_urls[1], "测试 19: 第二个备用 URL 是 unpkg");
-test_assert_contains('/assets/vendor/mermaid/', $fallback_urls[2], "测试 20: 第三个备用 URL 是本地");
-
-echo "\n=== 所有测试完成 ===\n";
diff --git a/tests/test-mermaid-wait-mechanism.md b/tests/test-mermaid-wait-mechanism.md
deleted file mode 100644
index 372f050..0000000
--- a/tests/test-mermaid-wait-mechanism.md
+++ /dev/null
@@ -1,124 +0,0 @@
-# Mermaid 库加载等待机制测试
-
-## 测试目标
-验证任务 2.1 的实现:Mermaid 库加载等待机制
-
-## 实现的功能
-
-### 1. waitForMermaid() 函数
-- ✅ 返回 Promise
-- ✅ 检查 Mermaid 库是否已加载
-- ✅ 如果已加载,立即返回 true
-- ✅ 如果未加载,使用轮询机制等待(每 100ms 检查一次)
-- ✅ 设置超时机制(默认 5000ms)
-- ✅ 超时后返回 false
-- ✅ 记录加载时间和状态日志
-
-### 2. 集成到渲染流程
-- ✅ renderAllCharts() 改为 async 函数
-- ✅ 渲染前调用 waitForMermaid() 等待库加载
-- ✅ 库加载失败时记录错误并停止渲染
-- ✅ init() 函数改为 async 函数
-- ✅ 初始化时使用 waitForMermaid() 替代原来的 setInterval
-
-## 测试场景
-
-### 场景 1: 库已加载
-**步骤:**
-1. 确保 Mermaid 库已加载
-2. 调用 waitForMermaid()
-3. 验证立即返回 true
-
-**预期结果:**
-- 函数立即返回 true
-- 日志显示 "Mermaid 库已加载"
-
-### 场景 2: 库延迟加载
-**步骤:**
-1. 页面加载时 Mermaid 库未加载
-2. 调用 waitForMermaid()
-3. 500ms 后加载 Mermaid 库
-4. 验证函数等待并返回 true
-
-**预期结果:**
-- 函数等待库加载
-- 库加载后返回 true
-- 日志显示加载耗时(约 500ms)
-
-### 场景 3: 库加载超时
-**步骤:**
-1. 页面加载时 Mermaid 库未加载
-2. 调用 waitForMermaid(2000) 设置 2 秒超时
-3. 2 秒内库未加载
-4. 验证函数超时返回 false
-
-**预期结果:**
-- 函数等待 2 秒后返回 false
-- 日志显示 "Mermaid 库加载超时(2000ms)"
-
-### 场景 4: PJAX 页面切换
-**步骤:**
-1. 通过 PJAX 跳转到包含 Mermaid 图表的页面
-2. renderAllCharts() 被调用
-3. 验证等待库加载后再渲染
-
-**预期结果:**
-- 等待库加载完成
-- 成功渲染所有图表
-- 不显示原始代码
-
-## 代码规范检查
-
-### ✅ JSDoc 注释
-```javascript
-/**
- * 等待 Mermaid 库加载
- * 需求 2.6: 清除缓存后首次加载时等待 Mermaid 库完全加载
- * 需求 4.1: 开始渲染前检查 Mermaid 库是否已加载
- * 需求 4.2: Mermaid 库未加载时等待库加载或显示错误提示
- * @param {number} timeout - 超时时间(毫秒),默认 5000ms
- * @returns {Promise} 是否加载成功
- */
-```
-
-### ✅ 代码风格
-- Tab 缩进
-- 单引号字符串
-- 严格相等 (===)
-- 语句末尾分号
-- 使用 const/let(不使用 var)
-
-### ✅ 错误处理
-- 超时时记录错误日志
-- 加载失败时停止渲染
-- 提供友好的错误提示
-
-## 性能考虑
-
-### 轮询间隔
-- 每 100ms 检查一次(平衡响应速度和性能)
-- 避免过于频繁的检查
-
-### 超时设置
-- 默认 5000ms(5 秒)
-- 可自定义超时时间
-- 防止无限等待
-
-### 日志记录
-- 记录加载耗时
-- 便于性能分析和调试
-
-## 需求映射
-
-| 需求 ID | 需求描述 | 实现状态 |
-|---------|----------|----------|
-| 2.6 | 清除缓存后首次加载时等待 Mermaid 库完全加载 | ✅ |
-| 4.1 | 开始渲染前检查 Mermaid 库是否已加载 | ✅ |
-| 4.2 | Mermaid 库未加载时等待库加载或显示错误提示 | ✅ |
-
-## 下一步
-
-任务 2.1 已完成,可以继续:
-- 任务 2.2: 优化 Mermaid 初始化配置
-- 任务 2.3: 添加语法错误处理和友好提示
-- 任务 2.4: 测试各种图表类型
diff --git a/tests/test-pjax-mermaid-rendering.md b/tests/test-pjax-mermaid-rendering.md
deleted file mode 100644
index 9369b4c..0000000
--- a/tests/test-pjax-mermaid-rendering.md
+++ /dev/null
@@ -1,413 +0,0 @@
-# PJAX Mermaid 渲染测试报告
-
-## 测试目标
-
-验证 PJAX 页面切换后 Mermaid 代码块能被正确检测和渲染。
-
-## 测试环境
-
-- **测试日期**: 2024-01-22
-- **主题版本**: Argon
-- **浏览器**: Chrome/Firefox/Safari
-- **测试文件**: argontheme.js (行 3247-3255, 4786-5400)
-
-## 测试用例
-
-### 测试用例 1: PJAX 页面切换后的代码块检测
-
-**需求**: 1.1, 1.2, 3.1-3.5
-
-**测试步骤**:
-1. 在首页点击包含 Mermaid 图表的文章卡片
-2. 等待 PJAX 加载完成
-3. 检查控制台日志,确认检测到 Mermaid 代码块
-4. 检查页面 DOM,确认代码块被正确识别
-
-**预期结果**:
-- ✅ PJAX complete 事件触发后调用 `MermaidRenderer.renderAllCharts()`
-- ✅ 控制台显示: `检测到 X 个未渲染的 Mermaid 代码块`
-- ✅ 代码块元素被正确识别(`` 或 ``)
-- ✅ 已渲染的代码块被过滤(不会重复渲染)
-
-**实际结果**:
-
-**状态**: ⏳ 待测试
-
----
-
-### 测试用例 2: PJAX 页面切换后的图表渲染
-
-**需求**: 1.3, 1.4, 4.1-4.6
-
-**测试步骤**:
-1. 在首页点击包含 Mermaid 图表的文章卡片
-2. 等待 PJAX 加载完成
-3. 观察页面上的 Mermaid 代码块
-4. 检查是否显示为 SVG 图表而不是原始文本
-
-**预期结果**:
-- ✅ 代码块被替换为 `.mermaid-container` 容器
-- ✅ 容器内包含渲染后的 SVG 图表
-- ✅ 图表正常显示,不是原始文本
-- ✅ 控制台显示: `准备渲染图表: mermaid-chart-xxx`
-- ✅ 控制台显示: `图表渲染成功: mermaid-chart-xxx`
-
-**实际结果**:
-
-**状态**: ⏳ 待测试
-
----
-
-### 测试用例 3: 多次 PJAX 跳转不会重复渲染
-
-**需求**: 1.3, 3.5, 16.1-16.5
-
-**测试步骤**:
-1. 从首页跳转到文章 A(包含 Mermaid 图表)
-2. 从文章 A 跳转到文章 B(包含 Mermaid 图表)
-3. 从文章 B 返回文章 A
-4. 检查图表是否重复渲染
-
-**预期结果**:
-- ✅ 每次跳转前调用 `cleanupMermaidInstances()` 清理旧实例
-- ✅ 控制台显示: `Mermaid 实例已清理`
-- ✅ 每次跳转后重新渲染图表
-- ✅ 不会出现重复的图表或错误
-- ✅ 渲染状态标记正确更新(`data-mermaid-rendered`)
-
-**实际结果**:
-
-**状态**: ⏳ 待测试
-
----
-
-### 测试用例 4: PJAX 加载失败的降级处理
-
-**需求**: 7.1-7.4
-
-**测试步骤**:
-1. 模拟 PJAX 加载失败(网络错误或超时)
-2. 观察页面行为
-3. 检查错误提示
-
-**预期结果**:
-- ✅ 页面回退到传统的页面跳转
-- ✅ Mermaid 图表在新页面正常渲染
-- ✅ 控制台记录错误信息
-- ✅ 用户体验不受影响
-
-**实际结果**:
-
-**状态**: ⏳ 待测试
-
----
-
-### 测试用例 5: 不同格式的 Mermaid 代码块
-
-**需求**: 3.1-3.4
-
-**测试步骤**:
-1. 创建包含不同格式 Mermaid 代码块的测试页面:
- - ``
- - ``
- - ``
-2. 通过 PJAX 跳转到测试页面
-3. 检查所有格式的代码块是否都被检测和渲染
-
-**预期结果**:
-- ✅ 所有格式的代码块都被检测到
-- ✅ 所有格式的代码块都被正确渲染
-- ✅ 控制台显示检测到的代码块数量正确
-
-**实际结果**:
-
-**状态**: ⏳ 待测试
-
----
-
-### 测试用例 6: 页面刷新后的渲染
-
-**需求**: 1.5
-
-**测试步骤**:
-1. 通过 PJAX 跳转到包含 Mermaid 图表的文章
-2. 刷新页面(F5 或 Ctrl+R)
-3. 观察图表是否正常渲染
-
-**预期结果**:
-- ✅ 页面刷新后图表正常渲染
-- ✅ 渲染效果与 PJAX 跳转一致
-- ✅ 没有重复渲染或错误
-
-**实际结果**:
-
-**状态**: ⏳ 待测试
-
----
-
-## 代码审查
-
-### PJAX 集成代码 (argontheme.js 行 3247-3255)
-
-```javascript
-// Mermaid 图表渲染(需求 3.6: 页面切换时重新渲染)
-try {
- if (typeof MermaidRenderer !== 'undefined' && MermaidRenderer.renderAllCharts) {
- MermaidRenderer.renderAllCharts();
- }
-} catch (err) {
- ArgonDebug.error('MermaidRenderer.renderAllCharts failed:', err);
-}
-```
-
-**审查结果**:
-- ✅ 正确检查 `MermaidRenderer` 是否存在
-- ✅ 正确检查 `renderAllCharts` 方法是否存在
-- ✅ 使用 try-catch 包裹,避免错误阻塞其他功能
-- ✅ 错误日志记录完整
-
----
-
-### 清理代码 (argontheme.js 行 2883-2915)
-
-```javascript
-function cleanupMermaidInstances() {
- try {
- // 清理已渲染的图表记录
- if (typeof MermaidRenderer !== 'undefined' && MermaidRenderer.rendered) {
- const count = MermaidRenderer.rendered.size;
- MermaidRenderer.rendered.clear();
- if (count > 0) {
- ArgonDebug.log(`已清理 ${count} 个 Mermaid 图表记录`);
- }
- }
-
- // 移除 Mermaid 容器的事件监听器
- document.querySelectorAll('.mermaid-container').forEach(function(container) {
- // 移除工具栏事件监听器
- const toolbar = container.querySelector('.mermaid-toolbar');
- if (toolbar) {
- // 克隆节点以移除所有事件监听器
- // ...
- }
- });
-
- ArgonDebug.log('Mermaid 实例已清理');
- } catch(e) {
- ArgonDebug.warn('清理 Mermaid 实例失败:', e);
- }
-}
-```
-
-**审查结果**:
-- ✅ 正确清理渲染记录(`MermaidRenderer.rendered.clear()`)
-- ✅ 移除事件监听器,避免内存泄漏
-- ✅ 使用 try-catch 包裹,避免清理失败影响其他功能
-- ✅ 日志记录完整
-
----
-
-### 检测代码 (argontheme.js 行 4890-4930)
-
-```javascript
-detectMermaidBlocks() {
- const blocks = [];
-
- // 需求 3.1: 扫描所有 元素
- // 需求 3.2: 识别 language-mermaid 类名
- document.querySelectorAll('pre code.language-mermaid').forEach(code => {
- // 需求 3.5: 过滤已渲染的代码块
- if (!this.isRendered(code)) {
- blocks.push(code);
- }
- });
-
- // 需求 3.3: 识别 mermaid 类名
- document.querySelectorAll('pre code.mermaid').forEach(code => {
- // 需求 3.5: 过滤已渲染的代码块
- if (!this.isRendered(code)) {
- blocks.push(code);
- }
- });
-
- this.logDebug(`检测到 ${blocks.length} 个未渲染的 Mermaid 代码块`);
- return blocks;
-}
-```
-
-**审查结果**:
-- ✅ 正确扫描 `` 元素
-- ✅ 正确识别 `language-mermaid` 和 `mermaid` 类名
-- ✅ 正确过滤已渲染的代码块
-- ✅ 日志记录完整
-- ✅ 符合需求 3.1-3.5
-
----
-
-### 渲染状态检查 (argontheme.js 行 4932-4948)
-
-```javascript
-isRendered(element) {
- // 检查元素是否有渲染标记
- if (element.hasAttribute('data-mermaid-rendered')) {
- return true;
- }
-
- // 检查元素是否在 mermaid-container 容器中
- if (element.closest('.mermaid-container') !== null) {
- return true;
- }
-
- return false;
-}
-```
-
-**审查结果**:
-- ✅ 正确检查 `data-mermaid-rendered` 属性
-- ✅ 正确检查是否在 `.mermaid-container` 容器中
-- ✅ 避免重复渲染
-- ✅ 符合需求 3.5
-
----
-
-## 测试执行计划
-
-### 阶段 1: 手动测试(推荐)
-
-1. **准备测试环境**:
- - 在 WordPress 后台创建测试文章
- - 在文章中添加 Mermaid 代码块
- - 发布文章
-
-2. **执行测试**:
- - 打开浏览器开发者工具(F12)
- - 切换到 Console 标签
- - 从首页点击文章卡片
- - 观察控制台日志和页面渲染效果
- - 记录测试结果
-
-3. **测试不同场景**:
- - 测试多次 PJAX 跳转
- - 测试页面刷新
- - 测试不同格式的代码块
- - 测试错误处理
-
-### 阶段 2: 自动化测试(可选)
-
-如果需要自动化测试,可以使用以下工具:
-- Selenium WebDriver
-- Puppeteer
-- Playwright
-
----
-
-## 已知问题
-
-### 问题 1: 代码块格式不统一
-
-**描述**: 不同的 Markdown 编辑器可能生成不同格式的代码块
-
-**影响**: 可能导致某些格式的代码块无法被检测
-
-**解决方案**:
-- 已在 `detectMermaidBlocks()` 中支持多种格式
-- 需要测试验证所有格式都能正常工作
-
----
-
-### 问题 2: PJAX 加载时机
-
-**描述**: PJAX complete 事件触发时,DOM 可能还未完全更新
-
-**影响**: 可能导致检测不到代码块
-
-**解决方案**:
-- 已在 PJAX complete 事件中调用渲染
-- 如果仍有问题,可以添加延迟或使用 MutationObserver
-
----
-
-## 测试结论
-
-**总体评估**: ⏳ 待测试
-
-**通过的测试用例**: 0/6
-
-**失败的测试用例**: 0/6
-
-**待测试的用例**: 6/6
-
----
-
-## 下一步行动
-
-1. ✅ 代码审查完成 - 代码实现符合需求
-2. ⏳ 执行手动测试 - 验证实际效果
-3. ⏳ 记录测试结果 - 更新本文档
-4. ⏳ 修复发现的问题 - 如果有
-5. ⏳ 更新任务状态 - 标记为完成
-
----
-
-## 测试检查清单
-
-- [ ] 测试用例 1: PJAX 页面切换后的代码块检测
-- [ ] 测试用例 2: PJAX 页面切换后的图表渲染
-- [ ] 测试用例 3: 多次 PJAX 跳转不会重复渲染
-- [ ] 测试用例 4: PJAX 加载失败的降级处理
-- [ ] 测试用例 5: 不同格式的 Mermaid 代码块
-- [ ] 测试用例 6: 页面刷新后的渲染
-- [ ] 代码审查: PJAX 集成代码
-- [ ] 代码审查: 清理代码
-- [ ] 代码审查: 检测代码
-- [ ] 代码审查: 渲染状态检查
-
----
-
-## 附录: 测试数据
-
-### 测试用的 Mermaid 代码
-
-#### 流程图 (Flowchart)
-```mermaid
-flowchart TD
- A[开始] --> B{是否登录?}
- B -->|是| C[显示主页]
- B -->|否| D[跳转登录页]
- C --> E[结束]
- D --> E
-```
-
-#### 时序图 (Sequence Diagram)
-```mermaid
-sequenceDiagram
- participant 用户
- participant 浏览器
- participant 服务器
- 用户->>浏览器: 点击文章
- 浏览器->>服务器: PJAX 请求
- 服务器-->>浏览器: 返回 HTML
- 浏览器->>浏览器: 渲染 Mermaid
-```
-
-#### ER 图 (Entity Relationship Diagram)
-```mermaid
-erDiagram
- USER ||--o{ POST : writes
- USER {
- int id
- string name
- string email
- }
- POST {
- int id
- string title
- text content
- }
-```
-
----
-
-**文档版本**: 1.0
-**最后更新**: 2024-01-22
-**测试负责人**: Kiro AI Agent