From 1d5899ce7e4756468314e3a52d5f025b31352780 Mon Sep 17 00:00:00 2001 From: nanhaoluo <3075912108@qq.com> Date: Fri, 23 Jan 2026 23:12:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=20Mermaid=20?= =?UTF-8?q?=E5=BA=93=E5=8A=A0=E8=BD=BD=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=99=8D?= =?UTF-8?q?=E7=BA=A7=E5=A4=84=E7=90=86=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加多 CDN 备选方案(jsdelivr、unpkg、本地镜像) - 实现递归加载逻辑,主 CDN 失败时自动尝试备用 CDN - 添加 onerror 事件处理,捕获库加载失败 - 所有 CDN 失败时显示友好的错误提示 - 在错误提示中保留原始代码供用户查看 - 添加详细的控制台日志输出 - 创建 PHP 和 HTML 测试文件验证功能 - 暴露 MermaidRenderer 到全局作用域供降级处理使用 Requirements: 1.4, 2.3, 7.1, 7.2, 7.3, 7.4, 7.5 --- .kiro/specs/mermaid-support/design.md | 977 ++++++++++++++++++++ .kiro/specs/mermaid-support/requirements.md | 143 +++ .kiro/specs/mermaid-support/tasks.md | 2 +- .kiro/steering/mermaid-removal-summary.md | 51 + argontheme.js | 3 + functions.php | 166 +++- tests/test-mermaid-fallback.html | 374 ++++++++ tests/test-mermaid-fallback.php | 249 +++++ 8 files changed, 1962 insertions(+), 3 deletions(-) create mode 100644 .kiro/specs/mermaid-support/design.md create mode 100644 .kiro/specs/mermaid-support/requirements.md create mode 100644 .kiro/steering/mermaid-removal-summary.md create mode 100644 tests/test-mermaid-fallback.html create mode 100644 tests/test-mermaid-fallback.php diff --git a/.kiro/specs/mermaid-support/design.md b/.kiro/specs/mermaid-support/design.md new file mode 100644 index 0000000..f0fbef2 --- /dev/null +++ b/.kiro/specs/mermaid-support/design.md @@ -0,0 +1,977 @@ +# Design Document: Mermaid 图表支持 + +## Overview + +本设计文档描述了在 Argon WordPress 主题中集成 Mermaid 图表支持的技术方案。由于 WP-Markdown 编辑器的特殊渲染方式(将 Mermaid 代码块保存为单行,缺少真正的换行符),直接集成 Mermaid 存在技术障碍。 + +本设计采用**插件兼容方案**,通过支持主流 Mermaid WordPress 插件(如 WP Githuber MD、Markdown Block 等)来实现功能,同时提供主题级别的样式优化、夜间模式适配和性能优化。 + +### 核心设计原则 + +1. **插件优先**:依赖成熟的 Mermaid 插件处理代码解析和渲染 +2. **主题增强**:提供样式优化、主题适配和性能优化 +3. **灵活配置**:支持 CDN/本地加载、多种主题、调试模式 +4. **优雅降级**:加载失败时提供备用方案和友好提示 +5. **性能优先**:按需加载、异步加载、缓存优化 + +## Architecture + +### 系统架构图 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ WordPress 前端页面 │ +└─────────────────────────────────────────────────────────────┘ + │ + ├─ 包含 Mermaid 代码块? + │ + ┌─────────┴─────────┐ + │ │ + 是 否 + │ │ + ▼ ▼ + ┌──────────────────┐ ┌──────────┐ + │ 加载 Mermaid 库 │ │ 不加载 │ + └──────────────────┘ └──────────┘ + │ + ├─ CDN 模式 / 本地模式 + │ + ▼ + ┌──────────────────┐ + │ Mermaid.js 库 │ + └──────────────────┘ + │ + ▼ + ┌──────────────────┐ + │ 代码块检测器 │ + │ - class="mermaid"│ + │ - language="mermaid"│ + │ - data-lang="mermaid"│ + └──────────────────┘ + │ + ▼ + ┌──────────────────┐ + │ 渲染引擎 │ + │ - 初始化配置 │ + │ - 主题适配 │ + │ - 错误处理 │ + └──────────────────┘ + │ + ▼ + ┌──────────────────┐ + │ 样式增强器 │ + │ - 容器样式 │ + │ - 响应式适配 │ + │ - 夜间模式 │ + └──────────────────┘ + │ + ▼ + ┌──────────────────┐ + │ 渲染后的 SVG │ + └──────────────────┘ +``` + +### 组件交互流程 + +``` +用户访问页面 + │ + ▼ +WordPress 渲染页面 + │ + ▼ +主题检测页面内容 + │ + ├─ 是否包含 Mermaid 代码块? + │ + ▼ (是) +加载 Mermaid 库 (CDN/本地) + │ + ▼ +DOMContentLoaded 事件触发 + │ + ▼ +初始化 Mermaid 配置 + │ + ├─ 设置主题 (日间/夜间) + ├─ 设置安全级别 + └─ 设置错误处理 + │ + ▼ +检测所有 Mermaid 代码块 + │ + ▼ +批量渲染图表 + │ + ├─ 成功 → 应用样式增强 + └─ 失败 → 显示错误提示 + │ + ▼ +监听主题切换事件 + │ + ▼ +重新渲染图表 (如需要) +``` + + +## Components and Interfaces + +### 1. 配置管理组件 (Configuration Manager) + +**职责**:管理 Mermaid 相关的所有配置选项 + +**接口**: +```php +// 获取配置选项 +function argon_get_mermaid_option($option_name, $default = null) + +// 保存配置选项 +function argon_update_mermaid_option($option_name, $value) + +// 验证 CDN 地址格式 +function argon_validate_mermaid_cdn_url($url) + +// 获取当前主题模式对应的 Mermaid 主题 +function argon_get_mermaid_theme() +``` + +**配置选项**: +- `argon_enable_mermaid`: 启用/禁用 Mermaid 支持 (true/false) +- `argon_mermaid_cdn_source`: CDN 来源 (jsdelivr/unpkg/custom/local) +- `argon_mermaid_cdn_custom_url`: 自定义 CDN 地址 +- `argon_mermaid_theme`: 图表主题 (default/dark/forest/neutral/auto) +- `argon_mermaid_use_local`: 使用本地镜像 (true/false) +- `argon_mermaid_debug_mode`: 调试模式 (true/false) + +### 2. 库加载器 (Library Loader) + +**职责**:负责检测页面内容并按需加载 Mermaid 库 + +**接口**: +```php +// 检测页面是否包含 Mermaid 代码块 +function argon_has_mermaid_content($content) + +// 加载 Mermaid 库 +function argon_enqueue_mermaid_scripts() + +// 获取 Mermaid 库 URL +function argon_get_mermaid_library_url() +``` + +**实现逻辑**: +1. 在 `wp_enqueue_scripts` 钩子中检查当前页面内容 +2. 使用正则表达式检测 Mermaid 代码块标记 +3. 如果检测到,根据配置加载对应的库文件 +4. 添加 async 或 defer 属性实现异步加载 + +**CDN 地址映射**: +```php +$cdn_urls = [ + 'jsdelivr' => 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js', + 'unpkg' => 'https://unpkg.com/mermaid@10/dist/mermaid.min.js', + 'local' => get_template_directory_uri() . '/assets/vendor/mermaid/mermaid.min.js' +]; +``` + +### 3. 渲染引擎初始化器 (Render Engine Initializer) + +**职责**:初始化 Mermaid 配置并启动渲染 + +**接口**: +```javascript +// 初始化 Mermaid 配置 +function initMermaidConfig() + +// 获取当前主题对应的 Mermaid 主题 +function getMermaidTheme() + +// 渲染所有 Mermaid 图表 +function renderAllMermaidCharts() + +// 重新渲染图表(主题切换时) +function reRenderMermaidCharts() +``` + +**Mermaid 配置对象**: +```javascript +{ + startOnLoad: false, // 手动控制渲染时机 + theme: 'default', // 根据页面主题动态设置 + securityLevel: 'loose', // 允许 HTML 标签 + logLevel: 'error', // 生产环境使用 error,调试模式使用 debug + flowchart: { + useMaxWidth: true, + htmlLabels: true + } +} +``` + +### 4. 代码块检测器 (Code Block Detector) + +**职责**:识别页面中的 Mermaid 代码块 + +**接口**: +```javascript +// 检测所有 Mermaid 代码块 +function detectMermaidBlocks() + +// 检查元素是否为 Mermaid 代码块 +function isMermaidBlock(element) + +// 提取代码块内容 +function extractMermaidCode(element) +``` + +**检测规则**(优先级从高到低): +1. `
` - Markdown 格式
+3. `` - 自定义属性格式
+4. `` - 简化格式
+
+**特殊处理**:
+- 忽略 HTML 注释中的代码块
+- 处理 WP-Markdown 生成的 `` 格式
+- 解码转义字符(`\n`, `\"`, `\'`)
+
+### 5. 样式增强器 (Style Enhancer)
+
+**职责**:为渲染后的图表添加主题样式
+
+**接口**:
+```javascript
+// 应用容器样式
+function applyMermaidContainerStyles(container)
+
+// 应用响应式样式
+function applyResponsiveStyles(container)
+
+// 应用夜间模式样式
+function applyDarkModeStyles(container, isDarkMode)
+```
+
+**CSS 样式类**:
+```css
+.mermaid-container {
+ background: var(--card-background);
+ border-radius: 8px;
+ padding: 20px;
+ margin: 20px 0;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+ overflow-x: auto;
+ max-width: 100%;
+}
+
+.mermaid-container svg {
+ max-width: 100%;
+ height: auto;
+}
+
+html.darkmode .mermaid-container {
+ background: var(--card-background-dark);
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3);
+}
+```
+
+### 6. 错误处理器 (Error Handler)
+
+**职责**:处理渲染错误并显示友好提示
+
+**接口**:
+```javascript
+// 处理渲染错误
+function handleMermaidError(error, element)
+
+// 显示错误提示
+function showErrorMessage(element, errorInfo)
+
+// 记录调试信息
+function logDebugInfo(message, data)
+```
+
+**错误提示格式**:
+```html
+
+
+ Mermaid 图表渲染失败
+
+ 行号: 3
+
+ 查看原始代码
+ ...
+
+
+```
+
+### 7. 主题切换监听器 (Theme Switch Listener)
+
+**职责**:监听主题模式切换并重新渲染图表
+
+**接口**:
+```javascript
+// 监听主题切换事件
+function listenThemeSwitch()
+
+// 主题切换回调
+function onThemeSwitched(isDarkMode)
+
+// 批量重新渲染
+function batchReRender(elements)
+```
+
+**实现方式**:
+- 监听 Argon 主题的 `argon:theme-switched` 自定义事件
+- 监听 `html` 元素的 `darkmode` class 变化(MutationObserver)
+- 使用防抖避免频繁重新渲染
+
+### 8. 插件兼容层 (Plugin Compatibility Layer)
+
+**职责**:检测并兼容主流 Mermaid 插件
+
+**接口**:
+```php
+// 检测已安装的 Mermaid 插件
+function argon_detect_mermaid_plugins()
+
+// 检查是否已加载 Mermaid 库
+function argon_is_mermaid_loaded()
+
+// 避免重复加载
+function argon_prevent_duplicate_loading()
+```
+
+**支持的插件**:
+1. **WP Githuber MD** - 检测 `wp-githuber-md` 插件
+2. **Markdown Block** - 检测 Gutenberg Mermaid 块
+3. **Code Syntax Block** - 检测代码高亮插件的 Mermaid 支持
+
+**兼容策略**:
+- 如果插件已加载 Mermaid 库,主题不再重复加载
+- 主题只提供样式增强和主题适配
+- 通过 `window.mermaid` 对象检测库是否已加载
+
+
+## Data Models
+
+### 1. Mermaid 配置对象
+
+```javascript
+interface MermaidConfig {
+ enabled: boolean; // 是否启用
+ cdnSource: string; // CDN 来源: 'jsdelivr' | 'unpkg' | 'custom' | 'local'
+ customCdnUrl: string; // 自定义 CDN 地址
+ theme: string; // 图表主题: 'default' | 'dark' | 'forest' | 'neutral' | 'auto'
+ useLocal: boolean; // 是否使用本地镜像
+ debugMode: boolean; // 调试模式
+ autoThemeSwitch: boolean; // 自动切换主题
+}
+```
+
+### 2. 代码块元素对象
+
+```javascript
+interface MermaidBlock {
+ element: HTMLElement; // DOM 元素
+ code: string; // Mermaid 代码
+ type: string; // 代码块类型: 'div' | 'pre-code' | 'custom'
+ rendered: boolean; // 是否已渲染
+ error: Error | null; // 渲染错误
+}
+```
+
+### 3. 渲染结果对象
+
+```javascript
+interface RenderResult {
+ success: boolean; // 是否成功
+ svg: string; // 渲染后的 SVG
+ error: {
+ type: string; // 错误类型
+ message: string; // 错误信息
+ line: number; // 错误行号
+ } | null;
+}
+```
+
+### 4. 插件检测结果
+
+```php
+interface PluginDetectionResult {
+ 'wp-githuber-md': boolean; // WP Githuber MD
+ 'markdown-block': boolean; // Markdown Block
+ 'code-syntax-block': boolean; // Code Syntax Block
+ 'mermaid-loaded': boolean; // Mermaid 库是否已加载
+}
+```
+
+### 5. 错误信息对象
+
+```javascript
+interface ErrorInfo {
+ type: string; // 错误类型: 'syntax' | 'render' | 'load'
+ message: string; // 错误信息
+ line: number | null; // 错误行号
+ code: string; // 原始代码
+ timestamp: number; // 时间戳
+}
+```
+
+## Correctness Properties
+
+*属性是一种特征或行为,应该在系统的所有有效执行中保持为真——本质上是关于系统应该做什么的正式陈述。属性作为人类可读规范和机器可验证正确性保证之间的桥梁。*
+
+### Property 1: 按需加载库
+
+*对于任意* WordPress 页面,当且仅当页面内容包含 Mermaid 代码块时,主题应该加载 Mermaid JavaScript 库。
+
+**Validates: Requirements 1.1, 1.5, 8.1**
+
+### Property 2: CDN 地址正确性
+
+*对于任意* CDN 配置选项(jsdelivr、unpkg、custom、local),生成的脚本 URL 应该与配置选项对应的 CDN 地址匹配。
+
+**Validates: Requirements 1.2, 1.3**
+
+### Property 3: 代码块识别完整性
+
+*对于任意* 包含 Mermaid 标记的 HTML 元素(class="mermaid"、language="mermaid"、data-lang="mermaid"),检测器应该能够识别并提取其中的 Mermaid 代码。
+
+**Validates: Requirements 10.1, 10.2, 10.3**
+
+### Property 4: 渲染成功后替换内容
+
+*对于任意* 成功渲染的 Mermaid 图表,原始代码块文本应该被移除,并替换为渲染后的 SVG 图表。
+
+**Validates: Requirements 2.4**
+
+### Property 5: 错误时保留原始代码
+
+*对于任意* 渲染失败的 Mermaid 代码块,系统应该显示错误提示信息,并保留原始代码块以便用户修正。
+
+**Validates: Requirements 7.1, 7.4**
+
+### Property 6: 主题模式自动切换
+
+*对于任意* 页面主题模式切换(日间↔夜间),当配置为自动切换时,所有 Mermaid 图表应该重新渲染并使用对应的图表主题(浅色↔深色)。
+
+**Validates: Requirements 4.1, 4.2, 4.3**
+
+### Property 7: 自定义主题优先级
+
+*对于任意* 图表,当管理员设置了自定义图表主题时,应该使用自定义主题而不是根据页面主题自动切换。
+
+**Validates: Requirements 4.5**
+
+### Property 8: 响应式容器宽度
+
+*对于任意* 渲染后的 Mermaid 图表容器,其最大宽度应该设置为 100%,并且当图表宽度超过容器时应该启用横向滚动。
+
+**Validates: Requirements 3.1, 3.3**
+
+### Property 9: 移动端自适应
+
+*对于任意* 屏幕宽度小于 768px 的设备,Mermaid 图表应该自动调整大小以适应屏幕宽度,不应该出现横向溢出。
+
+**Validates: Requirements 3.2**
+
+### Property 10: 夜间模式样式适配
+
+*对于任意* Mermaid 图表容器,在夜间模式下应该应用深色背景和边框样式,与页面整体风格保持一致。
+
+**Validates: Requirements 6.5**
+
+### Property 11: 卡片内边距
+
+*对于任意* 在卡片中显示的 Mermaid 图表,容器应该添加适当的内边距(padding),确保图表与卡片边缘有足够的间距。
+
+**Validates: Requirements 6.3**
+
+### Property 12: CDN 地址验证
+
+*对于任意* 用户输入的自定义 CDN 地址,保存前应该验证其格式是否为有效的 URL,并且以 `.js` 结尾。
+
+**Validates: Requirements 5.5**
+
+### Property 13: 避免重复加载
+
+*对于任意* 页面,当检测到已有 Mermaid 插件加载了 Mermaid 库时,主题不应该重复加载该库。
+
+**Validates: Requirements 9.4**
+
+### Property 14: 错误信息完整性
+
+*对于任意* Mermaid 解析错误,错误提示信息应该包含错误类型和详细的错误描述,帮助用户定位问题。
+
+**Validates: Requirements 7.3**
+
+### Property 15: 批量渲染性能
+
+*对于任意* 包含多个 Mermaid 图表的页面,所有图表应该在一次 DOM 遍历中批量收集,然后批量渲染,而不是逐个渲染。
+
+**Validates: Requirements 8.4**
+
+### Property 16: 渲染缓存
+
+*对于任意* 已成功渲染的 Mermaid 图表,在页面生命周期内不应该重复渲染,除非主题模式发生切换。
+
+**Validates: Requirements 8.3**
+
+### Property 17: 代码块优先级
+
+*对于任意* 同时包含多个 Mermaid 标记的元素(如同时有 class="mermaid" 和 data-lang="mermaid"),应该优先使用 class 属性进行识别。
+
+**Validates: Requirements 10.4**
+
+### Property 18: 忽略注释代码块
+
+*对于任意* 被 HTML 注释包裹的 Mermaid 代码块,检测器应该忽略它们,不进行渲染。
+
+**Validates: Requirements 10.5**
+
+## Error Handling
+
+### 1. 库加载失败
+
+**场景**:CDN 不可用或网络问题导致 Mermaid 库加载失败
+
+**处理策略**:
+1. 监听脚本 `onerror` 事件
+2. 在控制台输出详细错误信息
+3. 尝试降级到备用 CDN(jsdelivr → unpkg → local)
+4. 如果所有 CDN 都失败,显示全局提示信息
+
+**实现**:
+```javascript
+function loadMermaidWithFallback(urls, index = 0) {
+ if (index >= urls.length) {
+ console.error('[Argon Mermaid] 所有 CDN 加载失败');
+ showGlobalError('Mermaid 库加载失败,请检查网络连接');
+ return;
+ }
+
+ const script = document.createElement('script');
+ script.src = urls[index];
+ script.async = true;
+
+ script.onerror = () => {
+ console.warn(`[Argon Mermaid] CDN ${urls[index]} 加载失败,尝试备用 CDN`);
+ loadMermaidWithFallback(urls, index + 1);
+ };
+
+ script.onload = () => {
+ console.log(`[Argon Mermaid] 成功从 ${urls[index]} 加载库`);
+ initMermaid();
+ };
+
+ document.head.appendChild(script);
+}
+```
+
+### 2. 代码解析错误
+
+**场景**:Mermaid 代码语法错误导致解析失败
+
+**处理策略**:
+1. 捕获 Mermaid 渲染异常
+2. 提取错误类型和行号信息
+3. 在原代码块位置显示友好的错误提示
+4. 保留原始代码供用户查看和修正
+5. 在调试模式下输出详细堆栈信息
+
+**错误提示 UI**:
+```html
+
+
+
+ 图表渲染失败
+
+
+ 错误类型: 语法错误
+
+ 位置: 第 3 行
+
+
+ 查看原始代码
+ ...
+
+
+```
+
+### 3. 配置验证错误
+
+**场景**:管理员输入无效的配置选项
+
+**处理策略**:
+1. 在保存前验证所有配置项
+2. CDN URL 格式验证(必须是有效 URL 且以 .js 结尾)
+3. 主题名称验证(必须是预定义的主题之一)
+4. 显示具体的验证错误信息
+5. 阻止保存无效配置
+
+**验证函数**:
+```php
+function argon_validate_mermaid_settings($settings) {
+ $errors = [];
+
+ // 验证 CDN URL
+ if ($settings['cdn_source'] === 'custom') {
+ $url = $settings['custom_cdn_url'];
+ if (!filter_var($url, FILTER_VALIDATE_URL)) {
+ $errors[] = 'CDN 地址格式无效';
+ } elseif (!preg_match('/\.js$/', $url)) {
+ $errors[] = 'CDN 地址必须以 .js 结尾';
+ }
+ }
+
+ // 验证主题名称
+ $valid_themes = ['default', 'dark', 'forest', 'neutral', 'auto'];
+ if (!in_array($settings['theme'], $valid_themes)) {
+ $errors[] = '无效的图表主题';
+ }
+
+ return $errors;
+}
+```
+
+### 4. 插件冲突
+
+**场景**:多个插件同时加载 Mermaid 库导致冲突
+
+**处理策略**:
+1. 在加载前检测 `window.mermaid` 是否已存在
+2. 如果已存在,跳过库加载,只应用样式增强
+3. 记录检测结果到控制台
+4. 在设置页显示插件兼容性状态
+
+**检测逻辑**:
+```javascript
+function checkMermaidLoaded() {
+ if (typeof window.mermaid !== 'undefined') {
+ console.log('[Argon Mermaid] 检测到 Mermaid 库已由其他插件加载');
+ return true;
+ }
+ return false;
+}
+```
+
+### 5. 主题切换异常
+
+**场景**:主题切换时重新渲染失败
+
+**处理策略**:
+1. 使用 try-catch 包裹重新渲染逻辑
+2. 如果重新渲染失败,保留原有图表
+3. 在控制台输出警告信息
+4. 不影响页面其他功能
+
+**实现**:
+```javascript
+function reRenderOnThemeSwitch() {
+ const charts = document.querySelectorAll('.mermaid-rendered');
+
+ charts.forEach(chart => {
+ try {
+ const code = chart.dataset.mermaidCode;
+ const newTheme = getMermaidTheme();
+
+ mermaid.initialize({ theme: newTheme });
+ mermaid.render('mermaid-' + Date.now(), code, (svg) => {
+ chart.innerHTML = svg;
+ });
+ } catch (error) {
+ console.warn('[Argon Mermaid] 重新渲染失败,保留原图表', error);
+ }
+ });
+}
+```
+
+
+## Testing Strategy
+
+### 测试方法概述
+
+本功能采用**双重测试策略**:
+- **单元测试**:验证具体示例、边缘情况和错误条件
+- **属性测试**:验证跨所有输入的通用属性
+
+两者互补且都是全面覆盖所需的:
+- 单元测试捕获具体的 bug
+- 属性测试验证一般正确性
+
+### 单元测试策略
+
+单元测试应专注于:
+- **具体示例**:演示正确行为的特定案例
+- **集成点**:组件之间的交互
+- **边缘情况和错误条件**:特殊场景处理
+
+**测试框架**:使用 PHPUnit(PHP 部分)和 Jest(JavaScript 部分)
+
+**PHP 单元测试示例**:
+```php
+// 测试 CDN URL 验证
+public function test_validate_cdn_url_with_valid_url() {
+ $url = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js';
+ $this->assertTrue(argon_validate_mermaid_cdn_url($url));
+}
+
+public function test_validate_cdn_url_with_invalid_url() {
+ $url = 'not-a-valid-url';
+ $this->assertFalse(argon_validate_mermaid_cdn_url($url));
+}
+
+// 测试代码块检测
+public function test_has_mermaid_content_with_div_class() {
+ $content = 'flowchart TD';
+ $this->assertTrue(argon_has_mermaid_content($content));
+}
+
+public function test_has_mermaid_content_without_mermaid() {
+ $content = 'Regular paragraph
';
+ $this->assertFalse(argon_has_mermaid_content($content));
+}
+```
+
+**JavaScript 单元测试示例**:
+```javascript
+// 测试主题获取
+test('getMermaidTheme returns dark theme in dark mode', () => {
+ document.documentElement.classList.add('darkmode');
+ expect(getMermaidTheme()).toBe('dark');
+});
+
+test('getMermaidTheme returns default theme in light mode', () => {
+ document.documentElement.classList.remove('darkmode');
+ expect(getMermaidTheme()).toBe('default');
+});
+
+// 测试代码块检测
+test('isMermaidBlock detects div with mermaid class', () => {
+ const div = document.createElement('div');
+ div.className = 'mermaid';
+ expect(isMermaidBlock(div)).toBe(true);
+});
+
+test('isMermaidBlock ignores regular div', () => {
+ const div = document.createElement('div');
+ expect(isMermaidBlock(div)).toBe(false);
+});
+```
+
+### 属性测试策略
+
+**测试框架**:使用 fast-check(JavaScript)进行属性测试
+
+**配置要求**:
+- 每个属性测试最少运行 100 次迭代
+- 每个测试必须引用设计文档中的属性
+- 标签格式:`Feature: mermaid-support, Property {number}: {property_text}`
+
+**属性测试示例**:
+
+```javascript
+// Feature: mermaid-support, Property 1: 按需加载库
+// 对于任意 WordPress 页面,当且仅当页面内容包含 Mermaid 代码块时,主题应该加载 Mermaid JavaScript 库
+fc.assert(
+ fc.property(
+ fc.string(), // 生成随机页面内容
+ fc.boolean(), // 是否包含 Mermaid 代码块
+ (content, hasMermaid) => {
+ const pageContent = hasMermaid
+ ? content + 'flowchart TD'
+ : content;
+
+ const shouldLoad = argon_has_mermaid_content(pageContent);
+ expect(shouldLoad).toBe(hasMermaid);
+ }
+ ),
+ { numRuns: 100 }
+);
+
+// Feature: mermaid-support, Property 2: CDN 地址正确性
+// 对于任意 CDN 配置选项,生成的脚本 URL 应该与配置选项对应的 CDN 地址匹配
+fc.assert(
+ fc.property(
+ fc.constantFrom('jsdelivr', 'unpkg', 'local'),
+ (cdnSource) => {
+ const expectedUrls = {
+ 'jsdelivr': 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js',
+ 'unpkg': 'https://unpkg.com/mermaid@10/dist/mermaid.min.js',
+ 'local': '/wp-content/themes/argon/assets/vendor/mermaid/mermaid.min.js'
+ };
+
+ const actualUrl = argon_get_mermaid_library_url(cdnSource);
+ expect(actualUrl).toContain(expectedUrls[cdnSource]);
+ }
+ ),
+ { numRuns: 100 }
+);
+
+// Feature: mermaid-support, Property 3: 代码块识别完整性
+// 对于任意包含 Mermaid 标记的 HTML 元素,检测器应该能够识别并提取其中的 Mermaid 代码
+fc.assert(
+ fc.property(
+ fc.constantFrom('class', 'language', 'data-lang'),
+ fc.string({ minLength: 10 }),
+ (markType, code) => {
+ let element;
+ switch (markType) {
+ case 'class':
+ element = document.createElement('div');
+ element.className = 'mermaid';
+ element.textContent = code;
+ break;
+ case 'language':
+ element = document.createElement('pre');
+ const codeEl = document.createElement('code');
+ codeEl.className = 'language-mermaid';
+ codeEl.textContent = code;
+ element.appendChild(codeEl);
+ break;
+ case 'data-lang':
+ element = document.createElement('pre');
+ element.setAttribute('data-lang', 'mermaid');
+ element.textContent = code;
+ break;
+ }
+
+ expect(isMermaidBlock(element)).toBe(true);
+ expect(extractMermaidCode(element)).toBe(code);
+ }
+ ),
+ { numRuns: 100 }
+);
+
+// Feature: mermaid-support, Property 6: 主题模式自动切换
+// 对于任意页面主题模式切换,当配置为自动切换时,所有 Mermaid 图表应该重新渲染并使用对应的图表主题
+fc.assert(
+ fc.property(
+ fc.boolean(), // 初始主题模式
+ fc.integer({ min: 1, max: 10 }), // 图表数量
+ (initialDarkMode, chartCount) => {
+ // 设置初始主题
+ if (initialDarkMode) {
+ document.documentElement.classList.add('darkmode');
+ } else {
+ document.documentElement.classList.remove('darkmode');
+ }
+
+ // 创建多个图表
+ const charts = [];
+ for (let i = 0; i < chartCount; i++) {
+ const chart = document.createElement('div');
+ chart.className = 'mermaid-rendered';
+ chart.dataset.mermaidCode = 'flowchart TD\nA-->B';
+ document.body.appendChild(chart);
+ charts.push(chart);
+ }
+
+ // 切换主题
+ const newDarkMode = !initialDarkMode;
+ if (newDarkMode) {
+ document.documentElement.classList.add('darkmode');
+ } else {
+ document.documentElement.classList.remove('darkmode');
+ }
+
+ // 触发重新渲染
+ reRenderOnThemeSwitch();
+
+ // 验证所有图表都使用了新主题
+ const expectedTheme = newDarkMode ? 'dark' : 'default';
+ charts.forEach(chart => {
+ expect(chart.dataset.currentTheme).toBe(expectedTheme);
+ });
+
+ // 清理
+ charts.forEach(chart => chart.remove());
+ }
+ ),
+ { numRuns: 100 }
+);
+
+// Feature: mermaid-support, Property 12: CDN 地址验证
+// 对于任意用户输入的自定义 CDN 地址,保存前应该验证其格式是否为有效的 URL,并且以 .js 结尾
+fc.assert(
+ fc.property(
+ fc.webUrl(), // 生成随机 URL
+ fc.constantFrom('.js', '.css', '.json', ''), // 不同的文件扩展名
+ (baseUrl, extension) => {
+ const url = baseUrl + extension;
+ const isValid = argon_validate_mermaid_cdn_url(url);
+
+ // 只有以 .js 结尾的有效 URL 才应该通过验证
+ const shouldBeValid = extension === '.js';
+ expect(isValid).toBe(shouldBeValid);
+ }
+ ),
+ { numRuns: 100 }
+);
+
+// Feature: mermaid-support, Property 15: 批量渲染性能
+// 对于任意包含多个 Mermaid 图表的页面,所有图表应该在一次 DOM 遍历中批量收集,然后批量渲染
+fc.assert(
+ fc.property(
+ fc.integer({ min: 1, max: 20 }), // 图表数量
+ (chartCount) => {
+ // 创建多个图表
+ for (let i = 0; i < chartCount; i++) {
+ const div = document.createElement('div');
+ div.className = 'mermaid';
+ div.textContent = `flowchart TD\nA${i}-->B${i}`;
+ document.body.appendChild(div);
+ }
+
+ // 记录 DOM 查询次数
+ let queryCount = 0;
+ const originalQuerySelectorAll = document.querySelectorAll;
+ document.querySelectorAll = function(...args) {
+ queryCount++;
+ return originalQuerySelectorAll.apply(this, args);
+ };
+
+ // 执行批量渲染
+ renderAllMermaidCharts();
+
+ // 恢复原始方法
+ document.querySelectorAll = originalQuerySelectorAll;
+
+ // 验证只进行了一次 DOM 查询
+ expect(queryCount).toBe(1);
+
+ // 清理
+ document.querySelectorAll('.mermaid').forEach(el => el.remove());
+ }
+ ),
+ { numRuns: 100 }
+);
+```
+
+### 集成测试
+
+**测试场景**:
+1. **完整渲染流程**:从页面加载到图表显示的完整流程
+2. **插件兼容性**:与 WP Githuber MD、Markdown Block 等插件的集成
+3. **主题切换**:日间/夜间模式切换时的图表重新渲染
+4. **错误恢复**:CDN 加载失败时的降级处理
+
+**测试工具**:使用 Playwright 或 Puppeteer 进行端到端测试
+
+### 手动测试清单
+
+由于某些需求难以自动化测试,需要进行手动验证:
+
+- [ ] 图表颜色与页面背景色有足够的对比度(Requirements 4.4)
+- [ ] 图表容器样式与主题整体风格一致(Requirements 6.2)
+- [ ] 所有 Mermaid 官方图表类型都能正确渲染(Requirements 2.2)
+- [ ] 移动端显示效果良好(Requirements 3.2)
+- [ ] 错误提示信息友好易懂(Requirements 7.1)
+- [ ] 设置页预览功能正常工作(Requirements 5.6)
+
+### 测试覆盖率目标
+
+- **PHP 代码覆盖率**:≥ 80%
+- **JavaScript 代码覆盖率**:≥ 85%
+- **属性测试覆盖**:所有 18 个正确性属性
+- **单元测试覆盖**:所有核心函数和边缘情况
+
diff --git a/.kiro/specs/mermaid-support/requirements.md b/.kiro/specs/mermaid-support/requirements.md
new file mode 100644
index 0000000..bc62a15
--- /dev/null
+++ b/.kiro/specs/mermaid-support/requirements.md
@@ -0,0 +1,143 @@
+# Requirements Document
+
+## Introduction
+
+本文档定义了在 Argon WordPress 主题中实现 Mermaid 图表支持功能的需求。Mermaid 是一个基于 JavaScript 的图表和流程图生成工具,允许用户通过文本描述创建各种类型的可视化图表。
+
+由于 WP-Markdown 编辑器的特殊渲染方式(将整个 Mermaid 代码块保存为单行,没有真正的换行符),直接集成 Mermaid 存在技术障碍。本需求文档采用插件集成方案,通过支持 Mermaid 的 WordPress 插件来实现功能。
+
+## Glossary
+
+- **Mermaid**: 基于 JavaScript 的图表生成库,支持流程图、时序图、类图等多种图表类型
+- **WP-Markdown**: WordPress Markdown 编辑器,用于在 WordPress 中编辑 Markdown 格式的内容
+- **Theme**: Argon WordPress 主题系统
+- **Admin**: WordPress 后台管理员
+- **CDN**: 内容分发网络,用于加载外部 JavaScript 库
+- **Render_Engine**: Mermaid 图表渲染引擎
+- **Code_Block**: Markdown 代码块,用于包含 Mermaid 图表定义
+- **Theme_Mode**: 主题模式,包括日间模式和夜间模式
+- **Settings_Page**: Argon 主题设置页面
+
+## Requirements
+
+### Requirement 1: Mermaid 库加载
+
+**User Story:** 作为开发者,我希望主题能够正确加载 Mermaid 库,以便在文章中渲染图表。
+
+#### Acceptance Criteria
+
+1. WHEN Admin 在设置页启用 Mermaid 支持 THEN THE Theme SHALL 在前端页面加载 Mermaid JavaScript 库
+2. WHERE CDN 模式被选择 WHEN 页面加载时 THEN THE Theme SHALL 从指定的 CDN 地址加载 Mermaid 库
+3. WHERE 本地镜像模式被选择 WHEN 页面加载时 THEN THE Theme SHALL 从主题目录加载本地 Mermaid 库文件
+4. WHEN Mermaid 库加载失败 THEN THE Theme SHALL 在控制台输出错误信息并降级到备用 CDN
+5. THE Theme SHALL 仅在包含 Mermaid 代码块的页面加载 Mermaid 库
+
+### Requirement 2: 图表渲染
+
+**User Story:** 作为内容创作者,我希望在文章中使用 Mermaid 语法创建图表,并在前端正确显示。
+
+#### Acceptance Criteria
+
+1. WHEN 文章包含 Mermaid 代码块 THEN THE Render_Engine SHALL 将代码块渲染为可视化图表
+2. THE Render_Engine SHALL 支持所有 Mermaid 官方图表类型(流程图、时序图、类图、状态图、甘特图、饼图、Git 图等)
+3. WHEN Mermaid 代码语法错误 THEN THE Render_Engine SHALL 显示友好的错误提示信息
+4. WHEN 图表渲染成功 THEN THE Theme SHALL 移除原始代码块文本并显示渲染后的 SVG 图表
+5. THE Render_Engine SHALL 在 DOM 加载完成后初始化并渲染所有图表
+
+### Requirement 3: 响应式设计
+
+**User Story:** 作为用户,我希望图表能够在不同设备上正确显示,以便在移动端也能查看。
+
+#### Acceptance Criteria
+
+1. WHEN 图表宽度超过容器宽度 THEN THE Theme SHALL 启用横向滚动功能
+2. WHEN 用户在移动设备上查看 THEN THE Theme SHALL 自动调整图表大小以适应屏幕宽度
+3. THE Theme SHALL 为图表容器设置最大宽度为 100%
+4. WHEN 图表高度超过视口高度 THEN THE Theme SHALL 保持图表完整性不进行裁剪
+5. THE Theme SHALL 在图表容器上应用响应式 CSS 样式
+
+### Requirement 4: 主题模式适配
+
+**User Story:** 作为用户,我希望图表能够适配夜间模式,以便在不同主题下都有良好的视觉效果。
+
+#### Acceptance Criteria
+
+1. WHEN 用户切换到夜间模式 THEN THE Theme SHALL 自动切换 Mermaid 图表主题为深色主题
+2. WHEN 用户切换到日间模式 THEN THE Theme SHALL 自动切换 Mermaid 图表主题为浅色主题
+3. THE Theme SHALL 在主题切换时重新渲染所有 Mermaid 图表
+4. THE Theme SHALL 确保图表颜色与页面背景色有足够的对比度
+5. WHERE Admin 设置了自定义图表主题 THEN THE Theme SHALL 使用自定义主题而不是自动切换
+
+### Requirement 5: 后台设置
+
+**User Story:** 作为管理员,我希望能够在后台配置 Mermaid 功能,以便根据需求调整行为。
+
+#### Acceptance Criteria
+
+1. THE Settings_Page SHALL 提供启用或禁用 Mermaid 支持的开关选项
+2. THE Settings_Page SHALL 提供 CDN 地址选择选项(jsDelivr、unpkg、自定义)
+3. THE Settings_Page SHALL 提供图表主题选择选项(default、dark、forest、neutral)
+4. THE Settings_Page SHALL 提供本地镜像开关选项
+5. WHEN Admin 保存设置 THEN THE Theme SHALL 验证 CDN 地址格式的有效性
+6. THE Settings_Page SHALL 提供 Mermaid 配置预览功能
+
+### Requirement 6: 样式优化
+
+**User Story:** 作为内容创作者,我希望图表有美观的样式,以便提升文章的视觉质量。
+
+#### Acceptance Criteria
+
+1. THE Theme SHALL 为图表容器添加背景色、圆角和阴影样式
+2. THE Theme SHALL 确保图表容器样式与主题整体风格一致
+3. WHEN 图表在卡片中显示 THEN THE Theme SHALL 添加适当的内边距
+4. THE Theme SHALL 为图表添加淡入动画效果
+5. THE Theme SHALL 在夜间模式下调整图表容器的背景色和边框色
+
+### Requirement 7: 错误处理
+
+**User Story:** 作为开发者,我希望系统能够优雅地处理错误,以便快速定位和解决问题。
+
+#### Acceptance Criteria
+
+1. WHEN Mermaid 代码解析失败 THEN THE Theme SHALL 在图表位置显示错误提示信息
+2. WHEN Mermaid 库加载失败 THEN THE Theme SHALL 在控制台输出详细的错误日志
+3. THE Theme SHALL 在错误提示中包含错误类型和行号信息
+4. WHEN 发生错误 THEN THE Theme SHALL 保留原始代码块以便用户修正
+5. THE Theme SHALL 提供调试模式选项以输出详细的渲染过程信息
+
+### Requirement 8: 性能优化
+
+**User Story:** 作为用户,我希望图表加载不影响页面性能,以便获得流畅的浏览体验。
+
+#### Acceptance Criteria
+
+1. THE Theme SHALL 仅在包含 Mermaid 代码块的页面加载 Mermaid 库
+2. THE Theme SHALL 使用异步方式加载 Mermaid 库
+3. THE Theme SHALL 缓存已渲染的图表以避免重复渲染
+4. WHEN 页面包含多个图表 THEN THE Theme SHALL 批量渲染以提高性能
+5. THE Theme SHALL 使用 CDN 加速 Mermaid 库的加载速度
+
+### Requirement 9: 插件兼容性
+
+**User Story:** 作为管理员,我希望 Mermaid 功能能够与常用的 WordPress 插件兼容,以便灵活选择编辑器。
+
+#### Acceptance Criteria
+
+1. THE Theme SHALL 支持通过 WP Githuber MD 插件创建的 Mermaid 图表
+2. THE Theme SHALL 支持通过 Markdown Block 插件创建的 Mermaid 图表
+3. THE Theme SHALL 支持通过 Code Syntax Block 插件创建的 Mermaid 图表
+4. WHEN 检测到多个 Mermaid 插件 THEN THE Theme SHALL 避免重复加载 Mermaid 库
+5. THE Theme SHALL 提供插件兼容性检测功能
+
+### Requirement 10: 代码块识别
+
+**User Story:** 作为开发者,我希望系统能够准确识别 Mermaid 代码块,以便正确渲染图表。
+
+#### Acceptance Criteria
+
+1. THE Theme SHALL 识别 class 属性为 "mermaid" 的 div 元素作为 Mermaid 代码块
+2. THE Theme SHALL 识别 language 属性为 "mermaid" 的 pre 或 code 元素作为 Mermaid 代码块
+3. THE Theme SHALL 识别 data-lang 属性为 "mermaid" 的元素作为 Mermaid 代码块
+4. WHEN 代码块同时包含多个识别标记 THEN THE Theme SHALL 优先使用 class 属性
+5. THE Theme SHALL 忽略被注释掉的 Mermaid 代码块
+
diff --git a/.kiro/specs/mermaid-support/tasks.md b/.kiro/specs/mermaid-support/tasks.md
index 8cd1b01..f2ebc72 100644
--- a/.kiro/specs/mermaid-support/tasks.md
+++ b/.kiro/specs/mermaid-support/tasks.md
@@ -69,7 +69,7 @@
- **Validates: Requirements 8.4**
- _Requirements: 8.4_
-- [~] 5. 实现错误处理机制
+- [x] 5. 实现错误处理机制
- 添加库加载失败的降级处理(多个 CDN 备选)
- 实现代码解析错误的捕获和显示
- 创建错误提示 UI 组件
diff --git a/.kiro/steering/mermaid-removal-summary.md b/.kiro/steering/mermaid-removal-summary.md
new file mode 100644
index 0000000..ca4b121
--- /dev/null
+++ b/.kiro/steering/mermaid-removal-summary.md
@@ -0,0 +1,51 @@
+---
+inclusion: manual
+---
+
+# Mermaid 功能移除总结
+
+## 移除原因
+
+WP-Markdown 编辑器在保存 Markdown 文件时,会将 Mermaid 代码块保存为一整行(没有真正的换行符),导致 Mermaid 解析器无法正确解析,持续报错:`Parse error on line 1: Expecting 'NEWLINE', 'SPACE', 'GRAPH'`。
+
+尝试了多种解决方案(JavaScript 解码、PHP 预处理、智能格式化等)均失败。
+
+## 已移除内容
+
+1. **settings.php** - 移除 Mermaid 设置项和选项保存逻辑(约 15 行)
+2. **functions.php** - 移除两个 Mermaid 处理函数(约 70 行)
+3. **footer.php** - 移除 Mermaid 加载和渲染代码(约 220 行)
+4. **style.css** - 移除 Mermaid 图表样式(约 25 行)
+5. **本地镜像** - 删除 `assets/vendor/external/mermaid/` 目录
+
+总计移除约 330 行代码和 2 个本地镜像文件。
+
+## 需求文档
+
+已创建 `mermaid-support-requirements.md` 文档,包含:
+- 问题详细分析
+- 已尝试的解决方案
+- 推荐的替代方案(使用支持 Mermaid 的插件)
+- 技术实现参考
+- 测试用例
+
+## 推荐方案
+
+使用支持 Mermaid 的 WordPress 插件:
+- **WP Githuber MD** - 功能强大的 Markdown 编辑器
+- **Markdown Block** - Gutenberg 原生 Markdown 块
+- **Code Syntax Block** - 支持 Mermaid 的代码块插件
+
+## Git 提交
+
+```
+commit 54cbb40
+feat: 移除 Mermaid 支持并创建需求文档
+
+- 从 settings.php 移除 Mermaid 设置项和选项保存逻辑
+- 从 functions.php 移除 Mermaid 代码块预处理函数
+- 从 footer.php 移除 Mermaid 加载和渲染代码
+- 从 style.css 移除 Mermaid 图表样式
+- 删除本地镜像文件 assets/vendor/external/mermaid/
+- 创建 mermaid-support-requirements.md 需求文档
+```
diff --git a/argontheme.js b/argontheme.js
index 5335ae4..8766224 100644
--- a/argontheme.js
+++ b/argontheme.js
@@ -4840,6 +4840,9 @@ void 0;
// ---------- 启动渲染引擎 ----------
+ // 暴露到全局作用域(用于库加载失败时的降级处理)
+ window.MermaidRenderer = MermaidRenderer;
+
// 在 DOM 加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
diff --git a/functions.php b/functions.php
index 8679931..898ba8f 100644
--- a/functions.php
+++ b/functions.php
@@ -9340,6 +9340,168 @@ function argon_add_mermaid_async_attribute($tag, $handle) {
return $tag;
}
- // 添加 async 属性
- return str_replace(' src', ' async src', $tag);
+ // 添加 async 属性和 onerror 事件处理
+ $tag = str_replace(' src', ' async onerror="argonMermaidLoadFallback()" src', $tag);
+
+ return $tag;
}
+
+/**
+ * 添加 Mermaid 库加载失败的降级处理脚本
+ */
+function argon_add_mermaid_fallback_script() {
+ // 只在启用 Mermaid 且页面包含 Mermaid 代码块时添加
+ if (!argon_get_mermaid_option('enabled', false)) {
+ return;
+ }
+
+ global $post;
+ $has_mermaid = false;
+
+ if (is_singular() && isset($post->post_content)) {
+ $has_mermaid = argon_has_mermaid_content($post->post_content);
+ }
+
+ if (!$has_mermaid) {
+ return;
+ }
+
+ // 获取备用 CDN URL 列表
+ $fallback_urls = argon_get_mermaid_fallback_urls();
+ $fallback_urls_json = json_encode($fallback_urls);
+
+ // 输出降级处理脚本
+ ?>
+
+
+
+
+
+
+ Mermaid 库加载失败降级处理测试
+
+
+
+ 🧪 Mermaid 库加载失败降级处理测试
+
+
+ 测试说明
+ 本测试页面用于验证 Mermaid 库加载失败时的降级处理机制。
+
+ - 测试 1: 主 CDN 加载失败,自动尝试备用 CDN
+ - 测试 2: 所有 CDN 都失败,显示友好的错误提示
+ - 测试 3: 备用 CDN 加载成功,正常渲染图表
+
+
+
+
+ 测试控制
+
+
+
+
+
+
+
+
+
+
+ 测试图表
+
+flowchart TD
+ A[开始] --> B{主 CDN 加载}
+ B -->|成功| C[渲染图表]
+ B -->|失败| D[尝试备用 CDN 1]
+ D -->|成功| C
+ D -->|失败| E[尝试备用 CDN 2]
+ E -->|成功| C
+ E -->|失败| F[显示错误提示]
+
+
+
+
+
+
diff --git a/tests/test-mermaid-fallback.php b/tests/test-mermaid-fallback.php
new file mode 100644
index 0000000..6806a18
--- /dev/null
+++ b/tests/test-mermaid-fallback.php
@@ -0,0 +1,249 @@
+ $url) {
+ if (empty($url)) {
+ echo "❌ 失败: URL #{$index} 为空\n";
+ return false;
+ }
+
+ // 验证 URL 格式
+ if (!preg_match('/^https?:\/\/.+\.js$/', $url) && !preg_match('/\/mermaid\.min\.js$/', $url)) {
+ echo "❌ 失败: URL #{$index} 格式无效: {$url}\n";
+ return false;
+ }
+
+ echo "✓ URL #{$index}: {$url}\n";
+ }
+
+ echo "✅ 通过: 备用 CDN URL 列表验证成功\n\n";
+ return true;
+}
+
+/**
+ * 测试 2: 验证降级处理脚本生成
+ */
+function test_fallback_script_generation() {
+ echo "测试 2: 验证降级处理脚本生成\n";
+ echo str_repeat('-', 50) . "\n";
+
+ // 启用 Mermaid
+ update_option('argon_mermaid_enabled', true);
+
+ // 创建一个包含 Mermaid 代码块的测试文章
+ global $post;
+ $post = (object) [
+ 'ID' => 1,
+ 'post_content' => 'flowchart TD\nA-->B'
+ ];
+
+ // 捕获输出
+ ob_start();
+ argon_add_mermaid_fallback_script();
+ $output = ob_get_clean();
+
+ // 验证输出包含必要的脚本
+ $checks = [
+ 'argonMermaidLoadFallback' => '降级处理函数',
+ 'loadMermaidWithFallback' => '递归加载函数',
+ 'showGlobalError' => '错误提示函数',
+ 'fallbackUrls' => '备用 URL 列表',
+ 'script.onerror' => '错误处理',
+ 'script.onload' => '加载成功处理'
+ ];
+
+ $all_passed = true;
+ foreach ($checks as $keyword => $description) {
+ if (strpos($output, $keyword) !== false) {
+ echo "✓ 包含 {$description}\n";
+ } else {
+ echo "❌ 缺少 {$description}\n";
+ $all_passed = false;
+ }
+ }
+
+ if ($all_passed) {
+ echo "✅ 通过: 降级处理脚本生成正确\n\n";
+ return true;
+ } else {
+ echo "❌ 失败: 降级处理脚本不完整\n\n";
+ return false;
+ }
+}
+
+/**
+ * 测试 3: 验证 onerror 属性添加
+ */
+function test_onerror_attribute() {
+ echo "测试 3: 验证 onerror 属性添加\n";
+ echo str_repeat('-', 50) . "\n";
+
+ // 模拟脚本标签
+ $original_tag = '';
+
+ // 调用函数添加属性
+ $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);
+}