Skill: chouraycn/beautiful-mermaid (version 1.0.0)

--- name: beautiful-mermaid version: 1.0.0 description: "Render Mermaid diagrams as beautiful SVG, PNG, or ASCII art. Supports 6 chart types (flowchart, sequence, state, class, ER, XY chart), 16 built-in themes, 5 style presets, CSS-level customization, interactive preview, and batch rendering. Works in terminal, chat, or web environments." description_zh: "Mermaid 图表美化渲染工具，支持流程图/序列图/状态图/类图/ER图/XY图表6种类型，16主题5预设，可导出SVG/PNG/ASCII" description_en: "Beautiful Mermaid diagram renderer (SVG/PNG/ASCII, 16 themes, 5 presets, interactive preview)" homepage: https://github.com/chouraycn/beautiful-mermaid icon: beautiful-mermaid.png --- # Beautiful Mermaid Skill ## 概述 Beautiful Mermaid 是一个高性能的 Mermaid 图表渲染库，专为 AI 时代设计。它将 Mermaid 语法转换为美观的 SVG 图形、PNG 位图或 ASCII 艺术字，支持同步渲染、全主题定制、CSS级样式定制，零 DOM 依赖。 | 说明 | 地址 | |------|------| | 本 Skill | [https://github.com/chouraycn/beautiful-mermaid](https://github.com/chouraycn/beautiful-mermaid) | | 上游项目 | [https://github.com/lukilabs/beautiful-mermaid](https://github.com/lukilabs/beautiful-mermaid) | ## 核心能力 ### 1. 支持的图表类型 - **流程图 (Flowchart)**：`graph TD`、`graph LR`、`graph BT`、`graph RL` 等方向 - **序列图 (Sequence Diagram)**：参与者之间的交互 - **状态图 (State Diagram)**：状态转换 - **类图 (Class Diagram)**：面向对象设计 - **实体关系图 (ER Diagram)**：数据库设计 - **XY 图表**：条形图、折线图、组合图（`xychart-beta` 语法） ### 2. 输出格式 - **SVG**：适用于富 UI 界面，支持透明背景和内联样式 - **PNG**：适用于文档嵌入、高清位图输出，支持自定义尺寸和 DPI - **ASCII/Unicode**：适用于终端环境，支持颜色输出 ### 3. 主题系统 - **15 内置主题**：tokyo-night、dracula、github-dark、nord 等（9 暗色 + 6 亮色） - **完整 7 字段**：每个主题包含 `bg`、`fg`、`line`、`accent`、`muted`、`surface`、`border` 全部字段 - **Mono 模式**：仅需提供背景色(bg)和前景色(fg)，自动推导整套配色 - **自定义主题**：可覆盖任意元素颜色 - **Shiki 集成**：通过 `fromShikiTheme()` 直接使用 VS Code 编辑器主题 #### 主题完整对照表 | 主题名 | 类型 | 背景色 | 强调色(accent) | 推荐预设 | 适用场景 | |--------|------|--------|--------------|---------|---------| | `zinc-light` | 亮色 | `#FFFFFF` | `#3F3F46` | outline | 极简文档 | | `zinc-dark` | 暗色 | `#18181B` | `#A1A1AA` | glass | 极简暗场景 | | `tokyo-night` | 暗色 | `#1a1b26` | `#7aa2f7` | glass | 代码架构图 | | `tokyo-night-storm` | 暗色 | `#24283b` | `#7aa2f7` | modern | 系统设计图 | | `tokyo-night-light` | 亮色 | `#d5d6db` | `#34548a` | modern | 亮色演示 | | `catppuccin-mocha` | 暗色 | `#1e1e2e` | `#cba6f7` | glass | 产品展示 | | `catppuccin-latte` | 亮色 | `#eff1f5` | `#8839ef` | modern | 分享文档 | | `nord` | 暗色 | `#2e3440` | `#88c0d0` | modern | 技术文档暗色 | | `nord-light` | 亮色 | `#eceff4` | `#5e81ac` | outline | 技术文档亮色 | | `dracula` | 暗色 | `#282a36` | `#bd93f9` | gradient | 视觉展示 | | `github-light` | 亮色 | `#ffffff` | `#0969da` | default | GitHub/文档 | | `github-dark` | 暗色 | `#0d1117` | `#4493f8` | modern | 代码深度分析 | | `solarized-light` | 亮色 | `#fdf6e3` | `#268bd2` | outline | 护眼亮色 | | `solarized-dark` | 暗色 | `#002b36` | `#268bd2` | modern | 护眼暗色 | | `one-dark` | 暗色 | `#282c34` | `#c678dd` | gradient | 紫色调 | #### 主题字段说明 | 字段 | 说明 | 用途 | |------|------|------| | `bg` | 画布背景色 | 整体底色 | | `fg` | 主前景/文字色 | 节点标签、主要文字 | | `line` | 连线颜色 | 边/箭头连线 | | `accent` | 强调色 | 箭头头部、子图标题、高亮节点 | | `muted` | 次要色 | 边标签、辅助注释文字 | | `surface` | 节点填充次级色 | 比 bg 深/浅一级，用于节点内填充 | | `border` | 节点边框精确色 | 节点外框描边 | #### 推荐主题 × 预设搭配 ``` tokyo-night + glass → 代码架构图的最佳组合（深蓝毛玻璃） dracula + gradient → 视觉展示最强（紫色渐变阴影） github-light + default → 文档嵌入首选（简洁中性） catppuccin-mocha + glass → 产品 Deck 展示（柔和紫色调） nord + modern → 技术报告暗色版（冰蓝现代感） one-dark + gradient → 紫色调视觉最强（彩色阴影） orange-dark + glass → 暖色系深色主题（橙色毛玻璃） orange-light + modern → 暖色系浅色主题（橙色现代感） ``` > **AI 预览工作规则（最高优先级）**：任何涉及渲染 Mermaid 图表的请求，**无论用户是否已指定主题/预设**，都必须先调用 `preview_url` 打开预览工具，让用户在 preview.html 中直观确认效果后，再执行渲染命令。 > > **触发场景（包括但不限于）**： > - 用户询问主题/配色推荐 > - 用户已明确指定主题和预设（如"用 tokyo-night + glass 渲染"）→ **仍需先打开 preview 让用户确认，不得跳过** > - 需要查看图表效果 > - 任何使用 skill 渲染图表的场景 > > **打开方式**（唯一合法方式）： > - 工具：`preview_url` > - URL：`file://{__SKILL_DIR__}/assets/preview.html`（`__SKILL_DIR__` 为技能安装目录的绝对路径） > > **唯一例外**：用户明确说"直接渲染，不用预览"或"跳过预览"时，才可跳过 preview 步骤。 > > **禁止**： > - 使用 `open`/`start`/`xdg-open` 等命令打开系统浏览器 > - 启动 `python3 -m http.server` 或任何本地 HTTP 服务 > - 用户未明确说跳过时，擅自跳过 preview 直接执行渲染 > > 用户打开预览后，可在 Themes 和 Presets 标签页中直接点击切换对比效果。 ### 4. 样式定制系统 - **节点样式**：圆角、边框、阴影、填充色 - **连线样式**：粗细、颜色、箭头、圆角 - **字体排版**：支持多种字体和响应式适配 - **5种预设风格**：默认、现代简约、渐变、线条轮廓、毛玻璃 ## 使用方法 ### 安装 ```bash npm install beautiful-mermaid # 或 bun add beautiful-mermaid ``` ### 交互式预览工具 提供可视化的样式定制界面，**AI 必须用 `preview_url` 工具直接以文件路径打开**（在 IDE 内置浏览器中显示，无需服务器）： ``` preview_url(`file://{__SKILL_DIR__}/assets/preview.html`) ``` ❌ 禁止使用 `open`/`start` 命令或启动任何 HTTP 服务器。 **预览工具功能**： - **15 主题即时预览**：点击切换，实时查看效果 - **5 种样式预设**：default / modern / gradient / outline / glass - **自定义颜色**：背景色、前景色、连线颜色 - **9 种图表类型**：Flowchart、Sequence、State、Class、ER、Bar、Line、Combo、H-Bar - **实时代码编辑器**：支持自定义 Mermaid 代码，Tab 缩进，400ms debounce 自动渲染 - **状态持久化**：所有选择（主题、预设、颜色、自定义代码）自动保存到 localStorage - **一键导出**：复制代码或导出带完整样式的 SVG 文件 ### 丰富结果 HTML（多图表聚合展示） 当用户需要将**多张 Mermaid 图表**整合为一个专业的展示页面时，使用 `scripts/rich-html.js`。 生成的 HTML 包含： - 顶部 **Badge**（显示主题和预设）+ 标题 + 副标题 - **标签页导航**（每张图表对应一个 Tab，支持 ① ② ③ … 编号） - 每个 Tab 内的**信息卡片网格**（图表类型 + 自定义元数据） - **卡片式 SVG 图表区**（带图标、标题、描述） - 底部页脚（渲染信息） - **完整继承用户选择的主题风格**（背景、前景、强调色等） #### 基本用法 ```bash # 方式一：直接调用脚本 node scripts/rich-html.js "标题" \ --diagrams file1.mmd file2.mmd file3.mmd \ --theme tokyo-night --preset glass \ --subtitle "副标题" \ --output result.html # 方式二：通过 npm script（等效） npm run rich-html -- "标题" \ --diagrams file1.mmd file2.mmd file3.mmd \ --theme tokyo-night --preset glass \ --subtitle "副标题" \ --output result.html ``` #### 批量模式（渲染整个目录） ```bash node scripts/rich-html.js "示例集" \ --batch assets/examples \ --theme dracula --preset gradient \ --output examples-report.html # npm 方式 npm run rich-html -- "示例集" --batch assets/examples --theme dracula --preset gradient --output examples-report.html ``` #### 为图表添加元数据（可选） 在 `.mmd` 文件中通过注释声明元数据，显示在信息卡片中： ``` # @title 用户下单完整流程 # @desc 从进入活动页到订单完成的端到端链路，含限流、库存扣减、异步建单 # @icon 🛒 # @type Flowchart # @meta 关键节点:8 个|覆盖阶段:全链路|主题:Tokyo Night|限流策略:Redis Lua graph TD A[用户进入秒杀页] --> B{限流检查} ... ``` 元数据字段说明： - `@title`：Tab 名称和卡片标题 - `@desc`：卡片描述（小字，显示在标题下方） - `@icon`：图标 emoji（默认根据图表类型自动推断） - `@type`：图表类型说明（自动推断，可手动覆盖） - `@meta`：信息卡片内容，格式 `标签:值|标签:值`（最多 4 组） #### 主题继承规则 生成的 HTML **完整继承**渲染时指定的主题颜色： - 页面背景 = 主题 `bg` - 卡片/导航背景 = 主题 `surface` - 边框 = 主题 `border` - 正文文字 = 主题 `fg` - 次要文字 = 主题 `muted` - 强调色（Badge、Tab 激活、数值） = 主题 `accent` - SVG 图表背景 = 比 `bg` 略深/浅，自动计算 > **AI 工作规则**：生成 rich-html 后，必须用 `preview_url` 工具打开生成的 HTML 文件，让用户直观查看效果。 ### SVG 渲染 ```typescript import { renderMermaidSVG, THEMES } from 'beautiful-mermaid'; // 使用内置主题 const svg = renderMermaidSVG(`graph TD A[开始] --> B{判断} B -->|是| C[执行操作] B -->|否| D[结束]`, THEMES['tokyo-night']); // 使用自定义主题 const customSvg = renderMermaidSVG(diagramCode, { bg: '#1a1b26', fg: '#a9b1d6', accent: '#7aa2f7', transparent: true, }); // 使用 CSS 变量（支持实时主题切换） const cssVarSvg = renderMermaidSVG(diagramCode, { bg: 'var(--background)', fg: 'var(--foreground)', transparent: true, }); // 异步渲染（适用于 async 上下文） const asyncSvg = await renderMermaidSVGAsync(diagramCode, THEMES['dracula']); ``` ### ASCII 渲染 ```typescript import { renderMermaidASCII } from 'beautiful-mermaid'; // Unicode 模式（默认） const unicode = renderMermaidASCII(`graph LR A --> B --> C`); // 纯 ASCII 模式 const ascii = renderMermaidASCII(`graph LR A --> B --> C`, { useAscii: true }); // 带颜色输出 const colored = renderMermaidASCII(`graph LR A --> B --> C`, { colorMode: 'truecolor' }); // 输出示例： // ┌───┐ ┌───┐ ┌───┐ // │ │ │ │ │ │ // │ A │────►│ B │────►│ C │ // │ │ │ │ │ │ // └───┘ └───┘ └───┘ ``` ### React 集成 ```typescript import { renderMermaidSVG } from 'beautiful-mermaid'; function MermaidDiagram({ code }: { code: string }) { const { svg, error } = React.useMemo(() => { try { return { svg: renderMermaidSVG(code, { bg: 'var(--background)', fg: 'var(--foreground)', transparent: true, }), error: null, }; } catch (err) { return { svg: null, error: err as Error }; } }, [code]); if (error) return <pre>{error.message}</pre>; return <div dangerouslySetInnerHTML={{ __html: svg }} />; } ``` ### XY 图表（柱状图、折线图、组合图） ```typescript import { renderMermaidSVG } from 'beautiful-mermaid'; // 柱状图 const bar = renderMermaidSVG(`xychart-beta title "Monthly Revenue" x-axis [Jan, Feb, Mar, Apr, May, Jun] y-axis "Revenue ($K)" 0 --> 500 bar [180, 250, 310, 280, 350, 420]`, THEMES['tokyo-night']); // 折线图 const line = renderMermaidSVG(`xychart-beta title "User Growth" x-axis [Jan, Feb, Mar, Apr, May, Jun] line [1200, 1800, 2500, 3100, 3800, 4500]`, THEMES['github-dark']); // 组合图（柱状 + 折线） const combo = renderMermaidSVG(`xychart-beta title "Sales with Trend" x-axis [Jan, Feb, Mar, Apr, May, Jun] bar [300, 380, 280, 450, 350, 520] line [300, 330, 320, 353, 352, 395]`, THEMES['dracula']); // 交互式 XY 图表（鼠标悬停显示 tooltip） const interactive = renderMermaidSVG(chartCode, { bg: '#1a1b26', fg: '#a9b1d6', accent: '#7aa2f7', interactive: true, }); ``` ### Shiki 主题集成 ```typescript import { getSingletonHighlighter } from 'shiki'; import { renderMermaidSVG, fromShikiTheme } from 'beautiful-mermaid'; // 使用任意 VS Code 主题 const highlighter = await getSingletonHighlighter({ themes: ['vitesse-dark', 'rose-pine', 'material-theme-darker'] }); const colors = fromShikiTheme(highlighter.getTheme('vitesse-dark')); const svg = renderMermaidSVG(code, colors); ``` ## CLI 命令行工具 ### 基本用法 ```bash # 渲染文件为 SVG node scripts/render.js diagram.mmd -o output.svg # 使用指定主题 node scripts/render.js diagram.mmd -t dracula -o output.svg # 渲染为 PNG (高清位图) node scripts/render.js diagram.mmd -f png -o output.png # PNG 自定义尺寸 (宽度) node scripts/render.js diagram.mmd -f png -w 2400 -o output.png # PNG 高清缩放 (2x) node scripts/render.js diagram.mmd -f png -s 2 -o output.png # PNG 高 DPI (印刷质量) node scripts/render.js diagram.mmd -f png --dpi 300 -o output.png # 渲染为 ASCII (终端) node scripts/render.js diagram.mmd -f ascii # XY 图表启用交互式 tooltip node scripts/render.js chart.mmd --interactive -o chart.svg # 直接传入代码 (注意：Mermaid 代码中换行用 \n) node scripts/render.js -c "graph TD\nA --> B" -o output.svg ``` ### 命令行参数 | 参数 | 说明 | |------|------| | `--format`, `-f` | 输出格式: `svg` (默认) \| `ascii` \| `png` | | `--theme`, `-t` | 主题名称: tokyo-night, dracula, nord 等 15 个，或自定义 JSON | | `--output`, `-o` | 输出文件路径（批量模式为输出目录） | | `--code`, `-c` | 直接传入 Mermaid 代码 | | `--bg` | 背景色 (如: #f7f7fa) | | `--fg` | 前景色/文字颜色 (如: #27272a) | | `--line` | 连线颜色 (如: #6b7280) | | `--preset`, `-p` | 样式预设: `default` \| `modern` \| `gradient` \| `outline` \| `glass`（可与 --theme 联用） | | `--width`, `-w` | PNG 输出宽度 (默认: 1200px) | | `--scale`, `-s` | PNG 缩放比例 (默认: 1, 范围: 0.5-4) | | `--dpi` | PNG 输出 DPI (默认: 144, 范围: 72-600) | | `--interactive` | 启用交互式 tooltip (仅 XY 图表) | | `--color-mode` | ASCII 颜色模式: `none` \| `auto` \| `ansi16` \| `ansi256` \| `truecolor` \| `html` | | `--batch` | 批量模式: 渲染指定目录下所有 .mmd 文件 | | `--list-themes` | 列出所有可用主题及其推荐预设搭配 | | `--help`, `-h` | 显示帮助信息 | ### PNG 输出说明 PNG 格式适用于文档嵌入、PPT 演示等场景： - **默认宽度**：1200px - **默认 DPI**：144（适合屏幕显示） - **缩放范围**：0.5x - 4x (通过 `-s` 参数) - **DPI 范围**：72 - 600 (通过 `--dpi` 参数，72=屏幕，300=印刷) - **透明背景**：PNG 输出支持透明背景 - **高质量**：默认 100% 质量输出 ```bash # 屏幕显示 (默认 144 DPI) node scripts/render.js diagram.mmd -f png -o screen.png # 印刷质量 (300 DPI) node scripts/render.js diagram.mmd -f png --dpi 300 -o print.png # 2K 高清 (2400px) node scripts/render.js diagram.mmd -f png -w 2400 -o hd.png # 4K 超清 (4800px) node scripts/render.js diagram.mmd -f png -s 4 -o 4k.png # 图标尺寸 (600px) node scripts/render.js diagram.mmd -f png -w 600 -o icon.png ``` ### 样式预设 通过 `--preset` 参数使用与 `preview.html` 一致的样式预设。**推荐与 `--theme` 联用**： ```bash # ✅ 推荐：主题 + 预设自由组合 node scripts/render.js diagram.mmd -t tokyo-night -p glass -o output.svg node scripts/render.js diagram.mmd -t dracula -p gradient -o output.svg node scripts/render.js diagram.mmd -t catppuccin-mocha -p glass -f png -o output.png # 也支持自定义颜色 + 预设 node scripts/render.js diagram.mmd --bg '#f7f7fa' --fg '#27272a' --line '#6b7280' -p modern -o output.svg node scripts/render.js diagram.mmd --bg '#f7f7fa' --fg '#27272a' --line '#6b7280' -p gradient -o output.svg node scripts/render.js diagram.mmd --bg '#f7f7fa' --fg '#27272a' --line '#6b7280' -p outline -o output.svg node scripts/render.js diagram.mmd --bg '#f7f7fa' --fg '#27272a' --line '#6b7280' -p glass -f png -o output.png ``` **可用预设对比：** | 预设 | 圆角 | 边框 | 阴影 | 场景 | |------|------|------|------|------| | default | 8px | 2px | 中等 | 通用场景 | | modern | 16px | 1px | 柔和 | 现代产品 UI | | gradient | 12px | 0px | 彩色 | 视觉展示 | | outline | 4px | 2px | 无 | 技术文档 | | glass | 12px | 1px | 高模糊 | 深色叠加 | ### 批量渲染 使用 `--batch` 参数可以一次渲染目录下所有 `.mmd` 文件： ```bash # 批量渲染为 SVG（输出到源目录） node scripts/render.js --batch ./diagrams -t dracula # 批量渲染为 PNG，输出到指定目录 node scripts/render.js --batch ./diagrams -f png -t tokyo-night -o ./output # 批量渲染为 ASCII node scripts/render.js --batch ./examples -f ascii --color-mode ansi256 ``` 批量模式会遍历指定目录下的所有 `.mmd` 文件，自动按文件名生成对应的 `.svg`/`.png`/`.txt` 输出文件。 ### ASCII 颜色模式 通过 `--color-mode` 参数控制 ASCII 渲染的着色方式： ```bash # 无颜色（纯 ASCII） node scripts/render.js diagram.mmd -f ascii --color-mode none # 256 色模式（兼容更多终端） node scripts/render.js diagram.mmd -f ascii --color-mode ansi256 # HTML 模式（可在网页中展示） node scripts/render.js diagram.mmd -f ascii --color-mode html ``` ## 主题配置 ### `--theme` 与 `--preset` 联用（推荐） `--preset` 现在可以与 `--theme` 联用，不再互斥： ```bash # ✅ 推荐：--theme + --preset 自由组合 node scripts/render.js diagram.mmd -t dracula -p gradient -o out.svg node scripts/render.js diagram.mmd -t tokyo-night -p glass -o out.svg node scripts/render.js diagram.mmd -t github-light -p modern -o out.svg # ✅ 仍然支持：--preset 配合 --bg/--fg node scripts/render.js diagram.mmd --bg '#f7f7fa' --fg '#27272a' -p modern -o out.svg # ✅ 自动推荐预设：只指定 --theme，自动使用该主题的推荐预设 node scripts/render.js diagram.mmd -t dracula -o out.svg # 输出: ✓ SVG 已保存: out.svg + preset:gradient (dracula 推荐 gradient) ``` ### 查看所有主题 ```bash # 列出所有主题及其推荐预设 node scripts/render.js --list-themes # 输出示例: # ● 暗色主题 (Dark): # tokyo-night → 推荐预设: glass # dracula → 推荐预设: gradient # ... # ● 亮色主题 (Light): # github-light → 推荐预设: default # ... ``` ### 参数优先级 1. `--bg` + `--fg` → 自定义基础配色（line/accent/muted 自动推导） 2. `--theme` 名称 → 从内置 15 主题取完整 7 字段配色 3. `--theme` JSON → 直接使用 JSON 对象（缺省字段自动补全） 4. `--preset` → 应用形状预设（与任何颜色来源都可以搭配） 5. 自动推荐预设 → 不传 `--preset` 时，自动使用主题推荐的预设 ### CSS 样式注入原理 样式预设通过 **CSS 变量覆盖** 实现： 1. beautiful-mermaid 库使用 CSS 变量：`var(--_node-fill)`、`var(--_line)` 2. render.js 在 SVG `<svg>` 标签后注入 `<style>` 标签 3. 通过 `!important` 覆盖 CSS 变量和元素属性 ```css /* 注入的 CSS 结构 */ svg { --_text: #27272a !important; --_line: #6b7280 !important; --_node-fill: #f7f7fa !important; } rect { rx: 16px !important; } /* 圆角 */ path { stroke-width: 1.5px !important; } /* 线宽 */ ``` ### Mono 主题（最简单） ```typescript const monoTheme = { bg: '#0f0f0f', // 背景色 fg: '#e0e0e0', // 前景色 }; ``` ### 丰富主题 ```typescript const richTheme = { bg: '#0f0f0f', // 背景色 fg: '#e0e0e0', // 前景色 accent: '#ff6b6b', // 箭头/高亮色 muted: '#666666', // 次要文字/标签 surface: '#1a1a1a', // 节点填充色 border: '#333333', // 边框色 }; ``` ## 风格选择 → 生成 → 管理 工作流 ### 推荐工作流（Playground → CLI → 管理） > ⚠️ **强制规则**：Step 1 不可跳过。即使用户已指定主题和预设，AI 也必须先打开 Playground 让用户在 preview.html 中确认效果，再执行 Step 2 渲染。唯一例外：用户明确说"跳过预览"。 **Step 1 — 在 Playground 中确认风格（强制）** AI 直接用 `preview_url` 工具打开 `file://{__SKILL_DIR__}/assets/preview.html`（无需服务器，见上方"AI 预览工作规则"）。用户在页面中选择主题和预设后，底部会实时显示 CLI 命令栏： ``` node scripts/render.js input.mmd -t dracula -p gradient -o output-dracula-gradient.svg ``` **Step 2 — 将选定风格传递给生成** Playground 提供三种传递方式： | 按钮 | 内容 | 用途 | |------|------|------| | **CLI 命令栏（点击）** | `node scripts/render.js ... -t <theme> -p <preset>` | 直接在终端粘贴执行 | | **Copy Config** | JSON（含 theme、preset、颜色对象、CLI命令） | AI / 脚本读取，参数化批量生成 | | **Copy Code** | `renderMermaidSVG(code, THEMES['...'])` | 嵌入 JS/TS 代码 | **Step 3 — 统一管理生成的文件** - **Download SVG**：文件名自动为 `mermaid-{theme}-{preset}.svg`，方便按风格整理 - **Export HTML**：生成包含图表 + 主题配色 + CLI 命令的独立 HTML，可直接分享或存档 **Step 4（可选）— 多图表丰富展示 HTML** 当用户有**多张 Mermaid 图表**需要整合展示时（如系统设计文档、架构评审、技术报告），使用 `scripts/rich-html.js`，生成带标签页导航和信息卡片的专业展示页面： ```bash # 使用 Step 1 中用户确认的主题和预设 node scripts/rich-html.js "报告标题" \ --diagrams file1.mmd file2.mmd file3.mmd \ --theme <用户确认的主题> --preset <用户确认的预设> \ --subtitle "副标题" \ --output result.html ``` 生成后**必须**用 `preview_url` 打开结果 HTML，让用户预览效果。 > **工作流关键点**：rich-html.js 不需要单独指定颜色，只需传入 Step 1 中用户已在 preview.html 中确认的 `--theme` 和 `--preset`，所有颜色自动继承。 --- ## 多图表丰富展示 HTML 规范 > 当用户要求将**多张图表整合为报告/文档/展示页面**时，**必须使用 `scripts/rich-html.js`**，不要手动拼接 HTML。 ### 触发场景 - 用户说「生成一个报告」「做一份文档」「整合成页面」「汇总展示」 - 用户有 2 张及以上图表需要一并展示 - 用户要求类似「像参考文件那样的丰富展示」 ### 生成命令（直接执行，无需用户确认） ```bash # 方式一：直接调用脚本 node scripts/rich-html.js "<标题>" \ --diagrams <file1.mmd> [file2.mmd ...] \ --theme <主题> --preset <预设> \ --subtitle "<副标题>" \ --output <输出路径.html> # 方式二：npm script（等效，-- 之后的参数透传） npm run rich-html -- "<标题>" \ --diagrams <file1.mmd> [file2.mmd ...] \ --theme <主题> --preset <预设> \ --output <输出路径.html> ``` ### 主题风格继承（核心规则） **rich-html.js 自动从 `--theme` 和 `--preset` 继承所有颜色**，AI **无需手动指定**任何颜色值： | 页面元素 | 来源 | |---------|------| | 页面背景 | 主题 `bg` | | 卡片/导航背景 | 主题 `surface` | | 边框线 | 主题 `border` | | 正文文字 | 主题 `fg` | | 次要文字/标签 | 主题 `muted` | | 强调色（Badge/激活Tab/数值） | 主题 `accent` | | SVG 图表背景 | `bg` 自动加深/变浅 | ### .mmd 文件中添加元数据（可选但推荐） 为了让信息卡片显示有意义的内容，在图表文件中添加注释元数据： ``` # @title 用户下单完整流程 # @desc 从进入活动页到订单完成的端到端链路 # @icon 🛒 # @meta 关键节点:8 个|覆盖阶段:全链路|主题:Tokyo Night|限流:Redis Lua graph TD ... ``` ### 生成后的展示规则 生成 HTML 后，**必须**调用 `preview_url` 工具打开文件，让用户直接在 IDE 内预览： ``` preview_url("file:///绝对路径/result.html") ``` --- ## ⚠️ AI 生成独立 HTML 文件的强制规范 > 当用户要求将 Mermaid 图表输出为**独立 HTML 文件**时，AI **必须**严格按照本节模板生成，否则会出现样式串扰、SVG 裁剪、线条无颜色等问题。 ### SVG 内联进 HTML 的三件事（缺一不可） **① 每个 SVG 必须有唯一 id** 用 `injectStylesToSVG(svg, theme, preset, 'diagram-flow')` 第 4 个参数传入不同 id。 内联多个 SVG 时，每张图用不同 id（如 `diagram-flow`、`diagram-seq`、`diagram-3`）。 **② 内部 `<style>` 选择器必须用 id 作用域化** `generateCSSStyles` 会自动完成（传入 svgId 参数后），输出： ```css #diagram-flow { --_line: #A1A1AA; } /* ✅ 有 id 作用域，不会污染其他 SVG */ #diagram-flow text { ... } #diagram-flow path { ... } ``` 而非裸选择器： ```css svg { --_line: #A1A1AA; } /* ❌ 内联进 HTML 后会影响所有 SVG */ text { ... } path { ... } ``` **③ 外层 CSS 必须同时设 `max-width: 100%` + `height: auto`** `max-width: 100%` 单独不够。当容器宽度窄于 SVG 的硬编码宽度时，SVG 宽度被压缩， 但若不设 `height: auto`，高度不跟随缩放，图表下半部分被截断。 ```css /* ✅ 正确：两个属性必须同时设置 */ .diagram-wrap svg { display: block; max-width: 100%; height: auto; /* CRITICAL: 缺少这行会导致窄屏下图表被垂直截断 */ width: auto; } ``` ### 独立打开 SVG 时线条无颜色的修复 SVG 内部元素使用 `stroke="var(--_line)"`，该变量通过 CSS 从 `--line` 映射而来。 `injectStylesToSVG` 会自动在 `<svg style="...">` 属性中内联所有 `--_xxx` 精确颜色值， 确保在 Figma、Inkscape、系统预览等独立软件中打开时线条也有正确颜色。 核心修复： ```html <!-- 修复后的 SVG 开标签（--_line 等变量直接内联在 style 属性中） --> <svg id="diagram-flow" style="background:#FFF;--_line:#A1A1AA;--_arrow:#A1A1AA;--_text:#27272A;..." width="229" height="606" viewBox="0 0 229 606"> ``` ### 核心禁忌（必须遵守） 1. **绝对不要给容器 div 设置 `overflow: hidden`** — 这会截断超出容器的 SVG 内容 2. **绝对不要设置固定的容器高度（如 `height: 400px`）再嵌入 SVG** — SVG 会被截断 3. **多张 SVG 内联时，每张必须有不同 id** — 否则 `#id` 作用域化后还是会互相影响 4. **容器 CSS 必须同时写 `max-width:100%` 和 `height:auto`** — 只写前者会在窄屏裁剪 ### 标准代码（Node.js 生成 HTML 时使用） ```js import { renderMermaidSVG } from 'beautiful-mermaid'; import { injectStylesToSVG, resolveTheme } from './scripts/styles.js'; const theme = resolveTheme('zinc-light'); // 多张图时：每张图传入不同的 svgId（第 4 个参数） const flowSVG = injectStylesToSVG(renderMermaidSVG(flowCode, theme), theme, 'default', 'diagram-flow'); const seqSVG = injectStylesToSVG(renderMermaidSVG(seqCode, theme), theme, 'default', 'diagram-seq'); ``` ```html <style> /* ✅ 正确：max-width + height:auto 同时设置 */ .diagram-wrap svg { display: block; max-width: 100%; height: auto; /* CRITICAL：防止窄屏垂直裁剪 */ width: auto; } </style> <div class="diagram-wrap"> <!-- flowSVG 已含 id="diagram-flow" 和作用域化 <style> --> </div> ``` ### SVG 嵌入前的检查清单 AI 在将 SVG 写入 HTML 前，必须确认： - [ ] `<svg id="diagram-xxx">` — 每张图有**唯一 id**，多图时 id 不重复 - [ ] `<svg width="XXX" height="YYY">` — `width` 和 `height` 都是**具体像素数值**，不是 `100%` - [ ] `<svg viewBox="0 0 XXX YYY">` — `viewBox` 存在 - [ ] SVG 内 `<style>` 选择器以 `#id` 开头（如 `#diagram-flow {`），无裸 `svg {` 或 `text {` - [ ] SVG `style` 属性含 `--_line`、`--_arrow` 等直接颜色值 - [ ] 外层 CSS 同时设 `max-width: 100%` + `height: auto` ### 常见错误对比 ```html <!-- ❌ 错误1：裸选择器污染（两张图样式互串） --> <style>svg { --_line: red; } text { color: blue; }</style> <!-- ❌ 错误2：只设 max-width，窄屏下图表被竖向截断 --> .diagram-wrap svg { max-width: 100%; } /* 缺 height:auto */ <!-- ❌ 错误3：固定容器高度截断图表 --> <div style="height:400px; overflow:hidden"> <svg width="800" height="600" ...>...</svg> </div> <!-- ✅ 正确：id 作用域 + max-width/height:auto + 具体宽高 --> .diagram-wrap svg { display:block; max-width:100%; height:auto; width:auto; } <svg id="diagram-flow" width="229" height="606" viewBox="0 0 229 606" style="--_line:#A1A1AA;..."> <style>#diagram-flow { --_line: #A1A1AA; } #diagram-flow path { stroke:#A1A1AA; }</style> </svg> ``` --- ### AI 使用 Copy Config 的格式 当 AI 拿到「Copy Config」的内容后，可直接解析并用于批量渲染： ```json { "theme": "dracula", "preset": "gradient", "themeColors": { "bg": "#282a36", "fg": "#f8f8f2", ... }, "cliCommand": "node scripts/render.js input.mmd -t dracula -p gradient -o output-dracula-gradient.svg", "npmUsage": "renderMermaidSVG(code, THEMES['dracula'])" } ``` AI 工作流示例： 1. 用户在 Playground 选好 `tokyo-night + glass`，点「Copy Config」 2. 将 JSON 粘贴给 AI 3. AI 解析 `theme` / `preset`，用 `-t tokyo-night -p glass` 批量渲染所有 `.mmd` 文件 --- ## 常见用例 ### 1. 系统架构图 ```mermaid graph TB User[用户] -->|HTTP| Nginx[Nginx] Nginx -->|反向代理| App[应用服务器] App -->|查询| Redis[(Redis缓存)] App -->|读写| MySQL[(MySQL数据库)] ``` ### 2. 流程图 ```mermaid graph TD Start([开始]) --> Input[输入数据] Input --> Validate{验证?} Validate -->|通过| Process[处理数据] Validate -->|失败| Error[显示错误] Process --> Save[保存结果] Save --> End([结束]) Error --> Input ``` ### 3. 序列图 ```mermaid sequenceDiagram participant U as 用户 participant A as API网关 participant S as 服务 participant D as 数据库 U->>A: 发送请求 A->>S: 转发请求 S->>D: 查询数据 D-->>S: 返回数据 S-->>A: 处理结果 A-->>U: 响应 ``` ### 4. 状态图 ```mermaid stateDiagram-v2 [*] --> 待处理 待处理 --> 处理中: 开始处理 处理中 --> 已完成: 处理成功 处理中 --> 失败: 处理失败 失败 --> 待处理: 重试 已完成 --> [*] ``` ### 5. 类图 ```mermaid classDiagram class Animal { +String name +int age +makeSound() } class Dog { +bark() } class Cat { +meow() } Animal <|-- Dog Animal <|-- Cat ``` ### 6. ER 图 ```mermaid erDiagram CUSTOMER ||--o{ ORDER : places ORDER ||--|{ LINE-ITEM : contains PRODUCT ||--o{ LINE-ITEM : "is in" CUSTOMER { int id PK string name string email } ORDER { int id PK date created string status } ``` ### 7. XY 图表 — 柱状图 ```mermaid xychart-beta title "Monthly Revenue" x-axis [Jan, Feb, Mar, Apr, May, Jun] y-axis "Revenue ($K)" 0 --> 500 bar [180, 250, 310, 280, 350, 420] ``` ### 8. XY 图表 — 折线图 ```mermaid xychart-beta title "User Growth" x-axis [Jan, Feb, Mar, Apr, May, Jun] line [1200, 1800, 2500, 3100, 3800, 4500] ``` ### 9. XY 图表 — 组合图 ```mermaid xychart-beta title "Sales with Trend" x-axis [Jan, Feb, Mar, Apr, May, Jun] bar [300, 380, 280, 450, 350, 520] line [300, 330, 320, 353, 352, 395] ``` ## API 参考 ### `renderMermaidSVG(text, options?): string` 将 Mermaid 代码渲染为 SVG 字符串。**同步**，可直接用于 React `useMemo()`。 **参数：** - `text: string` - Mermaid 图表代码 - `options?: RenderOptions` - 渲染选项 **RenderOptions：** | 选项 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `bg` | `string` | `#FFFFFF` | 背景色（支持 CSS 变量） | | `fg` | `string` | `#27272A` | 前景色（支持 CSS 变量） | | `line` | `string?` | — | 连线颜色 | | `accent` | `string?` | — | 箭头、高亮色 | | `muted` | `string?` | — | 次要文字、标签 | | `surface` | `string?` | — | 节点填充色 | | `border` | `string?` | — | 节点边框色 | | `font` | `string` | `Inter` | 字体族 | | `transparent` | `boolean` | `false` | 透明背景 | | `padding` | `number` | `40` | 画布内边距 (px) | | `nodeSpacing` | `number` | `24` | 同级节点水平间距 | | `layerSpacing` | `number` | `40` | 层间垂直间距 | | `componentSpacing` | `number` | `24` | 断开组件间距 | | `interactive` | `boolean` | `false` | XY 图表悬浮 tooltip | **返回：** `string` - SVG 字符串 ### `renderMermaidSVGAsync(text, options?): Promise<string>` 异步版本，相同输出。适用于 async 上下文。 ### `renderMermaidASCII(text, options?): string` 将 Mermaid 代码渲染为 ASCII/Unicode 艺术字。**同步**。 **参数：** - `text: string` - Mermaid 图表代码 - `options?: AsciiRenderOptions` - 渲染选项 **AsciiRenderOptions：** | 选项 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `useAscii` | `boolean` | `false` | 使用 ASCII（true）或 Unicode（false） | | `paddingX` | `number` | `5` | 节点水平间距 | | `paddingY` | `number` | `5` | 节点垂直间距 | | `boxBorderPadding` | `number` | `1` | 节点框内边距 | | `colorMode` | `string` | `'auto'` | `'none'` \| `'auto'` \| `'ansi16'` \| `'ansi256'` \| `'truecolor'` \| `'html'` | | `theme` | `Partial<AsciiTheme>` | — | 自定义 ASCII 颜色 | **返回：** `string` - ASCII 字符串 ### `parseMermaid(text): MermaidGraph` 将 Mermaid 代码解析为结构化图对象（用于自定义处理）。支持流程图和状态图。 ### `fromShikiTheme(theme): DiagramColors` 从 Shiki 主题对象提取图表颜色。映射关系： | 编辑器颜色 | 图表角色 | |-----------|---------| | `editor.background` | `bg` | | `editor.foreground` | `fg` | | `editorLineNumber.foreground` | `line` | | `focusBorder` / keyword token | `accent` | | comment token | `muted` | | `editor.selectionBackground` | `surface` | | `editorWidget.border` | `border` | ### `THEMES` 内置主题集合，每个主题包含完整 7 字段（`bg`/`fg`/`line`/`accent`/`muted`/`surface`/`border`）。完整色值见"核心能力 → 主题完整对照表"。 | 主题名 | 类型 | accent 色 | 推荐预设 | |--------|------|-----------|---------| | `tokyo-night` | 暗色 | `#7aa2f7` | glass | | `tokyo-night-storm` | 暗色 | `#7aa2f7` | modern | | `tokyo-night-light` | 亮色 | `#34548a` | modern | | `dracula` | 暗色 | `#bd93f9` | gradient | | `github-dark` | 暗色 | `#4493f8` | modern | | `github-light` | 亮色 | `#0969da` | default | | `nord` | 暗色 | `#88c0d0` | modern | | `nord-light` | 亮色 | `#5e81ac` | outline | | `one-dark` | 暗色 | `#c678dd` | gradient | | `catppuccin-mocha` | 暗色 | `#cba6f7` | glass | | `catppuccin-latte` | 亮色 | `#8839ef` | modern | | `solarized-dark` | 暗色 | `#268bd2` | modern | | `solarized-light` | 亮色 | `#268bd2` | outline | | `zinc-light` | 亮色 | `#3F3F46` | outline | | `zinc-dark` | 暗色 | `#A1A1AA` | glass | ### `DEFAULTS` 默认颜色值（完整 7 字段）： ```typescript { bg: '#FFFFFF', // 画布背景 fg: '#27272A', // 主文字 line: '#A1A1AA', // 连线 accent: '#3F3F46', // 强调色 muted: '#71717A', // 次要色 surface: '#F4F4F5', // 节点填充次级色 border: '#D4D4D8', // 节点边框 } ``` ## 代码级样式预设 ```javascript // 导入样式预设模块（ESM） import { STYLE_PRESETS, generateCSSStyles, injectStylesToSVG } from './scripts/styles.js'; import { renderMermaidSVG } from 'beautiful-mermaid'; // 1. 渲染基础 SVG const svg = renderMermaidSVG(code, { bg: '#f7f7fa', fg: '#27272a', line: '#6b7280' }); // 2. 应用样式预设 (Node.js 环境) const styledSvg = injectStylesToSVG(svg, { bg: '#f7f7fa', fg: '#27272a', line: '#6b7280' }, 'modern'); // 3. 获取 CSS 字符串（可选） const css = generateCSSStyles({ bg: '#f7f7fa', fg: '#27272a' }, 'modern'); console.log(css); ``` ## 安全注意事项 1. **SVG 输出安全**：SVG 内容包含用户控制的文本，直接嵌入 HTML 可能导致 XSS。不要将不可信来源提供的 Mermaid 代码直接渲染后嵌入网页。如果 SVG 需要在浏览器中展示，建议对输出进行 sanitize 处理（如使用 DOMPurify）。 2. **输入信任边界**：本 Skill 设计用于渲染开发者自己编写的 .mmd 文件。如果渲染来自用户输入的 Mermaid 代码，请务必进行输入校验和输出清理。 3. **依赖安全**：定期检查 `npm audit` 输出，及时升级 beautiful-mermaid 至包含安全修复的最新版本。 ## 注意事项 1. **同步渲染**：`renderMermaidSVG` 和 `renderMermaidASCII` 都是同步函数，可直接用于 React 的 `useMemo` 2. **异步渲染**：`renderMermaidSVGAsync` 返回 `Promise<string>`，适用于 async 上下文 3. **无 DOM 依赖**：可在 Node.js、浏览器、终端等多环境运行 4. **错误处理**：建议用 try-catch 包裹渲染调用 5. **性能**：100+ 图表可在 500ms 内完成渲染 6. **`--preset` 与 `--theme` 可以自由联用**：`-t dracula -p gradient` 完全合法；不传 `--preset` 时自动使用该主题的推荐预设 7. **CSS 注入顺序**：注入的 `<style>` 标签位于 `<svg>` 之后，使用 `!important` 确保覆盖 8. **CSS 变量主题切换**：传递 `var(--xxx)` 作为颜色值，SVG 可实时响应主题变化 9. **XY 图表**：使用 `xychart-beta` 语法，`accent` 颜色驱动图表系列配色 10. **linkStyle 支持**：流程图和状态图支持 `linkStyle` 内联边样式覆盖 ## 相关链接 - [在线演示](https://agents.craft.do/mermaid) - [Mermaid 语法文档](https://mermaid.js.org/) - [GitHub 仓库](https://github.com/lukilabs/beautiful-mermaid) - [Issue #79: Mermaid config 支持](https://github.com/lukilabs/beautiful-mermaid/issues/79) — 上游计划中，待实现后将跟进 - [Issue #59: 更多图表类型](https://github.com/lukilabs/beautiful-mermaid/issues/59) — Mindmap、Pie、Gantt 等类型待上游支持后同步

path: README.md
content: # Beautiful Mermaid Skill 使用指南

## 项目地址

| 说明 | 地址 |
|------|------|
| 本 Skill | [https://github.com/chouraycn/beautiful-mermaid](https://github.com/chouraycn/beautiful-mermaid) |
| 上游项目 | [https://github.com/lukilabs/beautiful-mermaid](https://github.com/lukilabs/beautiful-mermaid) |

## 快速开始

### 1. 安装依赖

```bash
cd beautiful-mermaid
npm install
```

### 2. 交互式预览 (推荐)

```bash
# 在浏览器中打开样式定制预览工具
open assets/preview.html
```

**预览工具功能**：

- 15 个内置主题即时预览，点击切换
- 9 种图表类型：流程图、序列图、状态图、类图、ER 图、柱状图、折线图、组合图、水平柱状图
- 节点样式：背景色、圆角、边框、阴影
- 连线样式：粗细、颜色、箭头、圆角
- 5 种预设风格：默认、现代简约、渐变、线条轮廓、毛玻璃
- 一键导出带完整样式的 SVG 文件
- 使用 beautiful-mermaid 库渲染，与 CLI 输出一致

### 3. 运行示例

```bash
# 流程图
node scripts/render.js assets/examples/flowchart-basic.mmd -o flowchart-basic.svg

# 序列图
node scripts/render.js assets/examples/sequence-diagram.mmd -o sequence-diagram.svg

# 状态图
node scripts/render.js assets/examples/state-diagram.mmd -o state-diagram.svg

# ER 图
node scripts/render.js assets/examples/er-diagram.mmd -o er-diagram.svg

# 类图
node scripts/render.js assets/examples/class-diagram.mmd -o class-diagram.svg

# XY 柱状图
node scripts/render.js assets/examples/xychart-bar.mmd -o xychart-bar.svg

# XY 折线图
node scripts/render.js assets/examples/xychart-line.mmd -o xychart-line.svg

# XY 组合图
node scripts/render.js assets/examples/xychart-combo.mmd -o xychart-combo.svg
```

### 4. 使用不同主题

```bash
# 使用 dracula 主题
node scripts/render.js assets/examples/flowchart-basic.mmd -t dracula -o flowchart-dracula.svg

# 使用 catppuccin-mocha 主题
node scripts/render.js assets/examples/flowchart-basic.mmd -t catppuccin-mocha -o flowchart-catppuccin.svg

# 使用 github-light 主题 (亮色)
node scripts/render.js assets/examples/flowchart-basic.mmd -t github-light -o flowchart-light.svg
```

### 5. 渲染为 PNG (高清位图)

```bash
# 标准尺寸 PNG (1200px)
node scripts/render.js assets/examples/flowchart-basic.mmd -f png -o flowchart.png

# 2K 高清 PNG (2400px)
node scripts/render.js assets/examples/flowchart-basic.mmd -f png -w 2400 -o flowchart-hd.png

# 2x 缩放 PNG
node scripts/render.js assets/examples/flowchart-basic.mmd -f png -s 2 -o flowchart-2x.png

# 4K 超清 PNG (4800px)
node scripts/render.js assets/examples/flowchart-basic.mmd -f png -s 4 -o flowchart-4k.png

# 印刷质量 (300 DPI)
node scripts/render.js assets/examples/flowchart-basic.mmd -f png --dpi 300 -o flowchart-print.png
```

### 6. 渲染为 ASCII (终端输出)

```bash
node scripts/render.js assets/examples/flowchart-basic.mmd -f ascii
```

### 7. 自定义颜色 (Mono 模式)

```bash
# 自定义背景和前景色
node scripts/render.js assets/examples/flowchart-basic.mmd \
  --bg '#1a1b2e' \
  --fg '#e0e0e0' \
  --accent '#ff6b6b' \
  -o flowchart-custom.svg
```

### 8. XY 图表交互式 tooltip

```bash
# 启用鼠标悬停 tooltip
node scripts/render.js assets/examples/xychart-bar.mmd --interactive -o chart-interactive.svg
```

## 示例文件说明

| 文件 | 说明 |
|------|------|
| `flowchart-basic.mmd` | 基础流程图示例 |
| `sequence-diagram.mmd` | 序列图示例 |
| `state-diagram.mmd` | 状态图示例 |
| `er-diagram.mmd` | 实体关系图示例 |
| `class-diagram.mmd` | 类图示例 |
| `xychart-bar.mmd` | XY 柱状图示例 |
| `xychart-line.mmd` | XY 折线图示例 |
| `xychart-combo.mmd` | XY 组合图示例 |
| `system-architecture.mmd` | 复杂系统架构图 |
| `erp-*.mmd` | ERP 业务流程图 |

## 可用主题列表

| 主题 | 类型 | 说明 |
|------|------|------|
| tokyo-night | 暗色 | 东京之夜 |
| tokyo-night-storm | 暗色 | 东京之夜·暴风雨 |
| catppuccin-mocha | 暗色 | Catppuccin Mocha |
| nord | 暗色 | Nord 蓝 |
| dracula | 暗色 | 德古拉红 |
| github-dark | 暗色 | GitHub 暗色 |
| one-dark | 暗色 | One Dark |
| solarized-dark | 暗色 | Solarized 暗色 |
| zinc-dark | 暗色 | Zinc 暗色 |
| github-light | 亮色 | GitHub 亮色 |
| solarized-light | 亮色 | Solarized 亮色 |
| nord-light | 亮色 | Nord 亮色 |
| zinc-light | 亮色 | Zinc 亮色 |
| catppuccin-latte | 亮色 | Catppuccin Latte |
| tokyo-night-light | 亮色 | 东京之夜·亮色 |

## 提示

1. **交互式预览**：使用 `open assets/preview.html` 打开可视化样式定制工具
2. **SVG 输出**：适合嵌入网页、PPT 或文档中，支持透明背景和内联样式
3. **PNG 输出**：适合文档嵌入、PPT 演示，支持自定义尺寸 (默认 1200px，缩放 0.5-4x，DPI 72-600)
4. **ASCII 输出**：适合在终端、聊天界面或 Markdown 文档中展示
5. **透明背景**：设置 `transparent: true` 可以生成透明背景的 SVG
6. **主题选择**：暗色主题适合开发文档，亮色主题适合正式文档
7. **样式预设**：5 种预设风格（默认、现代简约、渐变、线条轮廓、毛玻璃）
8. **XY 图表**：使用 `xychart-beta` 语法，支持柱状图、折线图、组合图
9. **交互式图表**：使用 `--interactive` 参数为 XY 图表添加鼠标悬停 tooltip


path: package-lock.json
content: {
  "name": "beautiful-mermaid-skill",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "beautiful-mermaid-skill",
      "version": "1.0.0",
      "license": "MIT",
      "dependencies": {
        "beautiful-mermaid": "^1.1.3",
        "sharp": "^0.34.5"
      },
      "devDependencies": {
        "@types/node": "^20.10.0",
        "esbuild": "^0.27.4"
      },
      "engines": {
        "node": ">=16.0.0"
      }
    },
    "node_modules/@emnapi/runtime": {
      "version": "1.9.1",
      "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz",
      "integrity": [REDACTED_SECRET],
      "optional": true,
      "dependencies": {
        "tslib": "^2.4.0"
      }
    },
    "node_modules/@esbuild/aix-ppc64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ppc64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "aix"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/android-arm": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "android"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/android-arm64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "android"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/android-x64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "android"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/darwin-arm64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "darwin"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/darwin-x64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "darwin"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/freebsd-arm64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "freebsd"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/freebsd-x64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "freebsd"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-arm": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-arm64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-ia32": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ia32"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-loong64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "loong64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-mips64el": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "mips64el"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-ppc64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ppc64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-riscv64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "riscv64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-s390x": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "s390x"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-x64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/netbsd-arm64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "netbsd"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/netbsd-x64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "netbsd"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/openbsd-arm64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "openbsd"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/openbsd-x64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "openbsd"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/openharmony-arm64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "openharmony"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/sunos-x64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "sunos"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/win32-arm64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "win32"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/win32-ia32": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ia32"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "win32"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/win32-x64": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "optional": true,
      "os": [
        "win32"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@img/colour": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
      "integrity": [REDACTED_SECRET],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@img/sharp-darwin-arm64": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "optional": true,
      "os": [
        "darwin"
      ],
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      },
      "optionalDependencies": {
        "@img/sharp-libvips-darwin-arm64": "1.2.4"
      }
    },
    "node_modules/@img/sharp-darwin-x64": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "optional": true,
      "os": [
        "darwin"
      ],
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      },
      "optionalDependencies": {
        "@img/sharp-libvips-darwin-x64": "1.2.4"
      }
    },
    "node_modules/@img/sharp-libvips-darwin-arm64": {
      "version": "1.2.4",
      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "optional": true,
      "os": [
        "darwin"
      ],
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@img/sharp-libvips-darwin-x64": {
      "version": "1.2.4",
      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "optional": true,
      "os": [
        "darwin"
      ],
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@img/sharp-libvips-linux-arm": {
      "version": "1.2.4",
      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@img/sharp-libvips-linux-arm64": {
      "version": "1.2.4",
      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@img/sharp-libvips-linux-ppc64": {
      "version": "1.2.4",
      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ppc64"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@img/sharp-libvips-linux-riscv64": {
      "version": "1.2.4",
      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "riscv64"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@img/sharp-libvips-linux-s390x": {
      "version": "1.2.4",
      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "s390x"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@img/sharp-libvips-linux-x64": {
      "version": "1.2.4",
      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
      "version": "1.2.4",
      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@img/sharp-libvips-linuxmusl-x64": {
      "version": "1.2.4",
      "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@img/sharp-linux-arm": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      },
      "optionalDependencies": {
        "@img/sharp-libvips-linux-arm": "1.2.4"
      }
    },
    "node_modules/@img/sharp-linux-arm64": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      },
      "optionalDependencies": {
        "@img/sharp-libvips-linux-arm64": "1.2.4"
      }
    },
    "node_modules/@img/sharp-linux-ppc64": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ppc64"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      },
      "optionalDependencies": {
        "@img/sharp-libvips-linux-ppc64": "1.2.4"
      }
    },
    "node_modules/@img/sharp-linux-riscv64": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "riscv64"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      },
      "optionalDependencies": {
        "@img/sharp-libvips-linux-riscv64": "1.2.4"
      }
    },
    "node_modules/@img/sharp-linux-s390x": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "s390x"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      },
      "optionalDependencies": {
        "@img/sharp-libvips-linux-s390x": "1.2.4"
      }
    },
    "node_modules/@img/sharp-linux-x64": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      },
      "optionalDependencies": {
        "@img/sharp-libvips-linux-x64": "1.2.4"
      }
    },
    "node_modules/@img/sharp-linuxmusl-arm64": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      },
      "optionalDependencies": {
        "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
      }
    },
    "node_modules/@img/sharp-linuxmusl-x64": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      },
      "optionalDependencies": {
        "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
      }
    },
    "node_modules/@img/sharp-wasm32": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "wasm32"
      ],
      "optional": true,
      "dependencies": {
        "@emnapi/runtime": "^1.7.0"
      },
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@img/sharp-win32-arm64": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "optional": true,
      "os": [
        "win32"
      ],
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@img/sharp-win32-ia32": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ia32"
      ],
      "optional": true,
      "os": [
        "win32"
      ],
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@img/sharp-win32-x64": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "optional": true,
      "os": [
        "win32"
      ],
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      }
    },
    "node_modules/@types/node": {
      "version": "20.19.37",
      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "dependencies": {
        "undici-types": "~6.21.0"
      }
    },
    "node_modules/beautiful-mermaid": {
      "version": "1.1.3",
      "resolved": "https://registry.npmjs.org/beautiful-mermaid/-/beautiful-mermaid-1.1.3.tgz",
      "integrity": [REDACTED_SECRET],
      "dependencies": {
        "elkjs": "^0.11.0",
        "entities": "^7.0.1"
      }
    },
    "node_modules/detect-libc": {
      "version": "2.1.2",
      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
      "integrity": [REDACTED_SECRET],
      "engines": {
        "node": ">=8"
      }
    },
    "node_modules/elkjs": {
      "version": "0.11.1",
      "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.11.1.tgz",
      "integrity": [REDACTED_SECRET]
    },
    "node_modules/entities": {
      "version": "7.0.1",
      "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
      "integrity": [REDACTED_SECRET],
      "engines": {
        "node": ">=0.12"
      },
      "funding": {
        "url": "https://github.com/fb55/entities?sponsor=1"
      }
    },
    "node_modules/esbuild": {
      "version": "0.27.4",
      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "hasInstallScript": true,
      "bin": {
        "esbuild": "bin/esbuild"
      },
      "engines": {
        "node": ">=18"
      },
      "optionalDependencies": {
        "@esbuild/aix-ppc64": "0.27.4",
        "@esbuild/android-arm": "0.27.4",
        "@esbuild/android-arm64": "0.27.4",
        "@esbuild/android-x64": "0.27.4",
        "@esbuild/darwin-arm64": "0.27.4",
        "@esbuild/darwin-x64": "0.27.4",
        "@esbuild/freebsd-arm64": "0.27.4",
        "@esbuild/freebsd-x64": "0.27.4",
        "@esbuild/linux-arm": "0.27.4",
        "@esbuild/linux-arm64": "0.27.4",
        "@esbuild/linux-ia32": "0.27.4",
        "@esbuild/linux-loong64": "0.27.4",
        "@esbuild/linux-mips64el": "0.27.4",
        "@esbuild/linux-ppc64": "0.27.4",
        "@esbuild/linux-riscv64": "0.27.4",
        "@esbuild/linux-s390x": "0.27.4",
        "@esbuild/linux-x64": "0.27.4",
        "@esbuild/netbsd-arm64": "0.27.4",
        "@esbuild/netbsd-x64": "0.27.4",
        "@esbuild/openbsd-arm64": "0.27.4",
        "@esbuild/openbsd-x64": "0.27.4",
        "@esbuild/openharmony-arm64": "0.27.4",
        "@esbuild/sunos-x64": "0.27.4",
        "@esbuild/win32-arm64": "0.27.4",
        "@esbuild/win32-ia32": "0.27.4",
        "@esbuild/win32-x64": "0.27.4"
      }
    },
    "node_modules/semver": {
      "version": "7.7.4",
      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
      "integrity": [REDACTED_SECRET],
      "bin": {
        "semver": "bin/semver.js"
      },
      "engines": {
        "node": ">=10"
      }
    },
    "node_modules/sharp": {
      "version": "0.34.5",
      "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
      "integrity": [REDACTED_SECRET],
      "hasInstallScript": true,
      "dependencies": {
        "@img/colour": "^1.0.0",
        "detect-libc": "^2.1.2",
        "semver": "^7.7.3"
      },
      "engines": {
        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/libvips"
      },
      "optionalDependencies": {
        "@img/sharp-darwin-arm64": "0.34.5",
        "@img/sharp-darwin-x64": "0.34.5",
        "@img/sharp-libvips-darwin-arm64": "1.2.4",
        "@img/sharp-libvips-darwin-x64": "1.2.4",
        "@img/sharp-libvips-linux-arm": "1.2.4",
        "@img/sharp-libvips-linux-arm64": "1.2.4",
        "@img/sharp-libvips-linux-ppc64": "1.2.4",
        "@img/sharp-libvips-linux-riscv64": "1.2.4",
        "@img/sharp-libvips-linux-s390x": "1.2.4",
        "@img/sharp-libvips-linux-x64": "1.2.4",
        "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
        "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
        "@img/sharp-linux-arm": "0.34.5",
        "@img/sharp-linux-arm64": "0.34.5",
        "@img/sharp-linux-ppc64": "0.34.5",
        "@img/sharp-linux-riscv64": "0.34.5",
        "@img/sharp-linux-s390x": "0.34.5",
        "@img/sharp-linux-x64": "0.34.5",
        "@img/sharp-linuxmusl-arm64": "0.34.5",
        "@img/sharp-linuxmusl-x64": "0.34.5",
        "@img/sharp-wasm32": "0.34.5",
        "@img/sharp-win32-arm64": "0.34.5",
        "@img/sharp-win32-ia32": "0.34.5",
        "@img/sharp-win32-x64": "0.34.5"
      }
    },
    "node_modules/tslib": {
      "version": "2.8.1",
      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
      "integrity": [REDACTED_SECRET],
      "optional": true
    },
    "node_modules/undici-types": {
      "version": "6.21.0",
      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true
    }
  }
}


path: package.json
content: {
  "name": "beautiful-mermaid-skill",
  "version": "1.0.0",
  "description": "CodeBuddy Skill for rendering beautiful Mermaid diagrams to SVG or ASCII art",
  "type": "commonjs",
  "scripts": {
    "render": "node scripts/render.js",
    "render:svg": "node scripts/render.js --format svg",
    "render:ascii": "node scripts/render.js --format ascii",
    "render:png": "node scripts/render.js --format png",
    "rich-html": "node scripts/rich-html.js"
  },
  "dependencies": {
    "beautiful-mermaid": "^1.1.3",
    "sharp": "^0.34.5"
  },
  "devDependencies": {
    "@types/node": "^20.10.0",
    "esbuild": "^0.27.4"
  },
  "keywords": [
    "mermaid",
    "diagram",
    "svg",
    "ascii",
    "flowchart",
    "sequence-diagram",
    "class-diagram",
    "er-diagram",
    "state-diagram",
    "codebuddy-skill"
  ],
  "license": "MIT",
  "author": {
    "name": "chouray",
    "url": "https://github.com/chouraycn"
  },
  "engines": {
    "node": ">=16.0.0"
  }
}


path: scripts/render.js
content: #!/usr/bin/env node

/**
 * Beautiful Mermaid CLI Renderer
 * 
 * 用法:
 *   node scripts/render.js <input.mmd> [options]
 *   node scripts/render.js --code "graph TD\nA --> B" [options]
 *   node scripts/render.js --batch <dir> [options]
 * 
 * 选项:
 *   --format, -f     输出格式: svg (默认) | ascii | png
 *   --theme, -t      主题名称或自定义主题JSON
 *   --output, -o     输出文件路径
 *   --code, -c       直接传入 Mermaid 代码
 *   --bg             背景色 (Mono模式)
 *   --fg             前景色 (Mono模式)
 *   --line           连线颜色
 *   --preset, -p     样式预设: default | modern | gradient | outline | glass
 *   --width, -w      PNG 输出宽度 (默认: 1200)
 *   --scale, -s      PNG 缩放比例 (默认: 1, 范围: 0.5-4)
 *   --dpi            PNG 输出 DPI (默认: 144, 范围: 72-600)
 *   --interactive    启用交互式 tooltip (仅 XY 图表)
 *   --batch          批量模式: 渲染目录下所有 .mmd 文件
 *   --color-mode     ASCII 颜色模式: none | auto | ansi16 | ansi256 | truecolor (默认) | html
 *   --help, -h       显示帮助
 */

const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');

// sharp 仅在 PNG 渲染时按需加载，避免非 PNG 场景因 sharp 安装问题报错
let _sharp = null;
function getSharp() {
  if (!_sharp) _sharp = require('sharp');
  return _sharp;
}

// 从 styles.js 导入共享数据（唯一数据源）
const {
  STYLE_PRESETS,
  THEMES: LOCAL_THEMES,
  THEME_DEFAULTS,
  THEME_META,
  injectStylesToSVG,
  isValidPreset,
  isValidTheme,
  getTheme,
  getThemeMeta,
  getRecommendedPreset,
  resolveTheme,
  getDarkThemes,
  getLightThemes,
} = require('./styles');

// 解析命令行参数
function parseArgs() {
  const args = process.argv.slice(2);
  const options = {
    format: 'svg',
    theme: 'github-light',
    output: null,
    code: null,
    bg: null,
    fg: null,
    line: null,
    preset: null,
    width: 1200,
    scale: 1,
    dpi: 144,
    interactive: false,
    colorMode: 'truecolor',
    batch: false,
    input: null
  };

  for (let i = 0; i < args.length; i++) {
    const arg = args[i];
    switch (arg) {
      case '--format':
      case '-f':
        options.format = args[++i];
        break;
      case '--theme':
      case '-t':
        options.theme = args[++i];
        break;
      case '--output':
      case '-o':
        options.output = args[++i];
        break;
      case '--code':
      case '-c':
        options.code = args[++i];
        break;
      case '--bg':
        options.bg = args[++i];
        break;
      case '--fg':
        options.fg = args[++i];
        break;
      case '--line':
        options.line = args[++i];
        break;
      case '--preset':
      case '-p':
        options.preset = args[++i];
        break;
      case '--width':
      case '-w':
        options.width = parseInt(args[++i], 10);
        break;
      case '--scale':
      case '-s':
        options.scale = parseFloat(args[++i]);
        break;
      case '--dpi':
        options.dpi = parseInt(args[++i], 10);
        break;
      case '--interactive':
        options.interactive = true;
        break;
      case '--color-mode':
        options.colorMode = args[++i];
        break;
      case '--batch':
        options.batch = true;
        break;
      case '--list-themes':
        options.listThemes = true;
        break;
      case '--help':
      case '-h':
        showHelp();
        process.exit(0);
        break;
      default:
        if (!arg.startsWith('-') && !options.input) {
          options.input = arg;
        }
        break;
    }
  }

  return options;
}

function showHelp() {
  console.log(`
Beautiful Mermaid CLI Renderer
================================

用法:
  node scripts/render.js <input.mmd> [options]
  node scripts/render.js --code "graph TD\\nA --> B" [options]
  node scripts/render.js --batch <dir> [options]

选项:
  --format, -f     输出格式: svg (默认) | ascii | png
  --theme, -t      主题名称 (tokyo-night, dracula, github-dark 等)
                   或自定义主题JSON: '{"bg":"#...","fg":"#..."}'
  --output, -o     输出文件路径 (默认: input.svg 或 output.png)
  --code, -c       直接传入 Mermaid 代码
  --bg             背景色 (如: #f7f7fa)
  --fg             前景色/文字颜色 (如: #27272a)
  --line           连线颜色 (如: #6b7280)
  --preset, -p     样式预设: default | modern | gradient | outline | glass
                   (与 preview.html 中的样式预设一致)
  --width, -w      PNG 输出宽度 (默认: 1200px)
  --scale, -s      PNG 缩放比例 (默认: 1, 范围: 0.5-4)
  --dpi            PNG 输出 DPI (默认: 144, 范围: 72-600)
  --interactive    启用交互式 tooltip (仅 XY 图表)
  --color-mode     ASCII 颜色模式: none | auto | ansi16 | ansi256 | truecolor | html
  --batch          批量模式: 渲染目录下所有 .mmd 文件
  --help, -h       显示帮助

可用主题:
  github-light        GitHub 亮色 (默认)
  tokyo-night-storm    东京之夜·暴风雨
  tokyo-night-light    东京之夜·亮色
  dracula             德古拉红
  github-dark         GitHub 暗色
  github-light        GitHub 亮色
  nord                Nord 蓝
  nord-light         Nord 亮色
  one-dark            One Dark
  catppuccin-mocha    Catppuccin Mocha
  catppuccin-latte    Catppuccin Latte
  solarized-dark      Solarized 暗色
  solarized-light     Solarized 亮色
  zinc-light          Zinc 亮色
  zinc-dark           Zinc 暗色

样式预设 (--preset):
  default     默认样式 (圆角8px, 边框2px, 阴影)
  modern      现代简约 (圆角16px, 边框1px, 柔和阴影)
  gradient    渐变风格 (圆角12px, 无边框, 彩色阴影)
  outline     线条轮廓 (圆角4px, 边框2px, 无阴影)
  glass       毛玻璃 (圆角12px, 边框1px, 高模糊阴影)

示例:
  # 从文件渲染SVG (使用默认主题)
  node scripts/render.js diagram.mmd -o output.svg

  # 使用特定主题渲染
  node scripts/render.js diagram.mmd -t dracula -o output.svg

  # 自定义颜色 + 样式预设 (与 preview.html 一致)
  node scripts/render.js diagram.mmd --bg '#f7f7fa' --fg '#27272a' --line '#6b7280' -p modern -o output.svg

  # 渲染为PNG (高清位图)
  node scripts/render.js diagram.mmd -f png -o output.png

  # PNG 自定义尺寸
  node scripts/render.js diagram.mmd -f png -w 2400 -o output.png

  # PNG 高清缩放 (2x)
  node scripts/render.js diagram.mmd -f png -s 2 -o output.png

  # PNG 高 DPI (印刷质量)
  node scripts/render.js diagram.mmd -f png --dpi 300 -o output.png

  # 渲染为ASCII (终端友好)
  node scripts/render.js diagram.mmd -f ascii

  # ASCII 自定义颜色模式
  node scripts/render.js diagram.mmd -f ascii --color-mode ansi256

  # XY 图表启用交互式 tooltip
  node scripts/render.js chart.mmd --interactive -o chart.svg

  # 直接传入代码 (注意：使用 \\n 表示换行)
  node scripts/render.js -c "graph TD\\nA[开始] --> B[结束]" -f ascii
  node scripts/render.js -c "graph TD\\nA[开始] --> B[结束]" -o output.svg -t github-dark

  # 批量渲染目录下所有 .mmd 文件
  node scripts/render.js --batch ./diagrams -f svg -t dracula

  # 批量渲染到指定输出目录
  node scripts/render.js --batch ./diagrams -f svg -o ./output
`);
}

async function main() {
  const options = parseArgs();

  // --list-themes：输出所有主题详情
  if (options.listThemes) {
    console.log('\n可用主题 (--theme 参数):\n');
    const dark  = getDarkThemes();
    const light = getLightThemes();
    console.log('  ● 暗色主题 (Dark):');
    dark.forEach(n => {
      const meta = getThemeMeta(n);
      console.log(`    ${n.padEnd(25)} → 推荐预设: ${meta.recommendedPreset}`);
    });
    console.log('\n  ● 亮色主题 (Light):');
    light.forEach(n => {
      const meta = getThemeMeta(n);
      console.log(`    ${n.padEnd(25)} → 推荐预设: ${meta.recommendedPreset}`);
    });
    console.log('');
    process.exit(0);
  }

  // 动态导入 beautiful-mermaid (ESM)
  const { renderMermaidSVG, renderMermaidASCII, THEMES } = await import('beautiful-mermaid');

  // 批量模式
  if (options.batch) {
    if (!options.input) {
      console.error('错误: --batch 模式需要指定目录路径');
      showHelp();
      process.exit(1);
    }
    if (!fs.existsSync(options.input)) {
      console.error(`错误: 目录不存在 ${options.input}`);
      process.exit(1);
    }
    const stat = fs.statSync(options.input);
    if (!stat.isDirectory()) {
      console.error(`错误: ${options.input} 不是目录`);
      process.exit(1);
    }

    // 收集 .mmd 文件
    const mmdFiles = fs.readdirSync(options.input)
      .filter(f => f.endsWith('.mmd'))
      .sort();

    if (mmdFiles.length === 0) {
      console.error('错误: 目录中没有 .mmd 文件');
      process.exit(1);
    }

    // 确定输出目录
    const outputDir = options.output || options.input;
    if (!fs.existsSync(outputDir)) {
      fs.mkdirSync(outputDir, { recursive: true });
    }

    const ext = options.format === 'ascii' ? 'txt' : options.format === 'png' ? 'png' : 'svg';
    let success = 0;
    let failed = 0;

    console.log(`批量渲染: ${mmdFiles.length} 个 .mmd 文件 → ${outputDir}/ (${options.format})\n`);

    for (const file of mmdFiles) {
      const inputPath = path.join(options.input, file);
      const baseName = file.replace(/\.mmd$/, '');
      const outputPath = path.join(outputDir, `${baseName}.${ext}`);
      try {
        await renderSingleFile(inputPath, outputPath, options, { renderMermaidSVG, renderMermaidASCII, THEMES });
        success++;
      } catch (e) {
        failed++;
        console.error(`  ✗ ${file}: ${e.message}`);
      }
    }

    console.log(`\n完成: ${success} 成功, ${failed} 失败`);
    return;
  }

  // 单文件模式
  let code;
  if (options.code) {
    code = options.code;
  } else if (options.input) {
    if (!fs.existsSync(options.input)) {
      console.error(`错误: 文件不存在 ${options.input}`);
      process.exit(1);
    }
    code = fs.readFileSync(options.input, 'utf-8');
  } else {
    console.error('错误: 请提供输入文件或使用 --code 传入代码');
    showHelp();
    process.exit(1);
  }

  // 确定默认输出路径
  const ext = options.format === 'ascii' ? 'txt' : options.format === 'png' ? 'png' : 'svg';
  const defaultOutput = options.input
    ? options.input.replace(/\.mmd$/, `.${ext}`)
    : `output.${ext}`;
  const outputPath = options.output || defaultOutput;

  // 确保输出目录存在
  const outputDir = path.dirname(outputPath);
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  try {
    await renderSingleCode(code, outputPath, options, { renderMermaidSVG, renderMermaidASCII, THEMES });
  } catch (error) {
    console.error('渲染失败:', error.message);
    if (error.stack) {
      console.error(error.stack);
    }
    process.exit(1);
  }
}

// 渲染单个文件
async function renderSingleFile(inputPath, outputPath, options, lib) {
  const code = fs.readFileSync(inputPath, 'utf-8');
  await renderSingleCode(code, outputPath, options, lib);
}

// 渲染核心逻辑（从代码字符串到输出）
async function renderSingleCode(code, outputPath, options, { renderMermaidSVG, renderMermaidASCII, THEMES }) {
  // 解析主题
  // 优先级: CLI --bg/--fg > JSON字符串 > 本地 LOCAL_THEMES 名称
  // 解析后通过 resolveTheme() 补全全部 7 个字段（bg/fg/line/accent/muted/surface/border）
  let rawTheme;
  if (options.bg && options.fg) {
    rawTheme = { bg: options.bg, fg: options.fg, line: options.line };
  } else {
    try {
      rawTheme = JSON.parse(options.theme);
    } catch {
      rawTheme = options.theme; // 字符串名称，交给 resolveTheme 解析
    }
  }
  // resolveTheme 将名称 → 完整对象，并补全缺省字段
  const theme = resolveTheme(rawTheme);

  // 验证 preset 参数（不再与 --theme 互斥，任何主题都可搭配任意 preset）
  if (options.preset && !isValidPreset(options.preset)) {
    throw new Error(`未知样式预设 '${options.preset}'，可用: ${Object.keys(STYLE_PRESETS).join(', ')}`);
  }

  // 如果没有指定 preset，尝试用主题的推荐预设（仅当 --theme 是已知名称时）
  const effectivePreset = options.preset ||
    (typeof rawTheme === 'string' ? getRecommendedPreset(rawTheme) : null);

  // 验证 colorMode
  const validColorModes = ['none', 'auto', 'ansi16', 'ansi256', 'truecolor', 'html'];
  if (!validColorModes.includes(options.colorMode)) {
    console.warn(`警告: 未知颜色模式 '${options.colorMode}'，使用默认 'truecolor'`);
    options.colorMode = 'truecolor';
  }

  if (options.format === 'ascii') {
    const output = renderMermaidASCII(code, {
      colorMode: options.colorMode,
    });

    if (outputPath) {
      fs.writeFileSync(outputPath, output);
      console.log(`✓ ASCII 已保存: ${outputPath}`);
    } else {
      console.log(output);
    }
  } else if (options.format === 'png') {
    // theme 已经是完整对象（经过 resolveTheme 补全），直接传给库
    const renderOptions = { ...theme, interactive: options.interactive };
    let svgOutput = renderMermaidSVG(code, renderOptions);

    if (effectivePreset) {
      svgOutput = injectStylesToSVG(svgOutput, theme, effectivePreset);
    }

    const scale = Math.max(0.5, Math.min(4, options.scale));
    const dpi = Math.max(72, Math.min(600, options.dpi));
    const outputWidth = Math.round(options.width * scale);

    const pngBuffer = await getSharp()(Buffer.from(svgOutput))
      .resize(outputWidth, null, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } })
      .png({ quality: 100, density: dpi })
      .toBuffer();

    fs.writeFileSync(outputPath, pngBuffer);
    const presetInfo = effectivePreset ? `, preset:${effectivePreset}` : '';
    console.log(`✓ PNG 已保存: ${outputPath} (${outputWidth}px, scale:${scale}, dpi:${dpi}${presetInfo})`);
  } else {
    const renderOptions = { ...theme, interactive: options.interactive };
    let output = renderMermaidSVG(code, renderOptions);

    if (effectivePreset) {
      output = injectStylesToSVG(output, theme, effectivePreset);
    }

    fs.writeFileSync(outputPath, output);
    const presetInfo = effectivePreset ? ` + preset:${effectivePreset}` : '';
    console.log(`✓ SVG 已保存: ${outputPath}${presetInfo}`);
  }
}

main();


path: scripts/styles.js
content: /**
 * Beautiful Mermaid Style Presets
 *
 * 提供预设样式和 CSS 样式生成功能。
 * 这是唯一的预设数据源 —— render.js 和 preview.html 均从此模块导入。
 *
 * ─────────────────────────────────────────────
 * 同步自上游 src/styles.ts（lukilabs/beautiful-mermaid）
 *   - MONO_FONT / MONO_FONT_STACK        等宽字体栈
 *   - FONT_SIZES / FONT_WEIGHTS          字号与字重
 *   - NODE_PADDING                       节点内边距
 *   - STROKE_WIDTHS                      描边宽度
 *   - TEXT_BASELINE_SHIFT                文本基线偏移
 *   - ARROW_HEAD                         箭头尺寸
 *   - GROUP_HEADER_CONTENT_PAD           子图标题间距
 * ─────────────────────────────────────────────
 */

// ─────────────────────────────────────────────────────────────────────────────
// §1  字体度量常量（源自 src/styles.ts）
// ─────────────────────────────────────────────────────────────────────────────

/** 等宽字体主字体（JetBrains Mono）*/
const MONO_FONT = "'JetBrains Mono'";

/** 完整等宽字体降级栈 */
const MONO_FONT_STACK = `${MONO_FONT}, 'SF Mono', 'Fira Code', ui-monospace, monospace`;

/** 等宽字体文本宽度估算（字符数 × 字号 × 0.6） */
function estimateMonoTextWidth(text, fontSize) {
  return text.length * fontSize * 0.6;
}

// ─────────────────────────────────────────────────────────────────────────────
// §2  字号与字重（源自 src/styles.ts）
// ─────────────────────────────────────────────────────────────────────────────

const FONT_SIZES = {
  nodeLabel:   13,   // 节点标签文本
  edgeLabel:   11,   // 边标签文本
  groupHeader: 12,   // 子图标题文本
};

const FONT_WEIGHTS = {
  nodeLabel:   500,  // 节点标签字重
  edgeLabel:   400,  // 边标签字重
  groupHeader: 600,  // 子图标题字重
};

// ─────────────────────────────────────────────────────────────────────────────
// §3  几何参数（源自 src/styles.ts）
// ─────────────────────────────────────────────────────────────────────────────

/** 子图标题带与内容区域之间的垂直间隙 */
const GROUP_HEADER_CONTENT_PAD = 12;

/** 节点形状内边距 */
const NODE_PADDING = {
  horizontal:   20,  // 水平内边距
  vertical:     10,  // 垂直内边距
  diamondExtra: 24,  // 菱形额外内边距
};

/** 描边宽度 */
const STROKE_WIDTHS = {
  outerBox:  1,      // 外框描边
  innerBox:  0.75,   // 内框描边
  connector: 1,      // 连接器描边
};

/** 文本基线偏移量（用于字体无关的垂直居中） */
const TEXT_BASELINE_SHIFT = '0.35em';

/** 箭头尺寸 */
const ARROW_HEAD = {
  width:  8,   // 箭头宽度
  height: 5,   // 箭头高度
};

// ─────────────────────────────────────────────────────────────────────────────
// §4  主题定义（源自 src/theme.ts，共 15 个）
// ─────────────────────────────────────────────────────────────────────────────
// 每个主题包含完整 7 字段：
//   bg       背景色
//   fg       前景/主文字色
//   line     连线/边颜色
//   accent   强调色（箭头、高亮、子图标题）
//   muted    次要文字色（边标签、辅助信息）
//   surface  节点填充次级色（比 bg 深/浅一级，用于节点内部）
//   border   节点边框精确色
//
// 使用方式：THEMES['tokyo-night'] → { bg, fg, line, accent, muted, surface, border }

const THEMES = {
  // ── Zinc（极简黑白系） ──────────────────────────────────────────────────────
  'zinc-light': {
    bg:      '#FFFFFF',
    fg:      '#27272A',
    line:    '#A1A1AA',   // zinc-400
    accent:  '#3F3F46',   // zinc-700
    muted:   '#71717A',   // zinc-500
    surface: '#F4F4F5',   // zinc-100
    border:  '#D4D4D8',   // zinc-300
  },
  'zinc-dark': {
    bg:      '#18181B',
    fg:      '#FAFAFA',
    line:    '#52525B',   // zinc-600
    accent:  '#A1A1AA',   // zinc-400
    muted:   '#71717A',   // zinc-500
    surface: '#27272A',   // zinc-800
    border:  '#3F3F46',   // zinc-700
  },

  // ── Tokyo Night（夜间代码主题） ────────────────────────────────────────────
  'tokyo-night': {
    bg:      '#1a1b26',
    fg:      '#a9b1d6',
    line:    '#3d59a1',
    accent:  '#7aa2f7',
    muted:   '#565f89',
    surface: '#24283b',   // storm 背景色作为节点 surface
    border:  '#414868',   // 暗紫灰
  },
  'tokyo-night-storm': {
    bg:      '#24283b',
    fg:      '#a9b1d6',
    line:    '#3d59a1',
    accent:  '#7aa2f7',
    muted:   '#565f89',
    surface: '#2f3549',   // 比 bg 稍亮
    border:  '#414868',
  },
  'tokyo-night-light': {
    bg:      '#d5d6db',
    fg:      '#343b58',
    line:    '#34548a',
    accent:  '#34548a',
    muted:   '#9699a3',
    surface: '#e9e9ec',   // 比 bg 稍亮
    border:  '#b9bac4',
  },

  // ── Catppuccin（柔和莫卡系） ───────────────────────────────────────────────
  'catppuccin-mocha': {
    bg:      '#1e1e2e',
    fg:      '#cdd6f4',
    line:    '#585b70',
    accent:  '#cba6f7',   // mauve
    muted:   '#6c7086',
    surface: '#313244',   // surface0
    border:  '#45475a',   // surface1
  },
  'catppuccin-latte': {
    bg:      '#eff1f5',
    fg:      '#4c4f69',
    line:    '#9ca0b0',
    accent:  '#8839ef',   // mauve
    muted:   '#9ca0b0',
    surface: '#e6e9ef',   // mantle
    border:  '#ccd0da',   // surface0
  },

  // ── Nord（北欧极光） ────────────────────────────────────────────────────────
  'nord': {
    bg:      '#2e3440',
    fg:      '#d8dee9',
    line:    '#4c566a',
    accent:  '#88c0d0',   // nord8（冰蓝）
    muted:   '#616e88',
    surface: '#3b4252',   // nord1
    border:  '#434c5e',   // nord2
  },
  'nord-light': {
    bg:      '#eceff4',
    fg:      '#2e3440',
    line:    '#aab1c0',
    accent:  '#5e81ac',   // nord10（深蓝）
    muted:   '#7b88a1',
    surface: '#e5e9f0',   // nord5
    border:  '#d8dee9',   // nord4
  },

  // ── Dracula（经典德古拉） ──────────────────────────────────────────────────
  'dracula': {
    bg:      '#282a36',
    fg:      '#f8f8f2',
    line:    '#6272a4',
    accent:  '#bd93f9',   // purple
    muted:   '#6272a4',
    surface: '#44475a',   // current line / selection
    border:  '#6272a4',   // comment 色
  },

  // ── GitHub（官方 IDE 主题） ────────────────────────────────────────────────
  'github-light': {
    bg:      '#ffffff',
    fg:      '#1f2328',
    line:    '#d1d9e0',
    accent:  '#0969da',   // blue-500
    muted:   '#59636e',
    surface: '#f6f8fa',   // neutral-1
    border:  '#d1d9e0',   // border-default
  },
  'github-dark': {
    bg:      '#0d1117',
    fg:      '#e6edf3',
    line:    '#3d444d',
    accent:  '#4493f8',   // blue-400
    muted:   '#9198a1',
    surface: '#161b22',   // canvas-subtle
    border:  '#30363d',   // border-default
  },

  // ── Solarized（精准色温校准） ──────────────────────────────────────────────
  'solarized-light': {
    bg:      '#fdf6e3',
    fg:      '#657b83',
    line:    '#93a1a1',
    accent:  '#268bd2',   // blue
    muted:   '#93a1a1',
    surface: '#eee8d5',   // base2
    border:  '#d3ccb4',   // 介于 base2/base1 之间
  },
  'solarized-dark': {
    bg:      '#002b36',
    fg:      '#839496',
    line:    '#586e75',
    accent:  '#268bd2',   // blue
    muted:   '#586e75',
    surface: '#073642',   // base02
    border:  '#0a4555',   // 稍亮于 surface
  },

  // ── One Dark（Atom 编辑器经典） ────────────────────────────────────────────
  'one-dark': {
    bg:      '#282c34',
    fg:      '#abb2bf',
    line:    '#4b5263',
    accent:  '#c678dd',   // purple
    muted:   '#5c6370',
    surface: '#2c313a',   // 比 bg 略亮
    border:  '#3e4451',   // 背景高亮行
  },
  // 橙色系
  'orange-dark': {
    bg:      '#1c1410',   // 深棕黑
    fg:      '#f5e6d3',   // 暖白
    line:    '#6b4a2e',   // 深橙棕
    accent:  '#f97316',   // 橙色主色 orange-500
    muted:   '#9a6a4a',   // 中性橙棕
    surface: '#2a1e15',   // 比 bg 略亮
    border:  '#3d2b1c',   // 橙棕边框
  },
  'orange-light': {
    bg:      '#fffbf5',   // 暖白底
    fg:      '#431407',   // 深橙棕文字
    line:    '#c2a070',   // 浅橙棕连线
    accent:  '#ea580c',   // 橙色主色 orange-600
    muted:   '#9a6a4a',   // 中性橙棕
    surface: '#fff7ed',   // 浅橙面板背景
    border:  '#fed7aa',   // 浅橙边框 orange-200
  },
};

/** 未指定主题时的默认颜色 */
const THEME_DEFAULTS = {
  bg:      '#FFFFFF',
  fg:      '#27272A',
  line:    '#A1A1AA',
  accent:  '#3F3F46',
  muted:   '#71717A',
  surface: '#F4F4F5',
  border:  '#D4D4D8',
};

// ─────────────────────────────────────────────────────────────────────────────
// §4a  主题元数据（分组、亮暗、推荐预设）
// ─────────────────────────────────────────────────────────────────────────────

/**
 * 主题元数据：提供 dark/light 分类、显示名称、推荐搭配预设
 *
 * recommendedPreset: 该主题搭配哪种样式预设效果最佳
 *   - dark 主题 → glass（深色毛玻璃效果佳）或 modern
 *   - light 主题 → modern 或 outline
 */
const THEME_META = {
  'zinc-light':          { dark: false, label: 'Zinc Light',         family: 'zinc',        recommendedPreset: 'outline'  },
  'zinc-dark':           { dark: true,  label: 'Zinc Dark',          family: 'zinc',        recommendedPreset: 'glass'    },
  'tokyo-night':         { dark: true,  label: 'Tokyo Night',        family: 'tokyo-night', recommendedPreset: 'glass'    },
  'tokyo-night-storm':   { dark: true,  label: 'Tokyo Storm',        family: 'tokyo-night', recommendedPreset: 'modern'   },
  'tokyo-night-light':   { dark: false, label: 'Tokyo Light',        family: 'tokyo-night', recommendedPreset: 'modern'   },
  'catppuccin-mocha':    { dark: true,  label: 'Catppuccin Mocha',   family: 'catppuccin',  recommendedPreset: 'glass'    },
  'catppuccin-latte':    { dark: false, label: 'Catppuccin Latte',   family: 'catppuccin',  recommendedPreset: 'modern'   },
  'nord':                { dark: true,  label: 'Nord',               family: 'nord',        recommendedPreset: 'modern'   },
  'nord-light':          { dark: false, label: 'Nord Light',         family: 'nord',        recommendedPreset: 'outline'  },
  'dracula':             { dark: true,  label: 'Dracula',            family: 'dracula',     recommendedPreset: 'gradient' },
  'github-light':        { dark: false, label: 'GitHub Light',       family: 'github',      recommendedPreset: 'default'  },
  'github-dark':         { dark: true,  label: 'GitHub Dark',        family: 'github',      recommendedPreset: 'modern'   },
  'solarized-light':     { dark: false, label: 'Solarized Light',    family: 'solarized',   recommendedPreset: 'outline'  },
  'solarized-dark':      { dark: true,  label: 'Solarized Dark',     family: 'solarized',   recommendedPreset: 'modern'   },
  'one-dark':            { dark: true,  label: 'One Dark',           family: 'one-dark',    recommendedPreset: 'gradient' },
  'orange-dark':         { dark: true,  label: 'Orange Dark',        family: 'orange',      recommendedPreset: 'glass'    },
  'orange-light':        { dark: false, label: 'Orange Light',       family: 'orange',      recommendedPreset: 'modern'   },
};

// ─────────────────────────────────────────────────────────────────────────────
// §5  视觉样式预设（Preset）—— 5 种
// ─────────────────────────────────────────────────────────────────────────────
// 预设控制形状几何（圆角、描边、阴影）和字体，与主题颜色正交组合。

const STYLE_PRESETS = {
  // 默认样式
  default: {
    name: '默认样式',
    node: {
      borderRadius: 8,
      borderWidth: STROKE_WIDTHS.outerBox * 2,
      shadowBlur: 4,
      shadowColor: 'rgba(0,0,0,0.3)',
    },
    line: {
      width: STROKE_WIDTHS.connector * 2,
      radius: 0,
      arrowSize: ARROW_HEAD.width + ARROW_HEAD.height,   // 13
    },
    font: {
      family: 'system-ui, -apple-system, sans-serif',
      size: FONT_SIZES.nodeLabel,
    },
  },

  // 现代简约
  modern: {
    name: '现代简约',
    node: {
      borderRadius: 16,
      borderWidth: STROKE_WIDTHS.outerBox,
      shadowBlur: 8,
      shadowColor: 'rgba(0,0,0,0.2)',
    },
    line: {
      width: 1.5,
      radius: 5,
      arrowSize: ARROW_HEAD.width + 2,   // 10
    },
    font: {
      family: "'Inter', 'PingFang SC', sans-serif",
      size: FONT_SIZES.nodeLabel,
    },
  },

  // 渐变风格
  gradient: {
    name: '渐变风格',
    node: {
      borderRadius: 12,
      borderWidth: 0,
      shadowBlur: 12,
      shadowColor: 'rgba(122,162,247,0.4)',
    },
    line: {
      width: STROKE_WIDTHS.connector * 2,
      radius: 8,
      arrowSize: ARROW_HEAD.width + ARROW_HEAD.height,
    },
    font: {
      family: "'SF Pro Display', sans-serif",
      size: FONT_SIZES.nodeLabel,
    },
  },

  // 线条轮廓
  outline: {
    name: '线条轮廓',
    node: {
      borderRadius: 4,
      borderWidth: STROKE_WIDTHS.outerBox * 2,
      shadowBlur: 0,
      shadowColor: 'transparent',
    },
    line: {
      width: STROKE_WIDTHS.connector * 2,
      radius: 0,
      arrowSize: ARROW_HEAD.width + ARROW_HEAD.height,
    },
    font: {
      family: MONO_FONT_STACK,
      size: FONT_SIZES.edgeLabel + 2,   // 13
    },
  },

  // 毛玻璃效果
  glass: {
    name: '毛玻璃',
    node: {
      borderRadius: 12,
      borderWidth: STROKE_WIDTHS.outerBox,
      shadowBlur: 16,
      shadowColor: 'rgba(255,255,255,0.1)',
    },
    line: {
      width: 1.5,
      radius: 4,
      arrowSize: ARROW_HEAD.width + 2,
    },
    font: {
      family: "'SF Pro Text', sans-serif",
      size: FONT_SIZES.nodeLabel,
    },
  },
};

// ─────────────────────────────────────────────────────────────────────────────
// §6  CSS 生成函数
// ─────────────────────────────────────────────────────────────────────────────

/**
 * 生成 CSS 样式字符串（Node.js 环境 —— 字符串拼接，无 DOM 依赖）
 * 用于 render.js 的 SVG 样式注入。
 *
 * @param {object} theme  - 主题对象，包含 bg / fg / line 等颜色字段
 * @param {string} preset - 预设名称（default / modern / gradient / outline / glass）
 * @param {string} [svgId] - SVG 根元素的唯一 id（如 'diagram-flow'）。
 *                           传入后所有选择器会被作用域化为 #svgId xxx，
 *                           防止 SVG 内联到 HTML 时跨图表 CSS 污染。
 *                           不传则使用 'svg' 作为根选择器（单独文件场景）。
 */
function generateCSSStyles(theme, preset = 'default', svgId = null) {
  const p = STYLE_PRESETS[preset] || STYLE_PRESETS.default;
  const lineColor = theme.line || theme.fg + '80';
  const borderColor = theme.muted || theme.fg + '40';

  // 根选择器：有 id 时用 #id，否则用 svg（保持向后兼容）
  const root = svgId ? `#${svgId}` : 'svg';

  return `
${root} {
  /* 覆盖 CSS 变量 */
  --_text:       ${theme.fg}       !important;
  --_text-sec:   ${theme.muted  || theme.fg + '80'} !important;
  --_line:       ${lineColor}      !important;
  --_arrow:      ${lineColor}      !important;
  --_node-fill:  ${theme.bg}       !important;
  --_node-stroke:${borderColor}    !important;
  --_group-fill: ${theme.accent ? theme.accent + '18' : theme.fg + '0a'} !important;
  --_group-hdr:  ${theme.accent || theme.fg} !important;
}
${root} text {
  font-family: ${p.font.family} !important;
  font-size:   ${p.font.size}px !important;
  dominant-baseline: central;
  dy: ${TEXT_BASELINE_SHIFT};
}
/* 直接覆盖元素属性 */
${root} rect[fill="var(--_node-fill)"],
${root} circle[fill="var(--_node-fill)"],
${root} ellipse[fill="var(--_node-fill)"],
${root} polygon[fill="var(--_node-fill)"] {
  fill:         ${theme.bg}        !important;
  stroke:       ${borderColor}     !important;
  stroke-width: ${p.node.borderWidth}px !important;
  rx:           ${p.node.borderRadius}px !important;
  ry:           ${p.node.borderRadius}px !important;
  filter: drop-shadow(0 ${p.node.shadowBlur / 2}px ${p.node.shadowBlur}px ${p.node.shadowColor}) !important;
}
/* 节点文字 */
${root} g.node text {
  fill: ${theme.fg} !important;
}
/* 子图/cluster 标题 */
${root} .cluster-label text {
  fill:        ${theme.accent || theme.fg} !important;
  font-size:   ${FONT_SIZES.groupHeader}px !important;
  font-weight: ${FONT_WEIGHTS.groupHeader} !important;
}
/* 边/连线 */
${root} .edge, ${root} .edgePath .path {
  stroke:       ${lineColor}  !important;
  stroke-width: ${p.line.width}px !important;
}
${root} path {
  stroke:       ${lineColor}  !important;
  stroke-width: ${p.line.width}px !important;
}
/* 箭头 */
${root} .arrowhead polygon,
${root} marker polygon {
  stroke: ${lineColor} !important;
  fill:   ${lineColor} !important;
}
/* 边标签 */
${root} .edgeLabel text {
  fill:      ${theme.muted || theme.fg} !important;
  font-size: ${FONT_SIZES.edgeLabel}px  !important;
}`;
}

// 自增计数器，用于生成唯一 SVG id（每次进程内递增）
let _svgIdCounter = 0;

/**
 * 将 CSS 样式注入 SVG（Node.js 版本，无需 DOMParser）
 * 用于 render.js 的 CLI 渲染流程。
 *
 * 除注入样式外，还会：
 * 1. 给 SVG 根元素注入唯一 id（svgId），防止内联到 HTML 时跨图表 CSS 污染
 * 2. 用 svgId 作用域化所有内部 <style> 选择器（svg {} → #id {}，text {} → #id text {} 等）
 * 3. 在 SVG 的 style 属性中补全 --_line / --_arrow 等映射变量，确保独立用软件打开 SVG 时线条有颜色
 * 4. 修复 SVG 的 width/height 属性：
 *    - 若 width 或 height 是百分比（如 "100%"），从 viewBox 推算实际像素值写回
 *    - 若没有 viewBox 但有具体 width/height，补全 viewBox
 *
 * @param {string}  svgString  - 原始 SVG 字符串
 * @param {object}  theme      - 完整主题对象（含 bg/fg/line/accent/muted/surface/border）
 * @param {string}  [preset]   - 样式预设名称，默认 'default'
 * @param {string}  [svgId]    - 可选：指定 SVG 根元素 id；不传则自动生成 bm-diagram-N
 */
function injectStylesToSVG(svgString, theme, preset = 'default', svgId = null) {
  // 自动生成唯一 id（若未指定）
  const effectiveSvgId = svgId || `bm-diagram-${++_svgIdCounter}`;

  const css = generateCSSStyles(theme, preset, effectiveSvgId);
  const styleTag = `<style>${css}</style>`;

  // ── Step 1：修复 SVG 的 width/height，注入唯一 id，补全下划线 CSS 变量 ──
  let fixed = svgString;

  // 提取 <svg ...> 开标签（包含所有属性）
  const svgTagMatch = fixed.match(/<svg([^>]*)>/);
  if (svgTagMatch) {
    let attrs = svgTagMatch[1];

    // 提取当前 width / height / viewBox / id / style
    const wMatch     = attrs.match(/\bwidth\s*=\s*["']([^"']*)["']/);
    const hMatch     = attrs.match(/\bheight\s*=\s*["']([^"']*)["']/);
    const vbMatch    = attrs.match(/\bviewBox\s*=\s*["']([^"']*)["']/);
    const idMatch    = attrs.match(/\bid\s*=\s*["']([^"']*)["']/);
    const styleMatch = attrs.match(/\bstyle\s*=\s*["']([^"']*)["']/);

    const w  = wMatch  ? wMatch[1].trim()  : null;
    const h  = hMatch  ? hMatch[1].trim()  : null;
    const vb = vbMatch ? vbMatch[1].trim() : null;

    // 判断是否为有效像素数值（纯数字，不含 %、px、em 等单位）
    const isPixel = (v) => v != null && /^\d+(\.\d+)?$/.test(v);

    let newW = w, newH = h;
    let newVb = vb;

    // 如果 width 或 height 不是有效像素值，尝试从 viewBox 推算
    if (vb && (!isPixel(w) || !isPixel(h))) {
      const parts = vb.split(/[\s,]+/).map(Number);
      if (parts.length >= 4 && parts[2] > 0 && parts[3] > 0) {
        if (!isPixel(w)) newW = String(parts[2]);
        if (!isPixel(h)) newH = String(parts[3]);
      }
    }

    // 如果没有 viewBox，但有具体 width/height，补全 viewBox
    if (!vb && isPixel(newW) && isPixel(newH)) {
      newVb = `0 0 ${newW} ${newH}`;
    }

    // 重新构建 <svg> 开标签
    let newAttrs = attrs;

    // 1a. 修复 width/height/viewBox
    const changed = (newW !== w) || (newH !== h) || (newVb !== vb);
    if (changed) {
      if (newW !== w) {
        if (wMatch) newAttrs = newAttrs.replace(wMatch[0], `width="${newW}"`);
        else        newAttrs = newAttrs + ` width="${newW}"`;
      }
      if (newH !== h) {
        if (hMatch) newAttrs = newAttrs.replace(hMatch[0], `height="${newH}"`);
        else        newAttrs = newAttrs + ` height="${newH}"`;
      }
      if (!vb && newVb) {
        newAttrs = newAttrs + ` viewBox="${newVb}"`;
      }
    }

    // 1b. 注入唯一 id（若已有 id 则覆盖，保持幂等）
    if (idMatch) {
      newAttrs = newAttrs.replace(idMatch[0], `id="${effectiveSvgId}"`);
    } else {
      newAttrs = newAttrs + ` id="${effectiveSvgId}"`;
    }

    // 1c. 在 style 属性中补全 --_xxx 映射变量，解决独立打开 SVG 时线条/箭头无颜色的问题
    //     原因：SVG 内部元素用 stroke="var(--_line)"，这些变量通常由第二个 <style> 块
    //     通过 CSS 继承从 --line 映射而来，但独立打开 SVG 时该 CSS 可能不生效。
    //     直接在 style 属性里内联精确颜色值是最稳健的方案。
    const lineColor   = theme.line   || (theme.fg + '80');
    const borderColor = theme.muted  || (theme.fg + '40');
    const accentColor = theme.accent || theme.fg;
    const extraVars = [
      `--_line:${lineColor}`,
      `--_arrow:${lineColor}`,
      `--_text:${theme.fg}`,
      `--_text-sec:${theme.muted || theme.fg + '80'}`,
      `--_text-muted:${theme.muted || theme.fg + '80'}`,
      `--_node-fill:${theme.bg}`,
      `--_node-stroke:${borderColor}`,
      `--_group-fill:${accentColor + '18'}`,
      `--_group-hdr:${accentColor}`,
      `--_inner-stroke:${borderColor}`,
    ].join(';');

    if (styleMatch) {
      // 已有 style 属性：追加变量（避免重复，先去掉相同 key）
      let existingStyle = styleMatch[1];
      // 去掉已存在的 --_xxx: 声明（防止重复）
      existingStyle = existingStyle.replace(/--_[a-z-]+:[^;]+;?/g, '');
      // 补全 background: 颜色（如果还没设置）
      if (!existingStyle.includes('background')) {
        existingStyle += `;background:${theme.bg}`;
      }
      const newStyle = `${existingStyle};${extraVars}`.replace(/^;+/, '');
      newAttrs = newAttrs.replace(styleMatch[0], `style="${newStyle}"`);
    } else {
      newAttrs = newAttrs + ` style="background:${theme.bg};${extraVars}"`;
    }

    fixed = fixed.replace(svgTagMatch[0], `<svg${newAttrs}>`);
  }

  // ── Step 2：在 <svg> 开标签之后注入样式 ──
  const svgOpenEnd = fixed.indexOf('>');
  if (svgOpenEnd === -1) return fixed;

  return fixed.slice(0, svgOpenEnd + 1) + '\n' + styleTag + fixed.slice(svgOpenEnd + 1);
}

/**
 * 应用样式到 SVG 字符串（浏览器版本，依赖 DOMParser / XMLSerializer）
 * 用于 preview.html 等浏览器环境。
 */
function applyStylesToSVG(svgString, theme, preset = 'default', options = {}) {
  const {
    transparentBg = false,
    includeFonts   = true,
    width          = null,
    height         = null,
    fontFamily     = null,
    fontSize       = null,
  } = options;

  const parser = new DOMParser();
  const doc    = parser.parseFromString(svgString, 'image/svg+xml');
  const svgEl  = doc.querySelector('svg');

  if (!svgEl) return svgString;

  const styleEl = document.createElementNS('http://www.w3.org/2000/svg', 'style');
  const p       = STYLE_PRESETS[preset] || STYLE_PRESETS.default;

  const customFontFamily = fontFamily || p.font.family;
  const customFontSize   = fontSize   || p.font.size;
  const lineColor        = theme.line || theme.fg + '80';
  const borderColor      = theme.muted || theme.fg + '40';

  styleEl.textContent = `
    .node rect,
    .node circle,
    .node ellipse,
    .node polygon {
      fill:         ${theme.bg}         !important;
      stroke:       ${borderColor}      !important;
      stroke-width: ${p.node.borderWidth}px !important;
      rx:           ${p.node.borderRadius}px !important;
      ry:           ${p.node.borderRadius}px !important;
      filter: drop-shadow(0 ${p.node.shadowBlur / 2}px ${p.node.shadowBlur}px ${p.node.shadowColor}) !important;
    }
    .node text {
      fill:        ${theme.fg}          !important;
      font-family: ${customFontFamily}  !important;
      font-size:   ${customFontSize}px  !important;
      dominant-baseline: central;
      dy: ${TEXT_BASELINE_SHIFT};
    }
    .cluster-label text {
      fill:        ${theme.accent || theme.fg} !important;
      font-size:   ${FONT_SIZES.groupHeader}px !important;
      font-weight: ${FONT_WEIGHTS.groupHeader} !important;
    }
    .edgePath .path {
      stroke:       ${lineColor}  !important;
      stroke-width: ${p.line.width}px !important;
    }
    .edgeLabel text {
      fill:      ${theme.muted || theme.fg} !important;
      font-size: ${FONT_SIZES.edgeLabel}px  !important;
    }
    .arrowhead,
    marker polygon {
      stroke: ${lineColor} !important;
      fill:   ${lineColor} !important;
    }
  `;
  svgEl.insertBefore(styleEl, svgEl.firstChild);

  if (includeFonts) {
    svgEl.setAttribute('font-family', customFontFamily);
  }
  if (width)  svgEl.setAttribute('width',  width);
  if (height) svgEl.setAttribute('height', height);

  if (!transparentBg) {
    const bgRect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
    bgRect.setAttribute('width',  '100%');
    bgRect.setAttribute('height', '100%');
    bgRect.setAttribute('fill',   theme.bg);
    svgEl.insertBefore(bgRect, svgEl.firstChild);
  }

  const serializer = new XMLSerializer();
  return serializer.serializeToString(svgEl);
}

// ─────────────────────────────────────────────────────────────────────────────
// §7  工具函数
// ─────────────────────────────────────────────────────────────────────────────

/** 获取可用预设名称列表 */
function getPresetNames() {
  return Object.keys(STYLE_PRESETS);
}

/** 获取预设配置 */
function getPreset(presetName) {
  return STYLE_PRESETS[presetName] || null;
}

/** 验证预设名称 */
function isValidPreset(presetName) {
  return presetName in STYLE_PRESETS;
}

/** 获取主题配置（按名称） */
function getTheme(themeName) {
  return THEMES[themeName] || null;
}

/** 获取所有主题名称 */
function getThemeNames() {
  return Object.keys(THEMES);
}

/** 验证主题名称 */
function isValidTheme(themeName) {
  return themeName in THEMES;
}

// ─────────────────────────────────────────────────────────────────────────────
// §7a  主题元数据工具函数
// ─────────────────────────────────────────────────────────────────────────────

/**
 * 获取主题元数据（dark标志、label、family、推荐预设）
 * @param {string} themeName
 * @returns {{ dark: boolean, label: string, family: string, recommendedPreset: string } | null}
 */
function getThemeMeta(themeName) {
  return THEME_META[themeName] || null;
}

/**
 * 获取所有暗色主题名称列表
 * @returns {string[]}
 */
function getDarkThemes() {
  return Object.keys(THEME_META).filter(k => THEME_META[k].dark);
}

/**
 * 获取所有亮色主题名称列表
 * @returns {string[]}
 */
function getLightThemes() {
  return Object.keys(THEME_META).filter(k => !THEME_META[k].dark);
}

/**
 * 按 family 分组返回主题名称数组
 * @returns {{ [family: string]: string[] }}
 */
function getThemesByFamily() {
  const groups = {};
  for (const [name, meta] of Object.entries(THEME_META)) {
    if (!groups[meta.family]) groups[meta.family] = [];
    groups[meta.family].push(name);
  }
  return groups;
}

/**
 * 获取某主题的推荐搭配预设
 * @param {string} themeName
 * @returns {string}  预设名称，未知主题时返回 'default'
 */
function getRecommendedPreset(themeName) {
  const meta = THEME_META[themeName];
  return meta ? meta.recommendedPreset : 'default';
}

/**
 * 解析并补全主题对象 —— 确保返回包含全部 7 个字段的完整主题对象。
 * 输入可以是：主题名称字符串、部分主题对象、或 null（返回默认值）。
 *
 * @param {string | object | null} themeInput  主题名称或主题对象
 * @returns {{ bg, fg, line, accent, muted, surface, border }}
 */
function resolveTheme(themeInput) {
  let base;

  if (typeof themeInput === 'string') {
    base = THEMES[themeInput] || THEME_DEFAULTS;
  } else if (themeInput && typeof themeInput === 'object') {
    base = themeInput;
  } else {
    base = THEME_DEFAULTS;
  }

  // 用 THEME_DEFAULTS 补全缺省字段
  return {
    bg:      base.bg      || THEME_DEFAULTS.bg,
    fg:      base.fg      || THEME_DEFAULTS.fg,
    line:    base.line    || base.fg + '80' || THEME_DEFAULTS.line,
    accent:  base.accent  || base.fg       || THEME_DEFAULTS.accent,
    muted:   base.muted   || base.fg + '80' || THEME_DEFAULTS.muted,
    surface: base.surface || base.bg       || THEME_DEFAULTS.surface,
    border:  base.border  || base.line     || base.fg + '40' || THEME_DEFAULTS.border,
  };
}

/**
 * 判断颜色对比度是否足够（WCAG AA 近似检测）
 * 用途：帮助 AI 在推荐主题时判断 fg/bg 对是否可读
 *
 * @param {string} hex1  十六进制颜色（如 '#1a1b26'）
 * @param {string} hex2  十六进制颜色
 * @returns {number}     相对亮度对比比值（>= 4.5 为 WCAG AA）
 */
function colorContrast(hex1, hex2) {
  function luminance(hex) {
    const r = parseInt(hex.slice(1, 3), 16) / 255;
    const g = parseInt(hex.slice(3, 5), 16) / 255;
    const b = parseInt(hex.slice(5, 7), 16) / 255;
    const channel = c => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
    return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
  }
  const L1 = luminance(hex1);
  const L2 = luminance(hex2);
  const lighter = Math.max(L1, L2);
  const darker  = Math.min(L1, L2);
  return (lighter + 0.05) / (darker + 0.05);
}

/**
 * 检测一个主题的可读性评分（bg vs fg 的对比度）
 * @param {string} themeName
 * @returns {{ contrast: number, passAA: boolean, passAAA: boolean }}
 */
function checkThemeReadability(themeName) {
  const theme = THEMES[themeName];
  if (!theme) return null;
  const contrast = colorContrast(theme.bg, theme.fg);
  return {
    contrast: Math.round(contrast * 10) / 10,
    passAA:   contrast >= 4.5,
    passAAA:  contrast >= 7.0,
  };
}

// ─────────────────────────────────────────────────────────────────────────────
// §8  导出
// ─────────────────────────────────────────────────────────────────────────────

module.exports = {
  // 字体度量
  MONO_FONT,
  MONO_FONT_STACK,
  estimateMonoTextWidth,

  // 几何常量（源自 src/styles.ts）
  FONT_SIZES,
  FONT_WEIGHTS,
  NODE_PADDING,
  STROKE_WIDTHS,
  TEXT_BASELINE_SHIFT,
  ARROW_HEAD,
  GROUP_HEADER_CONTENT_PAD,

  // 主题（15 个，源自 src/theme.ts，已补全 surface/border）
  THEMES,
  THEME_DEFAULTS,
  THEME_META,
  // 主题查询
  getTheme,
  getThemeNames,
  isValidTheme,
  // 主题元数据工具
  getThemeMeta,
  getDarkThemes,
  getLightThemes,
  getThemesByFamily,
  getRecommendedPreset,
  resolveTheme,
  // 对比度检测
  colorContrast,
  checkThemeReadability,

  // 视觉预设（5 个）
  STYLE_PRESETS,
  getPresetNames,
  getPreset,
  isValidPreset,

  // CSS / SVG 生成
  generateCSSStyles,
  injectStylesToSVG,
  applyStylesToSVG,
};


path: references/api-config.json
content: {
  "type": "module",
  "title": "Mermaid 配置选项",
  "description": "renderMermaidSVG、renderMermaidSVGAsync、renderMermaidASCII、parseMermaid、fromShikiTheme 的完整配置选项",
  "version": "1.1.3",
  "options": {
    "renderMermaidSVG": {
      "parameters": {
        "text": {
          "type": "string",
          "required": true,
          "description": "Mermaid 图表代码"
        },
        "options": {
          "type": "RenderOptions",
          "required": false,
          "default": "{}"
        }
      },
      "render_options": {
        "bg": {
          "type": "string",
          "default": "#FFFFFF",
          "description": "背景色，支持 CSS 变量",
          "examples": ["#1a1b26", "var(--background)"]
        },
        "fg": {
          "type": "string",
          "default": "#27272A",
          "description": "前景色/文字颜色，支持 CSS 变量",
          "examples": ["#a9b1d6", "var(--foreground)"]
        },
        "line": {
          "type": "string",
          "required": false,
          "description": "连线颜色"
        },
        "accent": {
          "type": "string",
          "required": false,
          "description": "箭头/高亮色",
          "examples": ["#7aa2f7", "#61afef"]
        },
        "muted": {
          "type": "string",
          "required": false,
          "description": "次要文字/标签颜色",
          "examples": ["#565f89", "#5c6370"]
        },
        "surface": {
          "type": "string",
          "required": false,
          "description": "节点填充色",
          "examples": ["#1f2335", "#21252b"]
        },
        "border": {
          "type": "string",
          "required": false,
          "description": "节点边框色",
          "examples": ["#3b4261", "#181a1f"]
        },
        "font": {
          "type": "string",
          "default": "Inter",
          "description": "字体族"
        },
        "transparent": {
          "type": "boolean",
          "default": false,
          "description": "是否使用透明背景"
        },
        "padding": {
          "type": "number",
          "default": 40,
          "description": "画布内边距 (px)"
        },
        "nodeSpacing": {
          "type": "number",
          "default": 24,
          "description": "同级节点水平间距"
        },
        "layerSpacing": {
          "type": "number",
          "default": 40,
          "description": "层间垂直间距"
        },
        "componentSpacing": {
          "type": "number",
          "default": 24,
          "description": "断开组件间距"
        },
        "interactive": {
          "type": "boolean",
          "default": false,
          "description": "启用 XY 图表悬浮 tooltip"
        }
      }
    },
    "renderMermaidSVGAsync": {
      "description": "renderMermaidSVG 的异步版本，返回 Promise<string>",
      "parameters": {
        "text": {
          "type": "string",
          "required": true,
          "description": "Mermaid 图表代码"
        },
        "options": {
          "type": "RenderOptions",
          "required": false,
          "description": "与 renderMermaidSVG 相同的选项"
        }
      }
    },
    "renderMermaidASCII": {
      "parameters": {
        "text": {
          "type": "string",
          "required": true,
          "description": "Mermaid 图表代码"
        },
        "options": {
          "type": "AsciiRenderOptions",
          "required": false,
          "default": "{}"
        }
      },
      "ascii_options": {
        "useAscii": {
          "type": "boolean",
          "default": false,
          "description": "使用 ASCII 字符 (true) 或 Unicode 框线 (false)"
        },
        "paddingX": {
          "type": "number",
          "default": 5,
          "description": "节点水平间距"
        },
        "paddingY": {
          "type": "number",
          "default": 5,
          "description": "节点垂直间距"
        },
        "boxBorderPadding": {
          "type": "number",
          "default": 1,
          "description": "节点框内边距"
        },
        "colorMode": {
          "type": "string",
          "default": "auto",
          "description": "颜色模式",
          "enum": ["none", "auto", "ansi16", "ansi256", "truecolor", "html"],
          "examples": [
            "none - 无颜色",
            "auto - 自动检测",
            "ansi16 - 16色模式",
            "ansi256 - 256色模式",
            "truecolor - 真彩色",
            "html - HTML span 标签（浏览器渲染）"
          ]
        },
        "theme": {
          "type": "Partial<AsciiTheme>",
          "required": false,
          "description": "自定义 ASCII 颜色",
          "properties": {
            "fg": "文字颜色",
            "border": "边框颜色",
            "line": "连线颜色",
            "arrow": "箭头颜色",
            "accent": "强调色 (可选)",
            "bg": "背景色 (可选)"
          }
        }
      }
    },
    "parseMermaid": {
      "description": "解析 Mermaid 源码为结构化图对象",
      "parameters": {
        "text": {
          "type": "string",
          "required": true,
          "description": "Mermaid 图表代码"
        }
      },
      "returns": {
        "direction": "TD | TB | LR | BT | RL",
        "nodes": "Map<string, MermaidNode>",
        "edges": "MermaidEdge[]",
        "subgraphs": "MermaidSubgraph[]",
        "classDefs": "Map<string, Record<string, string>>",
        "nodeStyles": "Map<string, Record<string, string>>",
        "linkStyles": "Map<number | 'default', Record<string, string>>"
      }
    },
    "fromShikiTheme": {
      "description": "从 Shiki 主题对象提取图表颜色",
      "parameters": {
        "theme": {
          "type": "ShikiThemeLike",
          "required": true,
          "description": "Shiki 的 ThemeRegistrationResolved 对象"
        }
      },
      "returns": "DiagramColors 对象",
      "mapping": {
        "editor.background": "bg",
        "editor.foreground": "fg",
        "editorLineNumber.foreground": "line",
        "focusBorder / keyword": "accent",
        "comment token": "muted",
        "editor.selectionBackground": "surface",
        "editorWidget.border": "border"
      }
    }
  },
  "example_usage": {
    "svg_with_builtin_theme": {
      "code": "import { renderMermaidSVG, THEMES } from 'beautiful-mermaid';",
      "call": "const svg = renderMermaidSVG('graph TD; A --> B', THEMES.dracula);"
    },
    "svg_with_custom_theme": {
      "code": "import { renderMermaidSVG } from 'beautiful-mermaid';",
      "call": "const svg = renderMermaidSVG('graph TD; A --> B', { bg: '#1a1b26', fg: '#e0e0e0', accent: '#7aa2f7' });"
    },
    "svg_async": {
      "code": "import { renderMermaidSVGAsync } from 'beautiful-mermaid';",
      "call": "const svg = await renderMermaidSVGAsync('graph TD; A --> B', THEMES['nord']);"
    },
    "svg_with_css_vars": {
      "code": "import { renderMermaidSVG } from 'beautiful-mermaid';",
      "call": "const svg = renderMermaidSVG('graph TD; A --> B', { bg: 'var(--background)', fg: 'var(--foreground)', transparent: true });"
    },
    "ascii_unicode": {
      "code": "import { renderMermaidASCII } from 'beautiful-mermaid';",
      "call": "const ascii = renderMermaidASCII('graph TD; A --> B');"
    },
    "ascii_pure_ascii": {
      "code": "import { renderMermaidASCII } from 'beautiful-mermaid';",
      "call": "const ascii = renderMermaidASCII('graph TD; A --> B', { useAscii: true, colorMode: 'truecolor' });"
    },
    "parse_mermaid": {
      "code": "import { parseMermaid } from 'beautiful-mermaid';",
      "call": "const graph = parseMermaid('graph TD; A --> B --> C');"
    },
    "shiki_theme": {
      "code": "import { fromShikiTheme } from 'beautiful-mermaid';",
      "call": "const colors = fromShikiTheme(highlighter.getTheme('vitesse-dark'));"
    },
    "xychart_interactive": {
      "code": "import { renderMermaidSVG } from 'beautiful-mermaid';",
      "call": "const svg = renderMermaidSVG('xychart-beta\\n    bar [100, 200, 300]', { interactive: true, accent: '#7aa2f7' });"
    }
  }
}


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)